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
docker/docker-compose.yml
... | ... | @@ -9,13 +9,20 @@ services: |
9 | 9 | - "8080:8080" |
10 | 10 | - "8787:8787" |
11 | 11 | - "9990:9990" |
12 | + environment: | |
13 | + POSTGRES_USER: beuthbot_app | |
14 | + POSTGRES_PASSWORD: VhS7WPVpdYEHYLpf | |
15 | + POSTGRES_DB: beuthbot | |
12 | 16 | volumes: |
13 | 17 | - ./wildfly/volumes/deployments/:/opt/jboss/wildfly/standalone/deployments/ |
14 | 18 | - ./wildfly/volumes/logs/:/opt/jboss/wildfly/standalone/log/ |
15 | 19 | - ./wildfly/volumes/conf/:/opt/jboss/wildfly/standalone/conf/ |
20 | + - ./wildfly/volumes/modules/org/postgres/main/:/opt/jboss/wildfly/modules/org/postgres/main/ | |
16 | 21 | links: |
17 | 22 | - rasa-server |
23 | + - postgres-db | |
18 | 24 | command: /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug |
25 | + | |
19 | 26 | rasa-server: |
20 | 27 | build: ./rasa_nlu |
21 | 28 | container_name: rasa_nlu |
... | ... | @@ -25,4 +32,22 @@ services: |
25 | 32 | volumes: |
26 | 33 | - ./rasa_nlu/volumes/data/api/:/app/data/api |
27 | 34 | - ./rasa_nlu/volumes/logs/:/app/logs/ |
28 | - command: python -m rasa_nlu.server -c config/chatbot_config.json --server_model_dirs=default | |
29 | 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 | 55 | \ No newline at end of file | ... | ... |
docker/postgres/volumes/logs/README.md
0 → 100644
docker/wildfly/Dockerfile
1 | 1 | FROM jboss/wildfly:10.1.0.Final |
2 | 2 | RUN /opt/jboss/wildfly/bin/add-user.sh admin Admin |
3 | -COPY standalone.xml /opt/jboss/wildfly/standalone/configuration/standalone.xml | |
4 | 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 | 11 | \ No newline at end of file | ... | ... |
docker/wildfly/standalone.xml
... | ... | @@ -161,7 +161,7 @@ |
161 | 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 | 162 | </managed-scheduled-executor-services> |
163 | 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 | 165 | </subsystem> |
166 | 166 | <subsystem xmlns="urn:jboss:domain:ejb3:4.0"> |
167 | 167 | <session-bean> |
... | ... | @@ -417,6 +417,40 @@ |
417 | 417 | <client-config name="Standard-Client-Config"/> |
418 | 418 | </subsystem> |
419 | 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 | 454 | </profile> |
421 | 455 | |
422 | 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 | 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 | 9 | compile "org.jboss.spec:jboss-javaee-7.0:1.1.0.Final", |
10 | 10 | "com.google.code.gson:gson:2.8.1", |
11 | 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 | 20 | sourceCompatibility = 1.8 |
15 | 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 | 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 | 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 | 28 | \ No newline at end of file | ... | ... |
services/Global/build.gradle