Commit 95ac8082963d7d7cccaed9f2b89eba7a902ad999
Merge branch 'persistence' into 'development'
merge persistence to development See merge request !1
Showing
28 changed files
with
1118 additions
and
4 deletions
.gitignore
@@ -14,10 +14,13 @@ hs_err_pid* | @@ -14,10 +14,13 @@ hs_err_pid* | ||
14 | # Logfiles | 14 | # Logfiles |
15 | **.log | 15 | **.log |
16 | **.log.* | 16 | **.log.* |
17 | +docker/wildfly/volumes/logs/ | ||
17 | 18 | ||
18 | **/.DS_Store | 19 | **/.DS_Store |
19 | .idea | 20 | .idea |
20 | .gradle | 21 | .gradle |
21 | bht-chatbot.iml | 22 | bht-chatbot.iml |
23 | +BeuthBot.iml | ||
22 | target/ | 24 | target/ |
23 | build | 25 | build |
26 | +out |
docker/docker-compose.yml
@@ -9,13 +9,20 @@ services: | @@ -9,13 +9,20 @@ services: | ||
9 | - "8080:8080" | 9 | - "8080:8080" |
10 | - "8787:8787" | 10 | - "8787:8787" |
11 | - "9990:9990" | 11 | - "9990:9990" |
12 | + environment: | ||
13 | + POSTGRES_USER: beuthbot_app | ||
14 | + POSTGRES_PASSWORD: VhS7WPVpdYEHYLpf | ||
15 | + POSTGRES_DB: beuthbot | ||
12 | volumes: | 16 | volumes: |
13 | - ./wildfly/volumes/deployments/:/opt/jboss/wildfly/standalone/deployments/ | 17 | - ./wildfly/volumes/deployments/:/opt/jboss/wildfly/standalone/deployments/ |
14 | - ./wildfly/volumes/logs/:/opt/jboss/wildfly/standalone/log/ | 18 | - ./wildfly/volumes/logs/:/opt/jboss/wildfly/standalone/log/ |
15 | - ./wildfly/volumes/conf/:/opt/jboss/wildfly/standalone/conf/ | 19 | - ./wildfly/volumes/conf/:/opt/jboss/wildfly/standalone/conf/ |
20 | + - ./wildfly/volumes/modules/org/postgres/main/:/opt/jboss/wildfly/modules/org/postgres/main/ | ||
16 | links: | 21 | links: |
17 | - rasa-server | 22 | - rasa-server |
23 | + - postgres-db | ||
18 | command: /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug | 24 | command: /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug |
25 | + | ||
19 | rasa-server: | 26 | rasa-server: |
20 | build: ./rasa_nlu | 27 | build: ./rasa_nlu |
21 | container_name: rasa_nlu | 28 | container_name: rasa_nlu |
@@ -25,4 +32,22 @@ services: | @@ -25,4 +32,22 @@ services: | ||
25 | volumes: | 32 | volumes: |
26 | - ./rasa_nlu/volumes/data/api/:/app/data/api | 33 | - ./rasa_nlu/volumes/data/api/:/app/data/api |
27 | - ./rasa_nlu/volumes/logs/:/app/logs/ | 34 | - ./rasa_nlu/volumes/logs/:/app/logs/ |
28 | - command: python -m rasa_nlu.server -c config/chatbot_config.json --server_model_dirs=default | ||
29 | \ No newline at end of file | 35 | \ No newline at end of file |
36 | + command: python -m rasa_nlu.server -c config/chatbot_config.json --server_model_dirs=default | ||
37 | + | ||
38 | + postgres-db: | ||
39 | + image: postgres | ||
40 | + ports: | ||
41 | + - "5432:5432" | ||
42 | + command: postgres -c logging_collector=on -c log_destination=stderr -c log_directory=/logs | ||
43 | + environment: | ||
44 | + POSTGRES_USER: beuthbot_app | ||
45 | + POSTGRES_PASSWORD: VhS7WPVpdYEHYLpf | ||
46 | + POSTGRES_DB: beuthbot | ||
47 | + volumes: | ||
48 | + - ./postgres/volumes/logs:/logs | ||
49 | + | ||
50 | + adminer: | ||
51 | + container_name: adminer_dbmanagement | ||
52 | + image: adminer | ||
53 | + ports: | ||
54 | + - "8081:8080" | ||
30 | \ No newline at end of file | 55 | \ No newline at end of file |
docker/postgres/volumes/logs/README.md
0 → 100644
docker/wildfly/Dockerfile
1 | FROM jboss/wildfly:10.1.0.Final | 1 | FROM jboss/wildfly:10.1.0.Final |
2 | RUN /opt/jboss/wildfly/bin/add-user.sh admin Admin | 2 | RUN /opt/jboss/wildfly/bin/add-user.sh admin Admin |
3 | -COPY standalone.xml /opt/jboss/wildfly/standalone/configuration/standalone.xml | ||
4 | \ No newline at end of file | 3 | \ No newline at end of file |
4 | +COPY standalone.xml /opt/jboss/wildfly/standalone/configuration/standalone.xml | ||
5 | + | ||
6 | + | ||
7 | +#USER root | ||
8 | + | ||
9 | +#ADD modules/org/postgres/main/postgresql-42.1.4.jar /opt/jboss/wildfly/modules/org/postgres/main/postgresql-42.1.4.jar | ||
10 | +#ADD modules/org/postgres/main/module.xml /opt/jboss/wildfly/modules/org/postgres/main/module.xml | ||
5 | \ No newline at end of file | 11 | \ No newline at end of file |
docker/wildfly/standalone.xml
@@ -161,7 +161,7 @@ | @@ -161,7 +161,7 @@ | ||
161 | <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/> | 161 | <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/> |
162 | </managed-scheduled-executor-services> | 162 | </managed-scheduled-executor-services> |
163 | </concurrent> | 163 | </concurrent> |
164 | - <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" jms-connection-factory="java:jboss/DefaultJMSConnectionFactory" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/> | 164 | + <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/PostgreSQLDS" jms-connection-factory="java:jboss/DefaultJMSConnectionFactory" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/> |
165 | </subsystem> | 165 | </subsystem> |
166 | <subsystem xmlns="urn:jboss:domain:ejb3:4.0"> | 166 | <subsystem xmlns="urn:jboss:domain:ejb3:4.0"> |
167 | <session-bean> | 167 | <session-bean> |
@@ -417,6 +417,40 @@ | @@ -417,6 +417,40 @@ | ||
417 | <client-config name="Standard-Client-Config"/> | 417 | <client-config name="Standard-Client-Config"/> |
418 | </subsystem> | 418 | </subsystem> |
419 | <subsystem xmlns="urn:jboss:domain:weld:3.0"/> | 419 | <subsystem xmlns="urn:jboss:domain:weld:3.0"/> |
420 | + | ||
421 | + <subsystem xmlns="urn:jboss:domain:datasources:2.0"> | ||
422 | + <datasources> | ||
423 | + <datasource jta="true" jndi-name="java:jboss/datasources/PostgreSQLDS" pool-name="PostgreSQLDS" enabled="true" use-java-context="true"> | ||
424 | + <connection-url>jdbc:postgresql://postgres-db:5432/beuthbot</connection-url> | ||
425 | + <connection-property name="url">jdbc:postgresql://postgres-db:5432/beuthbot</connection-property> | ||
426 | + <connection-property name="databaseName">beuthbot</connection-property> | ||
427 | + <driver>postgresql</driver> | ||
428 | + <security> | ||
429 | + <user-name>beuthbot_app</user-name> | ||
430 | + <password>VhS7WPVpdYEHYLpf</password> | ||
431 | + </security> | ||
432 | + <pool> | ||
433 | + <min-pool-size>5</min-pool-size> | ||
434 | + <initial-pool-size>5</initial-pool-size> | ||
435 | + <max-pool-size>100</max-pool-size> | ||
436 | + <prefill>true</prefill> | ||
437 | + </pool> | ||
438 | + <validation> | ||
439 | + <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker"/> | ||
440 | + <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter"/> | ||
441 | + </validation> | ||
442 | + </datasource> | ||
443 | + | ||
444 | + <drivers> | ||
445 | + <driver name="postgresql" module="org.postgres"> | ||
446 | + <driver-class>org.postgresql.Driver</driver-class> | ||
447 | + <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class> | ||
448 | + <datasource-class>org.postgresql.ds.PGSimpleDataSource</datasource-class> | ||
449 | + </driver> | ||
450 | + </drivers> | ||
451 | + </datasources> | ||
452 | + </subsystem> | ||
453 | + | ||
420 | </profile> | 454 | </profile> |
421 | 455 | ||
422 | <interfaces> | 456 | <interfaces> |
docker/wildfly/volumes/modules/org/postgres/main/module.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<module xmlns="urn:jboss:module:1.1" name="org.postgres"> | ||
3 | + <resources> | ||
4 | + <!--https://jdbc.postgresql.org/download.html--> | ||
5 | + <resource-root path="postgresql-42.1.4.jar"/> | ||
6 | + </resources> | ||
7 | + <dependencies> | ||
8 | + <module name="javax.api"/> | ||
9 | + <module name="javax.transaction.api"/> | ||
10 | + <module name="javax.servlet.api" optional="true"/> | ||
11 | + </dependencies> | ||
12 | +</module> | ||
0 | \ No newline at end of file | 13 | \ No newline at end of file |
docker/wildfly/volumes/modules/org/postgres/main/postgresql-42.1.4.jar
0 → 100644
No preview for this file type
services/Common/build.gradle
@@ -9,6 +9,12 @@ dependencies { | @@ -9,6 +9,12 @@ dependencies { | ||
9 | compile "org.jboss.spec:jboss-javaee-7.0:1.1.0.Final", | 9 | compile "org.jboss.spec:jboss-javaee-7.0:1.1.0.Final", |
10 | "com.google.code.gson:gson:2.8.1", | 10 | "com.google.code.gson:gson:2.8.1", |
11 | "org.slf4j:slf4j-api:1.7.25" | 11 | "org.slf4j:slf4j-api:1.7.25" |
12 | + | ||
13 | + compile group: 'org.postgresql', name: 'postgresql', version: '9.3-1100-jdbc4' | ||
14 | + compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.0.1' | ||
15 | + | ||
16 | + compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '5.2.12.Final' | ||
17 | + compileOnly group: 'org.hibernate', name: 'hibernate-entitymanager', version: '4.3.6.Final' | ||
12 | } | 18 | } |
13 | 19 | ||
14 | sourceCompatibility = 1.8 | 20 | sourceCompatibility = 1.8 |
15 | \ No newline at end of file | 21 | \ No newline at end of file |
services/Common/src/main/java/de/bht/beuthbot/daos/AppUserDAO.java
0 → 100644
1 | +package de.bht.beuthbot.daos; | ||
2 | + | ||
3 | +import de.bht.beuthbot.model.entities.AppUser; | ||
4 | + | ||
5 | +import javax.ejb.Remote; | ||
6 | + | ||
7 | +/** | ||
8 | + * Created by Benjamin Rühl on 19.12.2017. | ||
9 | + */ | ||
10 | +@Remote | ||
11 | +public interface AppUserDAO extends GenericDAO<AppUser, Long> { | ||
12 | + | ||
13 | + AppUser createUser(); | ||
14 | +} |
services/Common/src/main/java/de/bht/beuthbot/daos/AppUserDAOImpl.java
0 → 100644
1 | +package de.bht.beuthbot.daos; | ||
2 | + | ||
3 | +import de.bht.beuthbot.model.entities.AppUserImpl; | ||
4 | +import de.bht.beuthbot.model.entities.AppUser; | ||
5 | + | ||
6 | +import javax.ejb.Stateless; | ||
7 | + | ||
8 | +/** | ||
9 | + * Created by Benjamin Rühl on 19.12.2017. | ||
10 | + */ | ||
11 | +@Stateless | ||
12 | +public class AppUserDAOImpl extends GenericHibernateDAO<AppUser, AppUserImpl, Long> implements AppUserDAO { | ||
13 | + | ||
14 | + @Override | ||
15 | + public AppUser createUser() { | ||
16 | + return new AppUserImpl(); | ||
17 | + } | ||
18 | +} |
services/Common/src/main/java/de/bht/beuthbot/daos/GenericDAO.java
0 → 100644
1 | +package de.bht.beuthbot.daos; | ||
2 | + | ||
3 | +import de.bht.beuthbot.model.entities.EntityBase; | ||
4 | + | ||
5 | +import javax.ejb.Remote; | ||
6 | +import java.io.Serializable; | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +/** | ||
10 | + * Created by Benjamin Rühl on 19.12.2017. | ||
11 | + */ | ||
12 | +@Remote | ||
13 | +public interface GenericDAO<T extends EntityBase, ID extends Serializable> { | ||
14 | + | ||
15 | + T findById(ID id); | ||
16 | + | ||
17 | + List<T> findAll(); | ||
18 | + | ||
19 | + T saveOrUpdate(T entity); | ||
20 | + | ||
21 | + void delete(T entity); | ||
22 | +} |
services/Common/src/main/java/de/bht/beuthbot/daos/GenericHibernateDAO.java
0 → 100644
1 | +package de.bht.beuthbot.daos; | ||
2 | + | ||
3 | +import de.bht.beuthbot.model.entities.EntityBase; | ||
4 | + | ||
5 | +import javax.annotation.PostConstruct; | ||
6 | +import javax.ejb.Stateless; | ||
7 | +import javax.persistence.EntityManager; | ||
8 | +import javax.persistence.EntityManagerFactory; | ||
9 | +import javax.persistence.PersistenceContext; | ||
10 | +import javax.persistence.PersistenceUnit; | ||
11 | +import javax.persistence.criteria.CriteriaBuilder; | ||
12 | +import javax.persistence.criteria.CriteriaQuery; | ||
13 | +import javax.persistence.criteria.Predicate; | ||
14 | +import javax.persistence.criteria.Root; | ||
15 | +import java.io.Serializable; | ||
16 | +import java.lang.reflect.ParameterizedType; | ||
17 | +import java.util.List; | ||
18 | + | ||
19 | +/** | ||
20 | + * Created by Benjamin Rühl on 19.12.2017. | ||
21 | + * Base class for DAO implementations using hibernate for persistence. | ||
22 | + * Provides generic functionality for interaction with entities. | ||
23 | + * @param <I> Interface type of entity class | ||
24 | + * @param <T> Implementation type of entity class | ||
25 | + * @param <ID> Type of entity's primary id | ||
26 | + */ | ||
27 | +@Stateless | ||
28 | +public class GenericHibernateDAO<I extends EntityBase, T extends I, ID extends Serializable> implements GenericDAO<I, ID> { | ||
29 | + | ||
30 | + private Class<T> entityClass; | ||
31 | + | ||
32 | + @PersistenceUnit(unitName = "PostgresPU") | ||
33 | + private EntityManagerFactory entityManagerFactory; | ||
34 | + | ||
35 | + @PersistenceContext(unitName = "PostgresPU") | ||
36 | + private EntityManager entityManager; | ||
37 | + | ||
38 | + @SuppressWarnings("unchecked cast") | ||
39 | + @PostConstruct | ||
40 | + public void init() { | ||
41 | + if (entityManager == null) | ||
42 | + throw new RuntimeException("EntityManager must not be null"); | ||
43 | + | ||
44 | + entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]; | ||
45 | + } | ||
46 | + | ||
47 | + public Class<T> getEntityClass() { | ||
48 | + return entityClass; | ||
49 | + } | ||
50 | + | ||
51 | + @Override | ||
52 | + public I findById(ID id) { | ||
53 | + return entityManager.find(getEntityClass(), id); | ||
54 | + } | ||
55 | + | ||
56 | + @Override | ||
57 | + public List<I> findAll() { | ||
58 | + return findByCriteria(); | ||
59 | + } | ||
60 | + | ||
61 | + @Override | ||
62 | + public I saveOrUpdate(I entity) { | ||
63 | + entityManager.merge(entity); | ||
64 | + return entity; | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + public void delete(I entity) { | ||
69 | + entityManager.remove(entity); | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * Use this inside subclasses as a convenience method. | ||
74 | + */ | ||
75 | + protected List<I> findByCriteria(Predicate... restrictions) { | ||
76 | + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); | ||
77 | + CriteriaQuery<T> criteria = builder.createQuery(getEntityClass()); | ||
78 | + Root<T> criteriaRoot = criteria.from(getEntityClass()); | ||
79 | + | ||
80 | + criteria.select(criteriaRoot); | ||
81 | + criteria.where(restrictions); | ||
82 | + | ||
83 | + List<T> elements = entityManager.createQuery(criteria).getResultList(); | ||
84 | + return (List<I>) elements; | ||
85 | + } | ||
86 | + | ||
87 | + protected EntityManager getEntityManager() { | ||
88 | + return entityManager; | ||
89 | + } | ||
90 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/AppUser.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +/** | ||
4 | + * Created by Benjamin Rühl on 19.11.2017. | ||
5 | + */ | ||
6 | +public interface AppUser extends EntityBase { | ||
7 | + | ||
8 | + void setFacebookUserId(String facebookUserId); | ||
9 | + String getFacebookUserId(); | ||
10 | + void setTelegramUserId(String telegramUserId); | ||
11 | + String getTelegramUserId(); | ||
12 | + <T extends Object> T getProperty(String propertyName, Class<T> propertyType); | ||
13 | + void setProperty(String propertyName, Object propertyValue); | ||
14 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/AppUserImpl.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import de.bht.beuthbot.persistence.GenericEntityAccessFacade; | ||
4 | + | ||
5 | +import javax.persistence.CascadeType; | ||
6 | +import javax.persistence.Entity; | ||
7 | +import javax.persistence.OneToOne; | ||
8 | +import javax.persistence.Table; | ||
9 | + | ||
10 | +/** | ||
11 | + * Created by Benjamin Rühl on 19.11.2017. | ||
12 | + * Note: This class must not be named "User" except a different table name is provided. | ||
13 | + * Postgres does not support creating a table named "user". | ||
14 | + */ | ||
15 | +@Entity | ||
16 | +@Table | ||
17 | +public class AppUserImpl extends EntityBaseImpl implements AppUser { | ||
18 | + | ||
19 | + private String facebookUserId; | ||
20 | + | ||
21 | + private String telegramUserId; | ||
22 | + | ||
23 | + @OneToOne(cascade = CascadeType.ALL) | ||
24 | + private GenericEntity additionalData; | ||
25 | + | ||
26 | + //@Type(type = "JsonMapType") | ||
27 | + //private Map<String, String> additionalData = new HashMap<>(); | ||
28 | + | ||
29 | + public AppUserImpl() { | ||
30 | + additionalData = new GenericEntity(); | ||
31 | + additionalData.setName("User"); | ||
32 | + } | ||
33 | + | ||
34 | + @Override | ||
35 | + public String getFacebookUserId() { | ||
36 | + return facebookUserId; | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public String getTelegramUserId() { | ||
41 | + return telegramUserId; | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + public <T> T getProperty(String propertyName, Class<T> propertyType) { | ||
46 | + return GenericEntityAccessFacade.getAttribute(additionalData, propertyName, propertyType); | ||
47 | + } | ||
48 | + | ||
49 | + @Override | ||
50 | + public void setProperty(String propertyName, Object propertyValue) { | ||
51 | + GenericEntityAccessFacade.setAttribute(additionalData, propertyName, propertyValue); | ||
52 | + } | ||
53 | + | ||
54 | + //@Override | ||
55 | + /*public <T extends Object> T getProperty(String propertyName, Class<T> propertyType) { | ||
56 | + String propertyValue = additionalData.getOrDefault(propertyName, null); | ||
57 | + | ||
58 | + if (propertyValue == null) | ||
59 | + return null; | ||
60 | + | ||
61 | + return (T) JsonHelper.fromJson(propertyValue, propertyType); | ||
62 | + }*/ | ||
63 | + | ||
64 | + //@Override | ||
65 | + /*public void setProperty(String propertyName, Object propertyValue) { | ||
66 | + String valueAsJson = JsonHelper.toJson(propertyValue, propertyValue.getClass()); | ||
67 | + additionalData.put(propertyName, valueAsJson); | ||
68 | + }*/ | ||
69 | + | ||
70 | + public void setFacebookUserId(String facebookUserId) { | ||
71 | + this.facebookUserId = facebookUserId; | ||
72 | + } | ||
73 | + | ||
74 | + public void setTelegramUserId(String telegramUserId) { | ||
75 | + this.telegramUserId = telegramUserId; | ||
76 | + } | ||
77 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/EntityBase.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import java.io.Serializable; | ||
4 | +import java.util.Date; | ||
5 | + | ||
6 | +/** | ||
7 | + * Created by Benjamin Rühl on 19.11.2017. | ||
8 | + */ | ||
9 | +public interface EntityBase extends Serializable { | ||
10 | + | ||
11 | + Long getId(); | ||
12 | + | ||
13 | + Date getCreationDate(); | ||
14 | + | ||
15 | + Date getUpdateDate(); | ||
16 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/EntityBaseImpl.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import de.bht.beuthbot.persistence.JsonMapUserType; | ||
4 | + | ||
5 | +import org.hibernate.annotations.TypeDef; | ||
6 | + | ||
7 | +import javax.persistence.*; | ||
8 | +import java.util.Date; | ||
9 | +import java.util.Objects; | ||
10 | + | ||
11 | +/** | ||
12 | + * Created by Benjamin Rühl on 19.11.2017. | ||
13 | + */ | ||
14 | +@MappedSuperclass | ||
15 | +@Access(AccessType.FIELD) | ||
16 | +@TypeDef(name = "JsonMapType", typeClass = JsonMapUserType.class) | ||
17 | +public class EntityBaseImpl implements EntityBase { | ||
18 | + | ||
19 | + @Id | ||
20 | + @GeneratedValue | ||
21 | + protected long id; | ||
22 | + | ||
23 | + @Temporal(TemporalType.TIMESTAMP) | ||
24 | + protected Date creationDate; | ||
25 | + | ||
26 | + @Temporal(TemporalType.TIMESTAMP) | ||
27 | + protected Date updateDate; | ||
28 | + | ||
29 | + @PrePersist | ||
30 | + protected void onCreate() { | ||
31 | + creationDate = new Date(); | ||
32 | + } | ||
33 | + | ||
34 | + @PreUpdate | ||
35 | + protected void onUpdate() { | ||
36 | + this.updateDate = new Date(); | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public Long getId() { | ||
41 | + return null; | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + public Date getCreationDate() { | ||
46 | + return creationDate; | ||
47 | + } | ||
48 | + | ||
49 | + @Override | ||
50 | + public Date getUpdateDate() { | ||
51 | + return updateDate; | ||
52 | + } | ||
53 | + | ||
54 | + @Override | ||
55 | + public boolean equals(Object obj) { | ||
56 | + if (this == obj) | ||
57 | + return true; | ||
58 | + | ||
59 | + if (obj == null) | ||
60 | + return false; | ||
61 | + | ||
62 | + if (obj.getClass() != getClass()) | ||
63 | + return false; | ||
64 | + | ||
65 | + EntityBaseImpl other = (EntityBaseImpl)obj; | ||
66 | + | ||
67 | + if (other.getId() == 0 && getId() == 0) | ||
68 | + return super.equals(obj); | ||
69 | + | ||
70 | + return Objects.equals(other.getId(), getId()); | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + public int hashCode(){ | ||
75 | + return Objects.hash(id); | ||
76 | + } | ||
77 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/GenericEntity.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import javax.persistence.CascadeType; | ||
4 | +import javax.persistence.Entity; | ||
5 | +import javax.persistence.OneToMany; | ||
6 | +import javax.persistence.Table; | ||
7 | +import java.util.ArrayList; | ||
8 | +import java.util.List; | ||
9 | +import java.util.stream.Collectors; | ||
10 | + | ||
11 | +/** | ||
12 | + * Created by Benjamin Rühl on 22.12.2017. | ||
13 | + * A generic container entity that holds references to a set of generic attributes which can be specified at runtime. | ||
14 | + * Follows the Entity-Attribute-Value model of database design. | ||
15 | + */ | ||
16 | +@Entity | ||
17 | +@Table | ||
18 | +public class GenericEntity extends EntityBaseImpl { | ||
19 | + | ||
20 | + /** | ||
21 | + * name is not mandatory, but can help to identify the entity's purpose for the developer | ||
22 | + */ | ||
23 | + private String name; | ||
24 | + | ||
25 | + @OneToMany(cascade = CascadeType.ALL) | ||
26 | + private List<GenericEntityAttribute> attributes = new ArrayList<>(); | ||
27 | + | ||
28 | + public String getName() { | ||
29 | + return name; | ||
30 | + } | ||
31 | + | ||
32 | + public void setName(String name) { | ||
33 | + this.name = name; | ||
34 | + } | ||
35 | + | ||
36 | + public List<GenericEntityAttribute> getAttributes() { | ||
37 | + return attributes; | ||
38 | + } | ||
39 | + | ||
40 | + public void setAttribute(GenericEntityAttribute attribute) { | ||
41 | + List<GenericEntityAttribute> existingAttributesForName = this.attributes.stream().filter(a -> a.getName().equals(attribute.getName())).collect(Collectors.toList()); | ||
42 | + | ||
43 | + if (!existingAttributesForName.isEmpty()) | ||
44 | + getAttributes().removeAll(existingAttributesForName); | ||
45 | + | ||
46 | + this.attributes.add(attribute); | ||
47 | + } | ||
48 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/GenericEntityAttribute.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import javax.persistence.CascadeType; | ||
4 | +import javax.persistence.Entity; | ||
5 | +import javax.persistence.OneToMany; | ||
6 | +import javax.persistence.Table; | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +/** | ||
10 | + * Created by Benjamin Rühl on 22.12.2017. | ||
11 | + * An attribute of a generic entity that can be specified at runtime. | ||
12 | + * Holds a collection of generic values. | ||
13 | + */ | ||
14 | +@Entity | ||
15 | +@Table | ||
16 | +public class GenericEntityAttribute extends EntityBaseImpl { | ||
17 | + | ||
18 | + private String name; | ||
19 | + | ||
20 | + @OneToMany(cascade = CascadeType.ALL) | ||
21 | + private List<GenericEntityAttributeValue> values; | ||
22 | + | ||
23 | + public String getName() { | ||
24 | + return name; | ||
25 | + } | ||
26 | + | ||
27 | + public void setName(String name) { | ||
28 | + this.name = name; | ||
29 | + } | ||
30 | + | ||
31 | + public List<GenericEntityAttributeValue> getValues() { | ||
32 | + return values; | ||
33 | + } | ||
34 | + | ||
35 | + public void addValue(GenericEntityAttributeValue value) { | ||
36 | + this.values.add(value); | ||
37 | + } | ||
38 | +} |
services/Common/src/main/java/de/bht/beuthbot/model/entities/GenericEntityAttributeValue.java
0 → 100644
1 | +package de.bht.beuthbot.model.entities; | ||
2 | + | ||
3 | +import javax.persistence.CascadeType; | ||
4 | +import javax.persistence.Entity; | ||
5 | +import javax.persistence.OneToOne; | ||
6 | +import javax.persistence.Table; | ||
7 | + | ||
8 | +/** | ||
9 | + * Created by Benjamin Rühl on 22.12.2017. | ||
10 | + * The value of a generic attribute in supported data formats. | ||
11 | + * Supports complex nested types using a reference to a generic sub entity. | ||
12 | + */ | ||
13 | +@Entity | ||
14 | +@Table | ||
15 | +public class GenericEntityAttributeValue extends EntityBaseImpl { | ||
16 | + | ||
17 | + private Boolean valueAsBool; | ||
18 | + | ||
19 | + private Long valueAsLong; | ||
20 | + | ||
21 | + private Double valueAsDouble; | ||
22 | + | ||
23 | + private String valueAsString; | ||
24 | + | ||
25 | + @OneToOne(cascade = CascadeType.ALL) | ||
26 | + private GenericEntity valueAsEntity; | ||
27 | + | ||
28 | + public Boolean getValueAsBool() { | ||
29 | + return valueAsBool; | ||
30 | + } | ||
31 | + | ||
32 | + public void setValueAsBool(boolean valueAsBool) { | ||
33 | + this.valueAsBool = valueAsBool; | ||
34 | + } | ||
35 | + | ||
36 | + public Long getValueAsLong() { | ||
37 | + return valueAsLong; | ||
38 | + } | ||
39 | + | ||
40 | + public void setValueAsLong(long valueAsLong) { | ||
41 | + this.valueAsLong = valueAsLong; | ||
42 | + } | ||
43 | + | ||
44 | + public Double getValueAsDouble() { | ||
45 | + return valueAsDouble; | ||
46 | + } | ||
47 | + | ||
48 | + public void setValueAsDouble(double valueAsDouble) { | ||
49 | + this.valueAsDouble = valueAsDouble; | ||
50 | + } | ||
51 | + | ||
52 | + public String getValueAsString() { | ||
53 | + return valueAsString; | ||
54 | + } | ||
55 | + | ||
56 | + public void setValueAsString(String valueAsString) { | ||
57 | + this.valueAsString = valueAsString; | ||
58 | + } | ||
59 | + | ||
60 | + public GenericEntity getValueAsEntity() { | ||
61 | + return valueAsEntity; | ||
62 | + } | ||
63 | + | ||
64 | + public void setValueAsEntity(GenericEntity valueAsEntity) { | ||
65 | + this.valueAsEntity = valueAsEntity; | ||
66 | + } | ||
67 | +} |
services/Common/src/main/java/de/bht/beuthbot/persistence/ExtendedPostgreSQLDialect.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import org.hibernate.dialect.PostgreSQLDialect; | ||
4 | + | ||
5 | +import java.sql.Types; | ||
6 | + | ||
7 | +public class ExtendedPostgreSQLDialect extends PostgreSQLDialect { | ||
8 | + | ||
9 | + public ExtendedPostgreSQLDialect(){ | ||
10 | + super(); | ||
11 | + registerHibernateType(Types.JAVA_OBJECT, "json"); | ||
12 | + | ||
13 | + } | ||
14 | +} | ||
0 | \ No newline at end of file | 15 | \ No newline at end of file |
services/Common/src/main/java/de/bht/beuthbot/persistence/GenericEntityAccessFacade.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
4 | +import com.fasterxml.jackson.databind.module.SimpleModule; | ||
5 | + | ||
6 | +import de.bht.beuthbot.model.entities.GenericEntity; | ||
7 | +import de.bht.beuthbot.model.entities.GenericEntityAttribute; | ||
8 | + | ||
9 | +import java.io.IOException; | ||
10 | +import java.util.List; | ||
11 | +import java.util.stream.Collectors; | ||
12 | + | ||
13 | +/** | ||
14 | + * Created by Benjamin Rühl on 22.12.2017. | ||
15 | + * Provides convenience methods for accessing GenericEntity structures | ||
16 | + */ | ||
17 | +public class GenericEntityAccessFacade { | ||
18 | + | ||
19 | + public static <T> T getAttribute(GenericEntity contextEntity, String attributeName, Class<T> attributeType) { | ||
20 | + List<GenericEntityAttribute> propertiesWithName = contextEntity.getAttributes().stream().filter(a -> a.getName().equals(attributeName)).limit(1).collect(Collectors.toList()); | ||
21 | + | ||
22 | + if (propertiesWithName == null || propertiesWithName.isEmpty()) | ||
23 | + return null; | ||
24 | + | ||
25 | + GenericEntityAttribute property = propertiesWithName.get(0); | ||
26 | + | ||
27 | + try { | ||
28 | + String propertyAsJson = GenericEntityJsonConverter.toJson(property); | ||
29 | + return (T) JsonHelper.fromJson(propertyAsJson, attributeType); | ||
30 | + } catch (IOException e) { | ||
31 | + e.printStackTrace(); | ||
32 | + } | ||
33 | + | ||
34 | + return null; | ||
35 | + } | ||
36 | + | ||
37 | + public static void setAttribute(GenericEntity contextEntity, String attributeName, Object attributeValue) { | ||
38 | + String attributeValueAsJson = JsonHelper.toJson(attributeValue, attributeValue.getClass()); | ||
39 | + | ||
40 | + GenericEntityAttribute deserializedGenericAttribute = null; | ||
41 | + | ||
42 | + try { | ||
43 | + deserializedGenericAttribute = createGenericEntityAttributeFromJson(attributeValueAsJson); | ||
44 | + } catch (IOException e) { | ||
45 | + e.printStackTrace(); | ||
46 | + } | ||
47 | + | ||
48 | + if (deserializedGenericAttribute == null) | ||
49 | + return; | ||
50 | + | ||
51 | + deserializedGenericAttribute.setName(attributeName); | ||
52 | + | ||
53 | + contextEntity.setAttribute(deserializedGenericAttribute); | ||
54 | + } | ||
55 | + | ||
56 | + private static GenericEntityAttribute createGenericEntityAttributeFromJson(String json) throws IOException { | ||
57 | + ObjectMapper mapper = new ObjectMapper(); | ||
58 | + SimpleModule module = new SimpleModule(); | ||
59 | + module.addDeserializer(GenericEntity.class, new GenericEntityDeserializer()); | ||
60 | + module.addDeserializer(GenericEntityAttribute.class, new GenericEntityAttributeDeserializer()); | ||
61 | + mapper.registerModule(module); | ||
62 | + | ||
63 | + return mapper.readValue(json, GenericEntityAttribute.class); | ||
64 | + } | ||
65 | +} |
services/Common/src/main/java/de/bht/beuthbot/persistence/GenericEntityAttributeDeserializer.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.core.JsonParser; | ||
4 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
5 | +import com.fasterxml.jackson.core.JsonToken; | ||
6 | +import com.fasterxml.jackson.databind.DeserializationContext; | ||
7 | +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | ||
8 | + | ||
9 | +import de.bht.beuthbot.model.entities.GenericEntity; | ||
10 | +import de.bht.beuthbot.model.entities.GenericEntityAttribute; | ||
11 | +import de.bht.beuthbot.model.entities.GenericEntityAttributeValue; | ||
12 | + | ||
13 | +import javax.naming.OperationNotSupportedException; | ||
14 | +import java.io.IOException; | ||
15 | +import java.util.ArrayList; | ||
16 | +import java.util.List; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by Benjamin Rühl on 22.12.2017. | ||
20 | + */ | ||
21 | +public class GenericEntityAttributeDeserializer extends StdDeserializer<GenericEntityAttribute> { | ||
22 | + | ||
23 | + public GenericEntityAttributeDeserializer() { | ||
24 | + this(GenericEntityAttribute.class); | ||
25 | + } | ||
26 | + | ||
27 | + public GenericEntityAttributeDeserializer(Class<? extends GenericEntityAttribute> t) { | ||
28 | + super(t); | ||
29 | + } | ||
30 | + | ||
31 | + @Override | ||
32 | + public GenericEntityAttribute deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { | ||
33 | + if (jp.isClosed()) | ||
34 | + return null; | ||
35 | + | ||
36 | + GenericEntityAttribute deserializedAttribute = new GenericEntityAttribute(); | ||
37 | + | ||
38 | + JsonToken jsonToken = jp.getCurrentToken(); | ||
39 | + | ||
40 | + if (jsonToken.equals(JsonToken.FIELD_NAME)) { | ||
41 | + String fieldName = jp.getCurrentName(); | ||
42 | + deserializedAttribute.setName(fieldName); | ||
43 | + | ||
44 | + jsonToken = jp.nextToken(); | ||
45 | + | ||
46 | + if (jsonToken.equals(JsonToken.START_ARRAY)) { | ||
47 | + try { | ||
48 | + for (GenericEntityAttributeValue attributeValue : readAttributeValuesUntilArrayEndToken(jp, ctxt)) { | ||
49 | + deserializedAttribute.addValue(attributeValue); | ||
50 | + } | ||
51 | + } catch (OperationNotSupportedException e) { | ||
52 | + e.printStackTrace(); | ||
53 | + } | ||
54 | + } else { | ||
55 | + deserializedAttribute.addValue(readAttributeValueFromCurrentToken(jp, ctxt)); | ||
56 | + } | ||
57 | + } | ||
58 | + | ||
59 | + return deserializedAttribute; | ||
60 | + } | ||
61 | + | ||
62 | + private List<GenericEntityAttributeValue> readAttributeValuesUntilArrayEndToken(JsonParser jp, DeserializationContext ctxt) throws IOException, OperationNotSupportedException { | ||
63 | + List<GenericEntityAttributeValue> attributes = new ArrayList<>(); | ||
64 | + JsonToken jsonToken = jp.getCurrentToken(); | ||
65 | + | ||
66 | + if (!jsonToken.equals(JsonToken.START_ARRAY)) | ||
67 | + throw new OperationNotSupportedException("Method should not be called if current token is not START_ARRAY"); | ||
68 | + | ||
69 | + while (!jp.isClosed() || jsonToken.equals(JsonToken.END_ARRAY)) { | ||
70 | + jsonToken = jp.nextToken(); | ||
71 | + attributes.add(readAttributeValueFromCurrentToken(jp, ctxt)); | ||
72 | + } | ||
73 | + | ||
74 | + return attributes; | ||
75 | + } | ||
76 | + | ||
77 | + private GenericEntityAttributeValue readAttributeValueFromCurrentToken(JsonParser jp, DeserializationContext ctxt) throws IOException { | ||
78 | + GenericEntityAttributeValue attributeValue = new GenericEntityAttributeValue(); | ||
79 | + JsonToken jsonToken = jp.getCurrentToken(); | ||
80 | + | ||
81 | + if (jsonToken.equals(JsonToken.VALUE_FALSE) || jsonToken.equals(JsonToken.VALUE_TRUE)) { | ||
82 | + attributeValue.setValueAsBool(jp.getBooleanValue()); | ||
83 | + } else if (jsonToken.equals(JsonToken.VALUE_NUMBER_INT)) { | ||
84 | + attributeValue.setValueAsLong(jp.getLongValue()); | ||
85 | + } else if (jsonToken.equals(JsonToken.VALUE_NUMBER_FLOAT)) { | ||
86 | + attributeValue.setValueAsDouble(jp.getFloatValue()); | ||
87 | + } else if (jsonToken.equals(JsonToken.VALUE_STRING)) { | ||
88 | + attributeValue.setValueAsString(jp.getText()); | ||
89 | + } else if (jsonToken.equals(JsonToken.START_OBJECT)) { | ||
90 | + GenericEntity embeddedEntity = new GenericEntityDeserializer().deserialize(jp, ctxt); | ||
91 | + attributeValue.setValueAsEntity(embeddedEntity); | ||
92 | + } | ||
93 | + | ||
94 | + return attributeValue; | ||
95 | + } | ||
96 | +} |
services/Common/src/main/java/de/bht/beuthbot/persistence/GenericEntityDeserializer.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.core.JsonParser; | ||
4 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
5 | +import com.fasterxml.jackson.core.JsonToken; | ||
6 | +import com.fasterxml.jackson.databind.DeserializationContext; | ||
7 | +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | ||
8 | + | ||
9 | +import de.bht.beuthbot.model.entities.GenericEntity; | ||
10 | +import de.bht.beuthbot.model.entities.GenericEntityAttribute; | ||
11 | + | ||
12 | +import java.io.IOException; | ||
13 | + | ||
14 | +/** | ||
15 | + * Created by Benjamin Rühl on 23.12.2017. | ||
16 | + */ | ||
17 | +public class GenericEntityDeserializer extends StdDeserializer<GenericEntity> { | ||
18 | + | ||
19 | + public GenericEntityDeserializer() { | ||
20 | + this(GenericEntity.class); | ||
21 | + } | ||
22 | + | ||
23 | + public GenericEntityDeserializer(Class<? extends GenericEntity> t) { | ||
24 | + super(t); | ||
25 | + } | ||
26 | + | ||
27 | + @Override | ||
28 | + public GenericEntity deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { | ||
29 | + GenericEntity deserializedEntity = new GenericEntity(); | ||
30 | + GenericEntityAttributeDeserializer attributeDeserializer = new GenericEntityAttributeDeserializer(); | ||
31 | + | ||
32 | + while (!jp.isClosed()) { | ||
33 | + JsonToken jsonToken = jp.nextToken(); | ||
34 | + | ||
35 | + if (jsonToken.equals(JsonToken.FIELD_NAME)) { | ||
36 | + GenericEntityAttribute attribute = attributeDeserializer.deserialize(jp, ctxt); | ||
37 | + deserializedEntity.setAttribute(attribute); | ||
38 | + } | ||
39 | + } | ||
40 | + | ||
41 | + return deserializedEntity; | ||
42 | + } | ||
43 | +} | ||
0 | \ No newline at end of file | 44 | \ No newline at end of file |
services/Common/src/main/java/de/bht/beuthbot/persistence/GenericEntityJsonConverter.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.core.JsonFactory; | ||
4 | +import com.fasterxml.jackson.core.JsonGenerator; | ||
5 | + | ||
6 | +import de.bht.beuthbot.model.entities.GenericEntity; | ||
7 | +import de.bht.beuthbot.model.entities.GenericEntityAttribute; | ||
8 | +import de.bht.beuthbot.model.entities.GenericEntityAttributeValue; | ||
9 | + | ||
10 | +import java.io.IOException; | ||
11 | +import java.io.StringWriter; | ||
12 | +import java.lang.reflect.Field; | ||
13 | +import java.lang.reflect.ParameterizedType; | ||
14 | +import java.lang.reflect.Type; | ||
15 | +import java.util.Arrays; | ||
16 | +import java.util.List; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by Benjamin Rühl on 22.12.2017. | ||
20 | + * Helper class to serialize GenericEntity as if its generic attributes were normal fields | ||
21 | + */ | ||
22 | +public class GenericEntityJsonConverter { | ||
23 | + | ||
24 | + /** | ||
25 | + * Serializes a GenericEntity to json as if its generic attributes were normal fields. | ||
26 | + * Other normal fields are serialized as well. | ||
27 | + * @param genericEntity the context entity to be serialized with all its sub structure | ||
28 | + * @return a serialized string in json format | ||
29 | + * @throws IOException | ||
30 | + */ | ||
31 | + public static String toJson(GenericEntity genericEntity) throws IOException { | ||
32 | + JsonFactory jFactory = new JsonFactory(); | ||
33 | + StringWriter writer = new StringWriter(); | ||
34 | + JsonGenerator jsonGenerator = jFactory.createJsonGenerator(writer); | ||
35 | + | ||
36 | + jsonGenerator.writeStartObject(); | ||
37 | + writeGenericEntityWithoutStart(jsonGenerator, genericEntity); | ||
38 | + | ||
39 | + jsonGenerator.close(); | ||
40 | + return writer.toString(); | ||
41 | + } | ||
42 | + | ||
43 | + /** | ||
44 | + * Serializes a GenericEntityAttribute to json as if it would be a normal field. | ||
45 | + * @param genericAttribute the context attribute to be serialized with all its sub structure | ||
46 | + * @return a serialized string in json format | ||
47 | + * @throws IOException | ||
48 | + */ | ||
49 | + public static String toJson(GenericEntityAttribute genericAttribute) throws IOException { | ||
50 | + JsonFactory jFactory = new JsonFactory(); | ||
51 | + StringWriter writer = new StringWriter(); | ||
52 | + JsonGenerator jsonGenerator = jFactory.createJsonGenerator(writer); | ||
53 | + | ||
54 | + writeGenericAttribute(jsonGenerator, genericAttribute); | ||
55 | + | ||
56 | + jsonGenerator.close(); | ||
57 | + return writer.toString(); | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * Uses a JsonGenerator to append a GenericEntity and its content to json. | ||
62 | + * Does not open the json object because this part depends on whether the object stands for itself or is the value of a field. | ||
63 | + * However it does close the object. | ||
64 | + * @param jsonGenerator the generator used for building the json | ||
65 | + * @param genericEntity the context entity that is serialized | ||
66 | + * @throws IOException | ||
67 | + */ | ||
68 | + private static void writeGenericEntityWithoutStart(JsonGenerator jsonGenerator, GenericEntity genericEntity) throws IOException { | ||
69 | + writeClassFieldsWithoutGenericEntityHierarchy(jsonGenerator, genericEntity); | ||
70 | + | ||
71 | + for (GenericEntityAttribute genericAttribute : genericEntity.getAttributes()) { | ||
72 | + writeGenericAttribute(jsonGenerator, genericAttribute); | ||
73 | + } | ||
74 | + | ||
75 | + jsonGenerator.writeEndObject(); | ||
76 | + } | ||
77 | + | ||
78 | + private static void writeGenericAttribute(JsonGenerator jsonGenerator, GenericEntityAttribute genericAttribute) throws IOException { | ||
79 | + List<GenericEntityAttributeValue> attributeValues = genericAttribute.getValues(); | ||
80 | + | ||
81 | + if (attributeValues == null || attributeValues.isEmpty()) { | ||
82 | + jsonGenerator.writeNullField(genericAttribute.getName()); | ||
83 | + } else if (attributeValues.size() == 1) { | ||
84 | + writeGenericAttributeValue(jsonGenerator, genericAttribute.getValues().get(0), genericAttribute.getName()); | ||
85 | + } else { | ||
86 | + writeGenericAttributeMultiValue(jsonGenerator, genericAttribute); | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + private static void writeGenericAttributeMultiValue(JsonGenerator jsonGenerator, GenericEntityAttribute genericAttribute) throws IOException { | ||
91 | + jsonGenerator.writeArrayFieldStart(genericAttribute.getName()); | ||
92 | + | ||
93 | + for (GenericEntityAttributeValue genericValue : genericAttribute.getValues()) { | ||
94 | + writeGenericAttributeValueInArray(jsonGenerator, genericValue); | ||
95 | + } | ||
96 | + | ||
97 | + jsonGenerator.writeEndArray(); | ||
98 | + } | ||
99 | + | ||
100 | + private static void writeGenericAttributeValue(JsonGenerator jsonGenerator, GenericEntityAttributeValue genericAttributeValue, String attributeName) throws IOException { | ||
101 | + if (genericAttributeValue.getValueAsBool() != null) { | ||
102 | + jsonGenerator.writeBooleanField(attributeName, genericAttributeValue.getValueAsBool()); | ||
103 | + } else if (genericAttributeValue.getValueAsLong() != null) { | ||
104 | + jsonGenerator.writeNumberField(attributeName, genericAttributeValue.getValueAsLong()); | ||
105 | + } else if (genericAttributeValue.getValueAsDouble() != null) { | ||
106 | + jsonGenerator.writeNumberField(attributeName, genericAttributeValue.getValueAsDouble()); | ||
107 | + } else if (genericAttributeValue.getValueAsString() != null) { | ||
108 | + jsonGenerator.writeStringField(attributeName, genericAttributeValue.getValueAsString()); | ||
109 | + } else if (genericAttributeValue.getValueAsEntity() != null) { | ||
110 | + jsonGenerator.writeObjectFieldStart(attributeName); | ||
111 | + writeGenericEntityWithoutStart(jsonGenerator, genericAttributeValue.getValueAsEntity()); | ||
112 | + } | ||
113 | + } | ||
114 | + | ||
115 | + private static void writeGenericAttributeValueInArray(JsonGenerator jsonGenerator, GenericEntityAttributeValue genericAttributeValue) throws IOException { | ||
116 | + if (genericAttributeValue.getValueAsBool() != null) { | ||
117 | + jsonGenerator.writeBoolean(genericAttributeValue.getValueAsBool()); | ||
118 | + } else if (genericAttributeValue.getValueAsLong() != null) { | ||
119 | + jsonGenerator.writeNumber(genericAttributeValue.getValueAsLong()); | ||
120 | + } else if (genericAttributeValue.getValueAsDouble() != null) { | ||
121 | + jsonGenerator.writeNumber(genericAttributeValue.getValueAsDouble()); | ||
122 | + } else if (genericAttributeValue.getValueAsString() != null) { | ||
123 | + jsonGenerator.writeString(genericAttributeValue.getValueAsString()); | ||
124 | + } else if (genericAttributeValue.getValueAsEntity() != null) { | ||
125 | + jsonGenerator.writeStartObject(); | ||
126 | + writeGenericEntityWithoutStart(jsonGenerator, genericAttributeValue.getValueAsEntity()); | ||
127 | + } | ||
128 | + } | ||
129 | + | ||
130 | + /** | ||
131 | + * Uses a JsonGenerator to append all fields and their values of the targetObject to json. | ||
132 | + * Ignores fields that have GenericEntity, GenericEntityAttribute or GenericEntityAttributeValue as type or generic list parameter. | ||
133 | + * @throws IOException | ||
134 | + */ | ||
135 | + private static void writeClassFieldsWithoutGenericEntityHierarchy(JsonGenerator jsonGenerator, Object targetObject) throws IOException { | ||
136 | + for (Field field : targetObject.getClass().getFields()) { | ||
137 | + if (isGenericEntityHierarchyType(field)) | ||
138 | + continue; | ||
139 | + | ||
140 | + try { | ||
141 | + jsonGenerator.writeObjectField(field.getName(), field.get(targetObject)); | ||
142 | + } catch (Exception e) { | ||
143 | + jsonGenerator.writeStringField(field.getName(), e.getClass().getName()); | ||
144 | + e.printStackTrace(); | ||
145 | + } | ||
146 | + } | ||
147 | + } | ||
148 | + | ||
149 | + /** | ||
150 | + * Determines whether the field's type is GenericEntity, GenericEntityAttribute or GenericEntityAttributeValue | ||
151 | + * or a list type with one of those types as generic type parameter. | ||
152 | + */ | ||
153 | + private static boolean isGenericEntityHierarchyType(Field field) { | ||
154 | + List<Class> typesOfGenericEntityHierarchy = Arrays.asList(GenericEntity.class, GenericEntityAttribute.class, GenericEntityAttributeValue.class); | ||
155 | + | ||
156 | + // check for list types | ||
157 | + try { | ||
158 | + if (Iterable.class.isAssignableFrom(field.getType())) { | ||
159 | + Type fieldFirstGenericType = ((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]; | ||
160 | + if (typesOfGenericEntityHierarchy.contains(fieldFirstGenericType.getClass())) | ||
161 | + return true; | ||
162 | + } | ||
163 | + } catch (Exception e) { | ||
164 | + // not a list or not generic | ||
165 | + } | ||
166 | + | ||
167 | + return typesOfGenericEntityHierarchy.contains(field.getType()); | ||
168 | + } | ||
169 | +} |
services/Common/src/main/java/de/bht/beuthbot/persistence/JsonHelper.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import com.google.gson.Gson; | ||
4 | +import com.google.gson.GsonBuilder; | ||
5 | + | ||
6 | +/** | ||
7 | + * Created by Benjamin Rühl on 03.12.2017. | ||
8 | + * A simple wrapper around the json serializer to make sure the same serializer is used across different classes | ||
9 | + */ | ||
10 | +public class JsonHelper { | ||
11 | + | ||
12 | + private static final Gson gson = new GsonBuilder().serializeNulls().create(); | ||
13 | + | ||
14 | + public static Object fromJson(String json, Class sourceClass) { | ||
15 | + return gson.fromJson(json, sourceClass); | ||
16 | + } | ||
17 | + | ||
18 | + public static String toJson(Object source, Class sourceClass) { | ||
19 | + return gson.toJson(source, sourceClass); | ||
20 | + } | ||
21 | +} |
services/Common/src/main/java/de/bht/beuthbot/persistence/JsonMapUserType.java
0 → 100644
1 | +package de.bht.beuthbot.persistence; | ||
2 | + | ||
3 | +import org.hibernate.HibernateException; | ||
4 | +import org.hibernate.engine.spi.SharedSessionContractImplementor; | ||
5 | +import org.hibernate.internal.util.compare.EqualsHelper; | ||
6 | +import org.hibernate.type.SerializationException; | ||
7 | +import org.hibernate.usertype.UserType; | ||
8 | +import org.postgresql.util.PGobject; | ||
9 | + | ||
10 | +import java.io.Serializable; | ||
11 | +import java.sql.PreparedStatement; | ||
12 | +import java.sql.ResultSet; | ||
13 | +import java.sql.SQLException; | ||
14 | +import java.sql.Types; | ||
15 | +import java.util.HashMap; | ||
16 | +import java.util.Map; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by Benjamin Rühl on 03.12.2017. | ||
20 | + * Custom Hibernate UserType for mapping json typed columns in postgres to java maps | ||
21 | + * @see <a href="http://vojtechruzicka.com/postgresqls-jsonb-type-mapping-using-hibernate/">vojtechruzicka.com</a> | ||
22 | + */ | ||
23 | +public class JsonMapUserType implements UserType { | ||
24 | + | ||
25 | + @Override | ||
26 | + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { | ||
27 | + if (value == null) { | ||
28 | + st.setNull(index, Types.OTHER); | ||
29 | + } else { | ||
30 | + st.setObject(index, JsonHelper.toJson(value, Map.class), Types.OTHER); | ||
31 | + } | ||
32 | + } | ||
33 | + | ||
34 | + @Override | ||
35 | + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { | ||
36 | + PGobject o = (PGobject) rs.getObject(names[0]); | ||
37 | + if (o.getValue() != null) { | ||
38 | + return JsonHelper.fromJson(o.getValue(), Map.class); | ||
39 | + } | ||
40 | + | ||
41 | + return new HashMap<String, String>(); | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + public Object deepCopy(Object originalValue) throws HibernateException { | ||
46 | + if (originalValue == null) { | ||
47 | + return null; | ||
48 | + } | ||
49 | + | ||
50 | + if (!(originalValue instanceof Map)) { | ||
51 | + return null; | ||
52 | + } | ||
53 | + | ||
54 | + Map<String, String> resultMap = new HashMap<>(); | ||
55 | + | ||
56 | + Map<?, ?> tempMap = (Map<?, ?>) originalValue; | ||
57 | + tempMap.forEach((key, value) -> resultMap.put((String) key, (String) value)); | ||
58 | + | ||
59 | + return resultMap; | ||
60 | + } | ||
61 | + | ||
62 | + @Override | ||
63 | + public Serializable disassemble(Object value) throws HibernateException { | ||
64 | + Object copy = deepCopy(value); | ||
65 | + | ||
66 | + if (copy instanceof Serializable) { | ||
67 | + return (Serializable) copy; | ||
68 | + } | ||
69 | + | ||
70 | + throw new SerializationException(String.format("Cannot serialize '%s', %s is not Serializable.", value, value.getClass()), null); | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + public Object assemble(Serializable cached, Object owner) throws HibernateException { | ||
75 | + return deepCopy(cached); | ||
76 | + } | ||
77 | + | ||
78 | + @Override | ||
79 | + public Object replace(Object original, Object target, Object owner) throws HibernateException { | ||
80 | + return deepCopy(original); | ||
81 | + } | ||
82 | + | ||
83 | + @Override | ||
84 | + public boolean isMutable() { | ||
85 | + return true; | ||
86 | + } | ||
87 | + | ||
88 | + @Override | ||
89 | + public int hashCode(Object x) throws HibernateException { | ||
90 | + if (x == null) { | ||
91 | + return 0; | ||
92 | + } | ||
93 | + | ||
94 | + return x.hashCode(); | ||
95 | + } | ||
96 | + | ||
97 | + @Override | ||
98 | + public boolean equals(Object x, Object y) throws HibernateException { | ||
99 | + return EqualsHelper.equals(x, y); | ||
100 | + } | ||
101 | + | ||
102 | + @Override | ||
103 | + public Class<?> returnedClass() { | ||
104 | + return Map.class; | ||
105 | + } | ||
106 | + | ||
107 | + @Override | ||
108 | + public int[] sqlTypes() { | ||
109 | + return new int[]{Types.JAVA_OBJECT}; | ||
110 | + } | ||
111 | +} |
services/Common/src/main/resources/META-INF/persistence.xml
0 → 100644
1 | +<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" | ||
2 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
3 | + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence | ||
4 | + http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" | ||
5 | + version="2.1"> | ||
6 | + | ||
7 | + <persistence-unit name="PostgresPU" transaction-type="JTA"> | ||
8 | + | ||
9 | + <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> | ||
10 | + | ||
11 | + <jta-data-source>java:jboss/datasources/PostgreSQLDS</jta-data-source> | ||
12 | + | ||
13 | + <properties> | ||
14 | + | ||
15 | + <!--<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>--> <!-- DB Dialect --> | ||
16 | + <property name="hibernate.dialect" value="de.bht.beuthbot.persistence.ExtendedPostgreSQLDialect"/> | ||
17 | + <!--<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>--> | ||
18 | + <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <!-- create / create-drop / update / none --> | ||
19 | + <property name="hibernate.archive.autodetection" value="class"/> | ||
20 | + | ||
21 | + <property name="hibernate.show_sql" value="true" /> <!-- Show SQL in console --> | ||
22 | + <property name="hibernate.format_sql" value="true" /> <!-- Show SQL formatted --> | ||
23 | + </properties> | ||
24 | + | ||
25 | + </persistence-unit> | ||
26 | + | ||
27 | +</persistence> | ||
0 | \ No newline at end of file | 28 | \ No newline at end of file |
services/Global/build.gradle
@@ -13,7 +13,6 @@ dependencies { | @@ -13,7 +13,6 @@ dependencies { | ||
13 | "org.apache.httpcomponents:httpclient:4.5.3", | 13 | "org.apache.httpcomponents:httpclient:4.5.3", |
14 | "commons-io:commons-io:2.5" | 14 | "commons-io:commons-io:2.5" |
15 | 15 | ||
16 | - | ||
17 | providedCompile "org.slf4j:slf4j-api:1.7.25" | 16 | providedCompile "org.slf4j:slf4j-api:1.7.25" |
18 | 17 | ||
19 | testCompile "org.jboss.arquillian.junit:arquillian-junit-container:1.1.13.Final", | 18 | testCompile "org.jboss.arquillian.junit:arquillian-junit-container:1.1.13.Final", |