From 5a5c1dc25a8ce35fe820c183ae0eed551fc7dfcc Mon Sep 17 00:00:00 2001 From: Mohit Date: Wed, 30 Oct 2024 10:46:51 +0530 Subject: [PATCH 1/2] Hibernate Changes --- build.gradle | 2 +- contrib/hibernate/build.gradle | 63 +- .../flipkart/gjex/db/DataSourceFactory.java | 565 ++++++++++++++++++ .../gjex/db/DatabaseConfiguration.java | 7 + .../java/com/flipkart/gjex/db/Duration.java | 136 +++++ .../flipkart/gjex/db/ManagedDataSource.java | 7 + .../gjex/db/ManagedPooledDataSource.java | 22 + .../gjex/db/PooledDataSourceFactory.java | 32 + .../gjex/db/TimeBoundHealthCheck.java | 28 + .../flipkart/gjex/hibernate/AbstractDAO.java | 224 +++++++ .../gjex/hibernate/HibernateBundle.java | 101 ++++ .../hibernate/ScanningHibernateBundle.java | 74 +++ .../gjex/hibernate/SessionFactoryFactory.java | 88 +++ .../hibernate/SessionFactoryHealthCheck.java | 75 +++ .../flipkart/gjex/hibernate/UnitOfWork.java | 22 + .../UnitOfWorkApplicationListener.java | 107 ++++ .../gjex/hibernate/UnitOfWorkAspect.java | 113 ++++ .../gjex/hibernate/UnitOfWorkInterceptor.java | 122 ++++ settings.gradle | 1 + 19 files changed, 1783 insertions(+), 6 deletions(-) create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/DataSourceFactory.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/DatabaseConfiguration.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/Duration.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedDataSource.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedPooledDataSource.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/PooledDataSourceFactory.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/db/TimeBoundHealthCheck.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/AbstractDAO.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/HibernateBundle.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/ScanningHibernateBundle.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryFactory.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryHealthCheck.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWork.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkApplicationListener.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkAspect.java create mode 100644 contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkInterceptor.java diff --git a/build.gradle b/build.gradle index 254ad6a3..6a8fd15d 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ tasks.withType(GenerateModuleMetadata) { enabled = false } -def gJEXVersion = '1.41-SNAPSHOT' +def gJEXVersion = '1.41-SNAPSHOT-HIBERNATE' def grpcVersion = '1.60.0' def jacksonVersion = '2.16.1' def guiceVersion = '5.1.0' diff --git a/contrib/hibernate/build.gradle b/contrib/hibernate/build.gradle index 905d947a..bb9fd24b 100644 --- a/contrib/hibernate/build.gradle +++ b/contrib/hibernate/build.gradle @@ -1,20 +1,73 @@ -apply plugin: 'java' +plugins { + id 'java-library' + id 'maven-publish' + id "io.freefair.lombok" version "8.6" +} repositories { mavenLocal() mavenCentral() } +publishing { + publications { + mavenJava(MavenPublication) { + artifactId = 'hibernate' + from components.java + pom { + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + } + } + } + repositories { + maven { + url "https://clojars.org/repo" + credentials { + username = rootProject.ext.clojarsusername + password = rootProject.ext.clojarspassword + } + } + } +} + dependencies { - compile "com.flipkart.grpc-jexpress:core:1.+" - compile "com.flipkart.grpc-jexpress:runtime:1.+" - compile "com.flipkart.grpc-jexpress:guice:1.+" + + implementation project(':core') + implementation libraries.lombok + implementation libraries.dw_metrics + implementation libraries.dw_metrics_healthchecks + implementation libraries.guice + implementation libraries.guava + implementation libraries.hibernate_validator + implementation 'io.grpc:grpc-core:1.60.0' + implementation 'io.grpc:grpc-stub:1.60.0' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'org.hibernate:hibernate-core:5.6.15.Final' + implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1' + implementation 'javax.validation:validation-api:2.0.1.Final' + implementation 'org.apache.tomcat:tomcat-jdbc:9.0.80' + implementation 'mysql:mysql-connector-java:5.1.44' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' + implementation 'io.dropwizard.metrics5:metrics-core:5.0.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:2.15.2' + implementation 'org.glassfish.jersey.core:jersey-server:2.36' + implementation 'org.glassfish.jersey.containers:jersey-container-servlet-core:3.1.2' + implementation 'aopalliance:aopalliance:1.0' + implementation 'javax.inject:javax.inject:1' } task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' + archiveClassifier = 'sources' from sourceSets.main.allSource } + artifacts { archives sourcesJar } diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DataSourceFactory.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DataSourceFactory.java new file mode 100644 index 00000000..3b28973c --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DataSourceFactory.java @@ -0,0 +1,565 @@ +package com.flipkart.gjex.db; + +import io.dropwizard.metrics5.MetricRegistry; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.primitives.Ints; +import org.apache.tomcat.jdbc.pool.PoolProperties; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.*; + +public class DataSourceFactory implements PooledDataSourceFactory { + @NotNull + private String driverClass = null; + @Min(0L) + @Max(100L) + private int abandonWhenPercentageFull = 0; + private boolean alternateUsernamesAllowed = false; + private boolean commitOnReturn = false; + private boolean rollbackOnReturn = false; + private Boolean autoCommitByDefault; + private Boolean readOnlyByDefault; + private String user = null; + private String password = null; + @NotNull + private String url = null; + @NotNull + private Map properties = new LinkedHashMap(); + private String defaultCatalog; + @NotNull + private DataSourceFactory.TransactionIsolation defaultTransactionIsolation; + private boolean useFairQueue; + @Min(0L) + private int initialSize; + @Min(0L) + private int minSize; + @Min(1L) + private int maxSize; + private String initializationQuery; + private boolean logAbandonedConnections; + private boolean logValidationErrors; + private Duration maxConnectionAge; + @NotNull + private Duration maxWaitForConnection; + @NotNull + private Duration minIdleTime; + @NotNull + private String validationQuery; + private Duration validationQueryTimeout; + private boolean checkConnectionWhileIdle; + private boolean checkConnectionOnBorrow; + private boolean checkConnectionOnConnect; + private boolean checkConnectionOnReturn; + private boolean autoCommentsEnabled; + @NotNull + private Duration evictionInterval; + @NotNull + private Duration validationInterval; + private Optional validatorClassName; + private boolean removeAbandoned; + @NotNull + private Duration removeAbandonedTimeout; + + public DataSourceFactory() { + this.defaultTransactionIsolation = TransactionIsolation.DEFAULT; + this.useFairQueue = true; + this.initialSize = 10; + this.minSize = 10; + this.maxSize = 100; + this.logAbandonedConnections = false; + this.logValidationErrors = false; + this.maxWaitForConnection = Duration.seconds(30L); + this.minIdleTime = Duration.minutes(1L); + this.validationQuery = "/* Health Check */ SELECT 1"; + this.checkConnectionWhileIdle = true; + this.checkConnectionOnBorrow = false; + this.checkConnectionOnConnect = true; + this.checkConnectionOnReturn = false; + this.autoCommentsEnabled = true; + this.evictionInterval = Duration.seconds(5L); + this.validationInterval = Duration.seconds(30L); + this.validatorClassName = Optional.empty(); + this.removeAbandoned = false; + this.removeAbandonedTimeout = Duration.seconds(60L); + } + + @JsonProperty + public boolean isAutoCommentsEnabled() { + return this.autoCommentsEnabled; + } + + @JsonProperty + public void setAutoCommentsEnabled(boolean autoCommentsEnabled) { + this.autoCommentsEnabled = autoCommentsEnabled; + } + + @JsonProperty + public String getDriverClass() { + return this.driverClass; + } + + @JsonProperty + public void setDriverClass(String driverClass) { + this.driverClass = driverClass; + } + + @JsonProperty + public String getUser() { + return this.user; + } + + @JsonProperty + public void setUser(String user) { + this.user = user; + } + + @JsonProperty + public String getPassword() { + return this.password; + } + + @JsonProperty + public void setPassword(String password) { + this.password = password; + } + + @JsonProperty + public String getUrl() { + return this.url; + } + + @JsonProperty + public void setUrl(String url) { + this.url = url; + } + + @JsonProperty + public Map getProperties() { + return this.properties; + } + + @JsonProperty + public void setProperties(Map properties) { + this.properties = properties; + } + + @JsonProperty + public Duration getMaxWaitForConnection() { + return this.maxWaitForConnection; + } + + @JsonProperty + public void setMaxWaitForConnection(Duration maxWaitForConnection) { + this.maxWaitForConnection = maxWaitForConnection; + } + + @JsonProperty + public String getValidationQuery() { + return this.validationQuery; + } + + /** @deprecated */ + @Deprecated + @JsonIgnore + public String getHealthCheckValidationQuery() { + return this.getValidationQuery(); + } + + @JsonProperty + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + @JsonProperty + public int getMinSize() { + return this.minSize; + } + + @JsonProperty + public void setMinSize(int minSize) { + this.minSize = minSize; + } + + @JsonProperty + public int getMaxSize() { + return this.maxSize; + } + + @JsonProperty + public void setMaxSize(int maxSize) { + this.maxSize = maxSize; + } + + @JsonProperty + public boolean getCheckConnectionWhileIdle() { + return this.checkConnectionWhileIdle; + } + + @JsonProperty + public void setCheckConnectionWhileIdle(boolean checkConnectionWhileIdle) { + this.checkConnectionWhileIdle = checkConnectionWhileIdle; + } + + /** @deprecated */ + @Deprecated + @JsonProperty + public boolean isDefaultReadOnly() { + return Boolean.TRUE.equals(this.readOnlyByDefault); + } + + /** @deprecated */ + @Deprecated + @JsonProperty + public void setDefaultReadOnly(boolean defaultReadOnly) { + this.readOnlyByDefault = defaultReadOnly; + } + + @JsonIgnore + public boolean isMinSizeLessThanMaxSize() { + return this.minSize <= this.maxSize; + } + + @JsonIgnore + public boolean isInitialSizeLessThanMaxSize() { + return this.initialSize <= this.maxSize; + } + + @JsonIgnore + public boolean isInitialSizeGreaterThanMinSize() { + return this.minSize <= this.initialSize; + } + + @JsonProperty + public int getAbandonWhenPercentageFull() { + return this.abandonWhenPercentageFull; + } + + @JsonProperty + public void setAbandonWhenPercentageFull(int percentage) { + this.abandonWhenPercentageFull = percentage; + } + + @JsonProperty + public boolean isAlternateUsernamesAllowed() { + return this.alternateUsernamesAllowed; + } + + @JsonProperty + public void setAlternateUsernamesAllowed(boolean allow) { + this.alternateUsernamesAllowed = allow; + } + + @JsonProperty + public boolean getCommitOnReturn() { + return this.commitOnReturn; + } + + @JsonProperty + public boolean getRollbackOnReturn() { + return this.rollbackOnReturn; + } + + @JsonProperty + public void setCommitOnReturn(boolean commitOnReturn) { + this.commitOnReturn = commitOnReturn; + } + + @JsonProperty + public void setRollbackOnReturn(boolean rollbackOnReturn) { + this.rollbackOnReturn = rollbackOnReturn; + } + + @JsonProperty + public Boolean getAutoCommitByDefault() { + return this.autoCommitByDefault; + } + + @JsonProperty + public void setAutoCommitByDefault(Boolean autoCommit) { + this.autoCommitByDefault = autoCommit; + } + + @JsonProperty + public String getDefaultCatalog() { + return this.defaultCatalog; + } + + @JsonProperty + public void setDefaultCatalog(String defaultCatalog) { + this.defaultCatalog = defaultCatalog; + } + + @JsonProperty + public Boolean getReadOnlyByDefault() { + return this.readOnlyByDefault; + } + + @JsonProperty + public void setReadOnlyByDefault(Boolean readOnlyByDefault) { + this.readOnlyByDefault = readOnlyByDefault; + } + + @JsonProperty + public TransactionIsolation getDefaultTransactionIsolation() { + return this.defaultTransactionIsolation; + } + + @JsonProperty + public void setDefaultTransactionIsolation(TransactionIsolation isolation) { + this.defaultTransactionIsolation = isolation; + } + + @JsonProperty + public boolean getUseFairQueue() { + return this.useFairQueue; + } + + @JsonProperty + public void setUseFairQueue(boolean fair) { + this.useFairQueue = fair; + } + + @JsonProperty + public int getInitialSize() { + return this.initialSize; + } + + @JsonProperty + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + + @JsonProperty + public String getInitializationQuery() { + return this.initializationQuery; + } + + @JsonProperty + public void setInitializationQuery(String query) { + this.initializationQuery = query; + } + + @JsonProperty + public boolean getLogAbandonedConnections() { + return this.logAbandonedConnections; + } + + @JsonProperty + public void setLogAbandonedConnections(boolean log) { + this.logAbandonedConnections = log; + } + + @JsonProperty + public boolean getLogValidationErrors() { + return this.logValidationErrors; + } + + @JsonProperty + public void setLogValidationErrors(boolean log) { + this.logValidationErrors = log; + } + + @JsonProperty + public Optional getMaxConnectionAge() { + return Optional.ofNullable(this.maxConnectionAge); + } + + @JsonProperty + public void setMaxConnectionAge(Duration age) { + this.maxConnectionAge = age; + } + + @JsonProperty + public Duration getMinIdleTime() { + return this.minIdleTime; + } + + @JsonProperty + public void setMinIdleTime(Duration time) { + this.minIdleTime = time; + } + + @JsonProperty + public boolean getCheckConnectionOnBorrow() { + return this.checkConnectionOnBorrow; + } + + @JsonProperty + public void setCheckConnectionOnBorrow(boolean checkConnectionOnBorrow) { + this.checkConnectionOnBorrow = checkConnectionOnBorrow; + } + + @JsonProperty + public boolean getCheckConnectionOnConnect() { + return this.checkConnectionOnConnect; + } + + @JsonProperty + public void setCheckConnectionOnConnect(boolean checkConnectionOnConnect) { + this.checkConnectionOnConnect = checkConnectionOnConnect; + } + + @JsonProperty + public boolean getCheckConnectionOnReturn() { + return this.checkConnectionOnReturn; + } + + @JsonProperty + public void setCheckConnectionOnReturn(boolean checkConnectionOnReturn) { + this.checkConnectionOnReturn = checkConnectionOnReturn; + } + + @JsonProperty + public Duration getEvictionInterval() { + return this.evictionInterval; + } + + @JsonProperty + public void setEvictionInterval(Duration interval) { + this.evictionInterval = interval; + } + + @JsonProperty + public Duration getValidationInterval() { + return this.validationInterval; + } + + @JsonProperty + public void setValidationInterval(Duration validationInterval) { + this.validationInterval = validationInterval; + } + + @JsonProperty + public Optional getValidationQueryTimeout() { + return Optional.ofNullable(this.validationQueryTimeout); + } + + @JsonProperty + public Optional getValidatorClassName() { + return this.validatorClassName; + } + + @JsonProperty + public void setValidatorClassName(Optional validatorClassName) { + this.validatorClassName = validatorClassName; + } + + /** @deprecated */ + @Deprecated + @JsonIgnore + public Optional getHealthCheckValidationTimeout() { + return this.getValidationQueryTimeout(); + } + + @JsonProperty + public void setValidationQueryTimeout(Duration validationQueryTimeout) { + this.validationQueryTimeout = validationQueryTimeout; + } + + @JsonProperty + public boolean isRemoveAbandoned() { + return this.removeAbandoned; + } + + @JsonProperty + public void setRemoveAbandoned(boolean removeAbandoned) { + this.removeAbandoned = removeAbandoned; + } + + @JsonProperty + public Duration getRemoveAbandonedTimeout() { + return this.removeAbandonedTimeout; + } + + @JsonProperty + public void setRemoveAbandonedTimeout(Duration removeAbandonedTimeout) { + this.removeAbandonedTimeout = (Duration) Objects.requireNonNull(removeAbandonedTimeout); + } + + public void asSingleConnectionPool() { + this.minSize = 1; + this.maxSize = 1; + this.initialSize = 1; + } + + public ManagedDataSource build(MetricRegistry metricRegistry, String name) { + Properties properties = new Properties(); + Iterator var4 = this.properties.entrySet().iterator(); + + while(var4.hasNext()) { + Map.Entry property = (Map.Entry)var4.next(); + properties.setProperty((String)property.getKey(), (String)property.getValue()); + } + + PoolProperties poolConfig = new PoolProperties(); + poolConfig.setAbandonWhenPercentageFull(this.abandonWhenPercentageFull); + poolConfig.setAlternateUsernameAllowed(this.alternateUsernamesAllowed); + poolConfig.setCommitOnReturn(this.commitOnReturn); + poolConfig.setRollbackOnReturn(this.rollbackOnReturn); + poolConfig.setDbProperties(properties); + poolConfig.setDefaultAutoCommit(this.autoCommitByDefault); + poolConfig.setDefaultCatalog(this.defaultCatalog); + poolConfig.setDefaultReadOnly(this.readOnlyByDefault); + poolConfig.setDefaultTransactionIsolation(this.defaultTransactionIsolation.get()); + poolConfig.setDriverClassName(this.driverClass); + poolConfig.setFairQueue(this.useFairQueue); + poolConfig.setInitialSize(this.initialSize); + poolConfig.setInitSQL(this.initializationQuery); + poolConfig.setLogAbandoned(this.logAbandonedConnections); + poolConfig.setLogValidationErrors(this.logValidationErrors); + poolConfig.setMaxActive(this.maxSize); + poolConfig.setMaxIdle(this.maxSize); + poolConfig.setMinIdle(this.minSize); + if (this.getMaxConnectionAge().isPresent()) { + poolConfig.setMaxAge(this.maxConnectionAge.toMilliseconds()); + } + + poolConfig.setMaxWait((int)this.maxWaitForConnection.toMilliseconds()); + poolConfig.setMinEvictableIdleTimeMillis((int)this.minIdleTime.toMilliseconds()); + poolConfig.setName(name); + poolConfig.setUrl(this.url); + poolConfig.setUsername(this.user); + poolConfig.setPassword(this.user != null && this.password == null ? "" : this.password); + poolConfig.setRemoveAbandoned(this.removeAbandoned); + poolConfig.setRemoveAbandonedTimeout(Ints.saturatedCast(this.removeAbandonedTimeout.toSeconds())); + poolConfig.setTestWhileIdle(this.checkConnectionWhileIdle); + poolConfig.setValidationQuery(this.validationQuery); + poolConfig.setTestOnBorrow(this.checkConnectionOnBorrow); + poolConfig.setTestOnConnect(this.checkConnectionOnConnect); + poolConfig.setTestOnReturn(this.checkConnectionOnReturn); + poolConfig.setTimeBetweenEvictionRunsMillis((int)this.evictionInterval.toMilliseconds()); + poolConfig.setValidationInterval(this.validationInterval.toMilliseconds()); + if (this.getValidationQueryTimeout().isPresent()) { + poolConfig.setValidationQueryTimeout((int)this.validationQueryTimeout.toSeconds()); + } + + if (this.validatorClassName.isPresent()) { + poolConfig.setValidatorClassName((String)this.validatorClassName.get()); + } + + return new ManagedPooledDataSource(poolConfig, metricRegistry); + } + + public static enum TransactionIsolation { + NONE(0), + DEFAULT(-1), + READ_UNCOMMITTED(1), + READ_COMMITTED(2), + REPEATABLE_READ(4), + SERIALIZABLE(8); + + private final int value; + + private TransactionIsolation(int value) { + this.value = value; + } + + public int get() { + return this.value; + } + } +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DatabaseConfiguration.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DatabaseConfiguration.java new file mode 100644 index 00000000..2a1d40f7 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/DatabaseConfiguration.java @@ -0,0 +1,7 @@ +package com.flipkart.gjex.db; + +import com.flipkart.gjex.core.GJEXConfiguration; + +public interface DatabaseConfiguration { + PooledDataSourceFactory getDataSourceFactory(T var1); +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/Duration.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/Duration.java new file mode 100644 index 00000000..7f2555fb --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/Duration.java @@ -0,0 +1,136 @@ +package com.flipkart.gjex.db; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Duration implements Comparable { + private static final Pattern DURATION_PATTERN = Pattern.compile("(\\d+)\\s*(\\S+)"); + private static final Map SUFFIXES; + private final long count; + private final TimeUnit unit; + + public static Duration nanoseconds(long count) { + return new Duration(count, TimeUnit.NANOSECONDS); + } + + public static Duration microseconds(long count) { + return new Duration(count, TimeUnit.MICROSECONDS); + } + + public static Duration milliseconds(long count) { + return new Duration(count, TimeUnit.MILLISECONDS); + } + + public static Duration seconds(long count) { + return new Duration(count, TimeUnit.SECONDS); + } + + public static Duration minutes(long count) { + return new Duration(count, TimeUnit.MINUTES); + } + + public static Duration hours(long count) { + return new Duration(count, TimeUnit.HOURS); + } + + public static Duration days(long count) { + return new Duration(count, TimeUnit.DAYS); + } + + @JsonCreator + public static Duration parse(String duration) { + Matcher matcher = DURATION_PATTERN.matcher(duration); + Preconditions.checkArgument(matcher.matches(), "Invalid duration: " + duration); + long count = Long.parseLong(matcher.group(1)); + TimeUnit unit = (TimeUnit)SUFFIXES.get(matcher.group(2)); + if (unit == null) { + throw new IllegalArgumentException("Invalid duration: " + duration + ". Wrong time unit"); + } else { + return new Duration(count, unit); + } + } + + private Duration(long count, TimeUnit unit) { + this.count = count; + this.unit = (TimeUnit) Objects.requireNonNull(unit); + } + + public long getQuantity() { + return this.count; + } + + public TimeUnit getUnit() { + return this.unit; + } + + public long toNanoseconds() { + return TimeUnit.NANOSECONDS.convert(this.count, this.unit); + } + + public long toMicroseconds() { + return TimeUnit.MICROSECONDS.convert(this.count, this.unit); + } + + public long toMilliseconds() { + return TimeUnit.MILLISECONDS.convert(this.count, this.unit); + } + + public long toSeconds() { + return TimeUnit.SECONDS.convert(this.count, this.unit); + } + + public long toMinutes() { + return TimeUnit.MINUTES.convert(this.count, this.unit); + } + + public long toHours() { + return TimeUnit.HOURS.convert(this.count, this.unit); + } + + public long toDays() { + return TimeUnit.DAYS.convert(this.count, this.unit); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj != null && this.getClass() == obj.getClass()) { + Duration duration = (Duration)obj; + return this.count == duration.count && this.unit == duration.unit; + } else { + return false; + } + } + + public int hashCode() { + return 31 * (int)(this.count ^ this.count >>> 32) + this.unit.hashCode(); + } + + @JsonValue + public String toString() { + String units = this.unit.toString().toLowerCase(Locale.ENGLISH); + if (this.count == 1L) { + units = units.substring(0, units.length() - 1); + } + + return Long.toString(this.count) + ' ' + units; + } + + public int compareTo(Duration other) { + return this.unit == other.unit ? Long.compare(this.count, other.count) : Long.compare(this.toNanoseconds(), other.toNanoseconds()); + } + + static { + SUFFIXES = (new ImmutableMap.Builder()).put("ns", TimeUnit.NANOSECONDS).put("nanosecond", TimeUnit.NANOSECONDS).put("nanoseconds", TimeUnit.NANOSECONDS).put("us", TimeUnit.MICROSECONDS).put("microsecond", TimeUnit.MICROSECONDS).put("microseconds", TimeUnit.MICROSECONDS).put("ms", TimeUnit.MILLISECONDS).put("millisecond", TimeUnit.MILLISECONDS).put("milliseconds", TimeUnit.MILLISECONDS).put("s", TimeUnit.SECONDS).put("second", TimeUnit.SECONDS).put("seconds", TimeUnit.SECONDS).put("m", TimeUnit.MINUTES).put("minute", TimeUnit.MINUTES).put("minutes", TimeUnit.MINUTES).put("h", TimeUnit.HOURS).put("hour", TimeUnit.HOURS).put("hours", TimeUnit.HOURS).put("d", TimeUnit.DAYS).put("day", TimeUnit.DAYS).put("days", TimeUnit.DAYS).build(); + } +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedDataSource.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedDataSource.java new file mode 100644 index 00000000..de302f53 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedDataSource.java @@ -0,0 +1,7 @@ +package com.flipkart.gjex.db; + +import javax.sql.DataSource; + +public interface ManagedDataSource extends DataSource { + +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedPooledDataSource.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedPooledDataSource.java new file mode 100644 index 00000000..7fc781aa --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/ManagedPooledDataSource.java @@ -0,0 +1,22 @@ +package com.flipkart.gjex.db; + +import io.dropwizard.metrics5.MetricRegistry; +import org.apache.tomcat.jdbc.pool.DataSourceProxy; +import org.apache.tomcat.jdbc.pool.PoolConfiguration; + +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; + +public class ManagedPooledDataSource extends DataSourceProxy implements ManagedDataSource { + private final MetricRegistry metricRegistry; + + public ManagedPooledDataSource(PoolConfiguration config, MetricRegistry metricRegistry) { + super(config); + this.metricRegistry = metricRegistry; + } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException("Doesn't use java.util.logging"); + } + +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/PooledDataSourceFactory.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/PooledDataSourceFactory.java new file mode 100644 index 00000000..e3ee58f0 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/PooledDataSourceFactory.java @@ -0,0 +1,32 @@ +package com.flipkart.gjex.db; + +import io.dropwizard.metrics5.MetricRegistry; + +import java.util.Map; +import java.util.Optional; + +public interface PooledDataSourceFactory { + boolean isAutoCommentsEnabled(); + + Map getProperties(); + + Optional getValidationQueryTimeout(); + + /** @deprecated */ + @Deprecated + Optional getHealthCheckValidationTimeout(); + + String getValidationQuery(); + + /** @deprecated */ + @Deprecated + String getHealthCheckValidationQuery(); + + String getDriverClass(); + + String getUrl(); + + void asSingleConnectionPool(); + + ManagedDataSource build(MetricRegistry var1, String var2); +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/db/TimeBoundHealthCheck.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/TimeBoundHealthCheck.java new file mode 100644 index 00000000..5bcc3529 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/db/TimeBoundHealthCheck.java @@ -0,0 +1,28 @@ +package com.flipkart.gjex.db; + +import io.dropwizard.metrics5.health.HealthCheck; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +public class TimeBoundHealthCheck { + private final ExecutorService executorService; + private final Duration duration; + + public TimeBoundHealthCheck(ExecutorService executorService, Duration duration) { + this.executorService = executorService; + this.duration = duration; + } + + public HealthCheck.Result check(Callable c) { + HealthCheck.Result result; + try { + result = (HealthCheck.Result)this.executorService.submit(c).get(this.duration.getQuantity(), this.duration.getUnit()); + } catch (Exception var4) { + result = HealthCheck.Result.unhealthy("Unable to successfully check in %s", new Object[]{this.duration}); + } + + return result; + } +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/AbstractDAO.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/AbstractDAO.java new file mode 100644 index 00000000..72d38584 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/AbstractDAO.java @@ -0,0 +1,224 @@ +package com.flipkart.gjex.hibernate; + +import com.flipkart.gjex.core.Generics; +import org.hibernate.*; +import org.hibernate.query.Query; +import org.hibernate.query.internal.AbstractProducedQuery; + +import javax.persistence.criteria.CriteriaQuery; +import java.io.Serializable; +import java.util.List; + +import static java.util.Objects.requireNonNull; + + +public class AbstractDAO { + private final SessionFactory sessionFactory; + private final Class entityClass; + + /** + * Creates a new DAO with a given session provider. + * + * @param sessionFactory a session provider + */ + public AbstractDAO(SessionFactory sessionFactory) { + this.sessionFactory = requireNonNull(sessionFactory); + this.entityClass = Generics.getTypeParameter(getClass()); + } + + /** + * Returns the current {@link Session}. + * + * @return the current session + */ + protected Session currentSession() { + return sessionFactory.getCurrentSession(); + } + + /** + * Creates a new {@link Criteria} query for {@code }. + * + * @return a new {@link Criteria} query + * @see Session#createCriteria(Class) + * @deprecated Use {@link AbstractDAO#criteriaQuery()} instead. + */ + @Deprecated + protected Criteria criteria() { + return currentSession().createCriteria(entityClass); + } + + /** + * Creates a new {@link CriteriaQuery} for {@code }. + * + * @return a new {@link CriteriaQuery} query + */ + protected CriteriaQuery criteriaQuery() { + return this.currentSession().getCriteriaBuilder().createQuery(getEntityClass()); + } + + /** + * Returns a named {@link Query}. + * + * @param queryName the name of the query + * @return the named query + * @see Session#getNamedQuery(String) + */ + protected Query namedQuery(String queryName) throws HibernateException { + return currentSession().getNamedQuery(requireNonNull(queryName)); + } + + /** + * Returns a named and type-safe {@link Query}. + * + * @param queryName the name of the query + * @return the named query + * @see Session#createNamedQuery(String, Class) + * @since 2.0.22 + */ + protected Query namedTypedQuery(String queryName) throws HibernateException { + return currentSession().createNamedQuery(queryName, getEntityClass()); + } + + /** + * Returns a typed {@link Query} + * + * @param queryString HQL query + * @return typed query + */ + protected Query query(String queryString) { + return currentSession().createQuery(requireNonNull(queryString), getEntityClass()); + } + + /** + * Returns the entity class managed by this DAO. + * + * @return the entity class managed by this DAO + */ + @SuppressWarnings("unchecked") + public Class getEntityClass() { + return (Class) entityClass; + } + + /** + * Convenience method to return a single instance that matches the criteria query, + * or null if the criteria returns no results. + * + * @param criteriaQuery the {@link CriteriaQuery} query to run + * @return the single result or {@code null} + * @throws HibernateException if there is more than one matching result + */ + protected E uniqueResult(CriteriaQuery criteriaQuery) throws HibernateException { + return AbstractProducedQuery.uniqueElement( + currentSession() + .createQuery(requireNonNull(criteriaQuery)) + .getResultList() + ); + } + + /** + * Convenience method to return a single instance that matches the criteria, or null if the + * criteria returns no results. + * + * @param criteria the {@link Criteria} query to run + * @return the single result or {@code null} + * @throws HibernateException if there is more than one matching result + * @see Criteria#uniqueResult() + */ + @SuppressWarnings("unchecked") + protected E uniqueResult(Criteria criteria) throws HibernateException { + return (E) requireNonNull(criteria).uniqueResult(); + } + + /** + * Convenience method to return a single instance that matches the query, or null if the query + * returns no results. + * + * @param query the query to run + * @return the single result or {@code null} + * @throws HibernateException if there is more than one matching result + * @see Query#uniqueResult() + */ + protected E uniqueResult(Query query) throws HibernateException { + return requireNonNull(query).uniqueResult(); + } + + /** + * Get the results of a {@link Criteria} query. + * + * @param criteria the {@link Criteria} query to run + * @return the list of matched query results + * @see Criteria#list() + */ + @SuppressWarnings("unchecked") + protected List list(Criteria criteria) throws HibernateException { + return requireNonNull(criteria).list(); + } + + /** + * Get the results of a {@link CriteriaQuery} query. + * + * @param criteria the {@link CriteriaQuery} query to run + * @return the list of matched query results + */ + protected List list(CriteriaQuery criteria) throws HibernateException { + return currentSession().createQuery(requireNonNull(criteria)).getResultList(); + } + + /** + * Get the results of a query. + * + * @param query the query to run + * @return the list of matched query results + * @see Query#list() + */ + protected List list(Query query) throws HibernateException { + return requireNonNull(query).list(); + } + + /** + * Return the persistent instance of {@code } with the given identifier, or {@code null} if + * there is no such persistent instance. (If the instance, or a proxy for the instance, is + * already associated with the session, return that instance or proxy.) + * + * @param id an identifier + * @return a persistent instance or {@code null} + * @throws HibernateException + * @see Session#get(Class, Serializable) + */ + @SuppressWarnings("unchecked") + protected E get(Serializable id) { + return (E) currentSession().get(entityClass, requireNonNull(id)); + } + + /** + * Either save or update the given instance, depending upon resolution of the unsaved-value + * checks (see the manual for discussion of unsaved-value checking). + *

+ * This operation cascades to associated instances if the association is mapped with + * cascade="save-update". + * + * @param entity a transient or detached instance containing new or updated state + * @throws HibernateException + * @see Session#saveOrUpdate(Object) + */ + protected E persist(E entity) throws HibernateException { + currentSession().saveOrUpdate(requireNonNull(entity)); + return entity; + } + + /** + * Force initialization of a proxy or persistent collection. + *

+ * Note: This only ensures initialization of a proxy object or collection; + * it is not guaranteed that the elements INSIDE the collection will be initialized/materialized. + * + * @param proxy a persistable object, proxy, persistent collection or {@code null} + * @throws HibernateException if we can't initialize the proxy at this time, eg. the {@link Session} was closed + */ + protected T initialize(T proxy) throws HibernateException { + if (!Hibernate.isInitialized(proxy)) { + Hibernate.initialize(proxy); + } + return proxy; + } +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/HibernateBundle.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/HibernateBundle.java new file mode 100644 index 00000000..56e66dee --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/HibernateBundle.java @@ -0,0 +1,101 @@ +package com.flipkart.gjex.hibernate; + +import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import com.flipkart.gjex.core.Bundle; +import com.flipkart.gjex.core.GJEXConfiguration; +import com.flipkart.gjex.core.service.Service; +import com.flipkart.gjex.core.setup.Bootstrap; +import com.flipkart.gjex.core.setup.Environment; +import com.flipkart.gjex.core.tracing.TracingSampler; +import com.flipkart.gjex.db.DatabaseConfiguration; +import com.flipkart.gjex.db.PooledDataSourceFactory; +import com.google.common.collect.ImmutableList; +import io.dropwizard.metrics5.health.HealthCheck; +import org.glassfish.jersey.server.ResourceConfig; +import org.hibernate.SessionFactory; + +import java.util.*; + +public abstract class HibernateBundle implements Bundle, DatabaseConfiguration { + public static final String DEFAULT_NAME = "hibernate"; + private final List> entities; + private final SessionFactoryFactory sessionFactoryFactory; + private SessionFactory sessionFactory; + private boolean lazyLoadingEnabled; + + protected HibernateBundle(Class entity, Class... entities) { + final List> entityClasses = new ArrayList<>(); + entityClasses.add(entity); + entityClasses.addAll(Arrays.asList(entities)); + + this.entities = Collections.unmodifiableList(entityClasses); + this.sessionFactoryFactory = new SessionFactoryFactory(); + } + + protected HibernateBundle(ImmutableList> entities, SessionFactoryFactory sessionFactoryFactory) { + this.lazyLoadingEnabled = true; + this.entities = entities; + this.sessionFactoryFactory = sessionFactoryFactory; + } + + public final void initialize(Bootstrap bootstrap) { + bootstrap.getObjectMapper().registerModule(this.createHibernate5Module()); + PooledDataSourceFactory dbConfig = this.getDataSourceFactory(bootstrap.getConfiguration()); + this.sessionFactory = this.sessionFactoryFactory.build(this, bootstrap.getMetricRegistry(), dbConfig, this.entities, this.name()); + } + + protected Hibernate5Module createHibernate5Module() { + Hibernate5Module module = new Hibernate5Module(); + if (this.lazyLoadingEnabled) { + module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING); + } + + return module; + } + + protected String name() { + return "hibernate"; + } + + + @Override + public void run(T t, U u, Environment environment) { + + } + + public boolean isLazyLoadingEnabled() { + return this.lazyLoadingEnabled; + } + + public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) { + this.lazyLoadingEnabled = lazyLoadingEnabled; + } + + public SessionFactory getSessionFactory() { + return this.sessionFactory; + } + + protected void configure(org.hibernate.cfg.Configuration configuration) { + + } + + @Override + public List getServices() { + return Collections.EMPTY_LIST; + } + + @Override + public List getHealthChecks() { + return Collections.EMPTY_LIST; + } + + @Override + public List getTracingSamplers() { + return Collections.EMPTY_LIST; + } + + @Override + public List getResourceConfigs() { + return Collections.EMPTY_LIST; + } +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/ScanningHibernateBundle.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/ScanningHibernateBundle.java new file mode 100644 index 00000000..f1f55d10 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/ScanningHibernateBundle.java @@ -0,0 +1,74 @@ +package com.flipkart.gjex.hibernate; + +import com.flipkart.gjex.core.GJEXConfiguration; +import com.google.common.collect.ImmutableList; +import org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener; +import org.glassfish.jersey.server.internal.scanning.PackageNamesScanner; + +import javax.persistence.Entity; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; + +public abstract class ScanningHibernateBundle extends HibernateBundle { + + protected ScanningHibernateBundle(String pckg) { + this(pckg, new SessionFactoryFactory()); + } + + protected ScanningHibernateBundle(String pckg, SessionFactoryFactory sessionFactoryFactory) { + this(new String[]{pckg}, sessionFactoryFactory); + } + + protected ScanningHibernateBundle(String[] pckgs, SessionFactoryFactory sessionFactoryFactory) { + super(findEntityClassesFromDirectory(pckgs), sessionFactoryFactory); + } + + public static ImmutableList> findEntityClassesFromDirectory(String[] pckgs) { + AnnotationAcceptingListener asl = new AnnotationAcceptingListener(new Class[]{Entity.class}); + PackageNamesScanner scanner = new PackageNamesScanner(pckgs, true); + + while (scanner.hasNext()) { + String next = scanner.next(); + if (asl.accept(next)) { + try { + InputStream in = scanner.open(); + Throwable var5 = null; + + try { + asl.process(next, in); + } catch (Throwable var15) { + var5 = var15; + throw var15; + } finally { + if (in != null) { + if (var5 != null) { + try { + in.close(); + } catch (Throwable var14) { + var5.addSuppressed(var14); + } + } else { + in.close(); + } + } + + } + } catch (IOException var17) { + throw new RuntimeException("AnnotationAcceptingListener failed to process scanned resource: " + next); + } + } + } + + ImmutableList.Builder> builder = ImmutableList.builder(); + Iterator var19 = asl.getAnnotatedClasses().iterator(); + + while (var19.hasNext()) { + Class clazz = (Class) var19.next(); + builder.add(clazz); + } + + return builder.build(); + } +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryFactory.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryFactory.java new file mode 100644 index 00000000..757952c0 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryFactory.java @@ -0,0 +1,88 @@ +package com.flipkart.gjex.hibernate; + +import io.dropwizard.metrics5.MetricRegistry; +import com.flipkart.gjex.db.ManagedDataSource; +import com.flipkart.gjex.db.PooledDataSourceFactory; +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.ServiceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.util.*; + +public class SessionFactoryFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionFactoryFactory.class); + private static final String DEFAULT_NAME = "hibernate"; + + public SessionFactoryFactory() { + } + + public SessionFactory build(HibernateBundle bundle, MetricRegistry metricRegistry, PooledDataSourceFactory dbConfig, List> entities) { + return this.build(bundle, metricRegistry, dbConfig, entities, "hibernate"); + } + + public SessionFactory build(HibernateBundle bundle, MetricRegistry metricRegistry, PooledDataSourceFactory dbConfig, List> entities, String name) { + ManagedDataSource dataSource = dbConfig.build(metricRegistry, name); + return this.build(bundle, metricRegistry, dbConfig, dataSource, entities); + } + + public SessionFactory build(HibernateBundle bundle, MetricRegistry metricRegistry, PooledDataSourceFactory dbConfig, ManagedDataSource dataSource, List> entities) { + ConnectionProvider provider = this.buildConnectionProvider(dataSource, dbConfig.getProperties()); + SessionFactory factory = this.buildSessionFactory(bundle, dbConfig, provider, dbConfig.getProperties(), entities); + return factory; + } + + private ConnectionProvider buildConnectionProvider(DataSource dataSource, Map properties) { + DatasourceConnectionProviderImpl connectionProvider = new DatasourceConnectionProviderImpl(); + connectionProvider.setDataSource(dataSource); + connectionProvider.configure(properties); + return connectionProvider; + } + + private SessionFactory buildSessionFactory(HibernateBundle bundle, PooledDataSourceFactory dbConfig, ConnectionProvider connectionProvider, Map properties, List> entities) { + Configuration configuration = new Configuration(); + configuration.setProperty("hibernate.current_session_context_class", "managed"); + configuration.setProperty("hibernate.use_sql_comments", Boolean.toString(dbConfig.isAutoCommentsEnabled())); + configuration.setProperty("hibernate.jdbc.use_get_generated_keys", "true"); + configuration.setProperty("hibernate.generate_statistics", "true"); + configuration.setProperty("hibernate.bytecode.use_reflection_optimizer", "true"); + configuration.setProperty("hibernate.order_updates", "true"); + configuration.setProperty("hibernate.order_inserts", "true"); + configuration.setProperty("hibernate.id.new_generator_mappings", "true"); + configuration.setProperty("jadira.usertype.autoRegisterUserTypes", "true"); + Iterator var7 = properties.entrySet().iterator(); + + while (var7.hasNext()) { + Map.Entry property = (Map.Entry) var7.next(); + configuration.setProperty((String) property.getKey(), (String) property.getValue()); + } + + this.addAnnotatedClasses(configuration, entities); + bundle.configure(configuration); + ServiceRegistry registry = (new StandardServiceRegistryBuilder()).addService(ConnectionProvider.class, connectionProvider).applySettings(configuration.getProperties()).build(); + this.configure(configuration, registry); + return configuration.buildSessionFactory(registry); + } + + protected void configure(Configuration configuration, ServiceRegistry registry) { + } + + private void addAnnotatedClasses(Configuration configuration, Iterable> entities) { + SortedSet entityClasses = new TreeSet(); + Iterator var4 = entities.iterator(); + + while (var4.hasNext()) { + Class klass = (Class) var4.next(); + configuration.addAnnotatedClass(klass); + entityClasses.add(klass.getCanonicalName()); + } + + LOGGER.info("Entity classes: {}", entityClasses); + } +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryHealthCheck.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryHealthCheck.java new file mode 100644 index 00000000..a0ecb8b5 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/SessionFactoryHealthCheck.java @@ -0,0 +1,75 @@ +package com.flipkart.gjex.hibernate; + +import com.flipkart.gjex.db.Duration; +import com.flipkart.gjex.db.TimeBoundHealthCheck; +import com.google.common.util.concurrent.MoreExecutors; +import io.dropwizard.metrics5.health.HealthCheck; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; + +import java.util.concurrent.ExecutorService; + +public class SessionFactoryHealthCheck extends HealthCheck { + private final SessionFactory sessionFactory; + private final String validationQuery; + private final TimeBoundHealthCheck timeBoundHealthCheck; + + public SessionFactoryHealthCheck(SessionFactory sessionFactory, String validationQuery) { + this(MoreExecutors.newDirectExecutorService(), Duration.seconds(0L), sessionFactory, validationQuery); + } + + public SessionFactoryHealthCheck(ExecutorService executorService, Duration duration, SessionFactory sessionFactory, String validationQuery) { + this.sessionFactory = sessionFactory; + this.validationQuery = validationQuery; + this.timeBoundHealthCheck = new TimeBoundHealthCheck(executorService, duration); + } + + public SessionFactory getSessionFactory() { + return this.sessionFactory; + } + + public String getValidationQuery() { + return this.validationQuery; + } + + protected Result check() throws Exception { + return this.timeBoundHealthCheck.check(() -> { + Session session = this.sessionFactory.openSession(); + Throwable var2 = null; + + try { + Transaction txn = session.beginTransaction(); + + try { + session.createSQLQuery(this.validationQuery).list(); + txn.commit(); + } catch (Exception var13) { + if (txn.getStatus().canRollback()) { + txn.rollback(); + } + + throw var13; + } + } catch (Throwable var14) { + var2 = var14; + throw var14; + } finally { + if (session != null) { + if (var2 != null) { + try { + session.close(); + } catch (Throwable var12) { + var2.addSuppressed(var12); + } + } else { + session.close(); + } + } + + } + + return Result.healthy(); + }); + } +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWork.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWork.java new file mode 100644 index 00000000..63ea4ec7 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWork.java @@ -0,0 +1,22 @@ +package com.flipkart.gjex.hibernate; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface UnitOfWork { + boolean readOnly() default false; + + boolean transactional() default true; + + CacheMode cacheMode() default CacheMode.NORMAL; + + FlushMode flushMode() default FlushMode.AUTO; + + String value() default "hibernate"; +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkApplicationListener.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkApplicationListener.java new file mode 100644 index 00000000..e22e953f --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkApplicationListener.java @@ -0,0 +1,107 @@ +package com.flipkart.gjex.hibernate; + +import org.glassfish.jersey.server.internal.process.MappableException; +import org.glassfish.jersey.server.model.Resource; +import org.glassfish.jersey.server.model.ResourceMethod; +import org.glassfish.jersey.server.monitoring.ApplicationEvent; +import org.glassfish.jersey.server.monitoring.ApplicationEventListener; +import org.glassfish.jersey.server.monitoring.RequestEvent; +import org.glassfish.jersey.server.monitoring.RequestEventListener; +import org.hibernate.SessionFactory; + +import javax.ws.rs.ext.Provider; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +@Provider +public class UnitOfWorkApplicationListener implements ApplicationEventListener { + private Map methodMap = new HashMap(); + private Map sessionFactories = new HashMap(); + + public UnitOfWorkApplicationListener() { + } + + public UnitOfWorkApplicationListener(String name, SessionFactory sessionFactory) { + this.registerSessionFactory(name, sessionFactory); + } + + public void registerSessionFactory(String name, SessionFactory sessionFactory) { + this.sessionFactories.put(name, sessionFactory); + } + + public void onEvent(ApplicationEvent event) { + if (event.getType() == ApplicationEvent.Type.INITIALIZATION_APP_FINISHED) { + Iterator var2 = event.getResourceModel().getResources().iterator(); + + while (var2.hasNext()) { + Resource resource = (Resource) var2.next(); + Iterator var4 = resource.getAllMethods().iterator(); + + while (var4.hasNext()) { + ResourceMethod method = (ResourceMethod) var4.next(); + this.registerUnitOfWorkAnnotations(method); + } + + var4 = resource.getChildResources().iterator(); + + while (var4.hasNext()) { + Resource childResource = (Resource) var4.next(); + Iterator var6 = childResource.getAllMethods().iterator(); + + while (var6.hasNext()) { + ResourceMethod method = (ResourceMethod) var6.next(); + this.registerUnitOfWorkAnnotations(method); + } + } + } + } + + } + + public RequestEventListener onRequest(RequestEvent event) { + return new UnitOfWorkEventListener(this.methodMap, this.sessionFactories); + } + + private void registerUnitOfWorkAnnotations(ResourceMethod method) { + UnitOfWork annotation = (UnitOfWork) method.getInvocable().getDefinitionMethod().getAnnotation(UnitOfWork.class); + if (annotation == null) { + annotation = (UnitOfWork) method.getInvocable().getHandlingMethod().getAnnotation(UnitOfWork.class); + } + + if (annotation != null) { + this.methodMap.put(method.getInvocable().getDefinitionMethod(), annotation); + } + + } + + private static class UnitOfWorkEventListener implements RequestEventListener { + private final Map methodMap; + private final UnitOfWorkAspect unitOfWorkAspect; + + UnitOfWorkEventListener(Map methodMap, Map sessionFactories) { + this.methodMap = methodMap; + this.unitOfWorkAspect = new UnitOfWorkAspect(sessionFactories); + } + + public void onEvent(RequestEvent event) { + RequestEvent.Type eventType = event.getType(); + if (eventType == RequestEvent.Type.RESOURCE_METHOD_START) { + UnitOfWork unitOfWork = (UnitOfWork) this.methodMap.get(event.getUriInfo().getMatchedResourceMethod().getInvocable().getDefinitionMethod()); + this.unitOfWorkAspect.beforeStart(unitOfWork); + } else if (eventType == RequestEvent.Type.RESP_FILTERS_START) { + try { + this.unitOfWorkAspect.afterEnd(); + } catch (Exception var4) { + throw new MappableException(var4); + } + } else if (eventType == RequestEvent.Type.ON_EXCEPTION) { + this.unitOfWorkAspect.onError(); + } else if (eventType == RequestEvent.Type.FINISHED) { + this.unitOfWorkAspect.onFinish(); + } + + } + } +} diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkAspect.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkAspect.java new file mode 100644 index 00000000..e3b1c30d --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkAspect.java @@ -0,0 +1,113 @@ +package com.flipkart.gjex.hibernate; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.context.internal.ManagedSessionContext; + +import java.util.Map; + +class UnitOfWorkAspect { + private final Map sessionFactories; + private UnitOfWork unitOfWork; + private Session session; + private SessionFactory sessionFactory; + + public UnitOfWorkAspect(Map sessionFactories) { + this.sessionFactories = sessionFactories; + } + + public void beforeStart(UnitOfWork unitOfWork) { + if (unitOfWork != null) { + this.unitOfWork = unitOfWork; + this.sessionFactory = (SessionFactory) this.sessionFactories.get(unitOfWork.value()); + if (this.sessionFactory == null) { + if (!unitOfWork.value().equals("hibernate") || this.sessionFactories.size() != 1) { + throw new IllegalArgumentException("Unregistered Hibernate bundle: '" + unitOfWork.value() + "'"); + } + + this.sessionFactory = (SessionFactory) this.sessionFactories.values().iterator().next(); + } + + this.session = this.sessionFactory.openSession(); + + try { + this.configureSession(); + ManagedSessionContext.bind(this.session); + this.beginTransaction(); + } catch (Throwable var3) { + this.session.close(); + this.session = null; + ManagedSessionContext.unbind(this.sessionFactory); + throw var3; + } + } + } + + public void afterEnd() { + if (this.session != null) { + try { + this.commitTransaction(); + } catch (Exception var2) { + this.rollbackTransaction(); + throw var2; + } + } + } + + public void onError() { + if (this.session != null) { + try { + this.rollbackTransaction(); + } finally { + this.onFinish(); + } + + } + } + + public void onFinish() { + try { + if (this.session != null) { + this.session.close(); + } + } finally { + this.session = null; + ManagedSessionContext.unbind(this.sessionFactory); + } + + } + + private void configureSession() { + this.session.setDefaultReadOnly(this.unitOfWork.readOnly()); + this.session.setCacheMode(this.unitOfWork.cacheMode()); + this.session.setFlushMode(this.unitOfWork.flushMode()); + } + + private void beginTransaction() { + if (this.unitOfWork.transactional()) { + this.session.beginTransaction(); + } + } + + private void rollbackTransaction() { + if (this.unitOfWork.transactional()) { + Transaction txn = this.session.getTransaction(); + if (txn != null && txn.getStatus().canRollback()) { + txn.rollback(); + } + + } + } + + private void commitTransaction() { + if (this.unitOfWork.transactional()) { + Transaction txn = this.session.getTransaction(); + if (txn != null && txn.getStatus().canRollback()) { + txn.commit(); + } + + } + } +} + diff --git a/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkInterceptor.java b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkInterceptor.java new file mode 100644 index 00000000..1c3db713 --- /dev/null +++ b/contrib/hibernate/src/main/java/com/flipkart/gjex/hibernate/UnitOfWorkInterceptor.java @@ -0,0 +1,122 @@ +package com.flipkart.gjex.hibernate; + + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.glassfish.jersey.server.internal.process.MappableException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.context.internal.ManagedSessionContext; +import org.hibernate.resource.transaction.spi.TransactionStatus; + +import javax.inject.Inject; + +public class UnitOfWorkInterceptor implements MethodInterceptor { + + @Inject + private SessionFactory sessionFactory; + + public Object invoke(MethodInvocation arg0) throws Throwable { + Session session = null; + UnitOfWork aUnitOfWork = arg0.getMethod().getAnnotation(UnitOfWork.class); + if (!ManagedSessionContext.hasBind(this.sessionFactory)) { + session = this.openSession(aUnitOfWork); + } + + try { + Object response = arg0.proceed(); + if (session != null) { + this.closeSession(session, aUnitOfWork); + } + + return response; + } catch (Throwable var5) { + if (session != null) { + this.rollbackSession(session, aUnitOfWork); + } + + throw var5; + } + } + + private Session openSession(UnitOfWork aUnitOfWork) throws Throwable { + Session session = this.sessionFactory.openSession(); + + try { + this.configureSession(session, aUnitOfWork); + ManagedSessionContext.bind(session); + this.beginTransaction(session, aUnitOfWork); + return session; + } catch (Throwable var4) { + if (session != null && session.isOpen()) { + session.close(); + } + + ManagedSessionContext.unbind(this.sessionFactory); + throw var4; + } + } + + private void beginTransaction(Session session, UnitOfWork aUnitOfWork) { + if (aUnitOfWork.transactional()) { + session.beginTransaction(); + } + + } + + private void configureSession(Session session, UnitOfWork aUnitOfWork) { + session.setDefaultReadOnly(aUnitOfWork.readOnly()); + session.setCacheMode(aUnitOfWork.cacheMode()); + session.setFlushMode(aUnitOfWork.flushMode()); + } + + private void closeSession(Session session, UnitOfWork aUnitOfWork) { + try { + this.commitTransaction(session, aUnitOfWork); + } catch (Exception var7) { + this.rollbackTransaction(session, aUnitOfWork); + throw new MappableException(var7); + } finally { + if (session != null && session.isOpen()) { + session.close(); + } + + ManagedSessionContext.unbind(this.sessionFactory); + } + + } + + private void commitTransaction(Session session, UnitOfWork aUnitOfWork) { + if (aUnitOfWork.transactional()) { + Transaction txn = session.getTransaction(); + if (txn != null && txn.getStatus().equals(TransactionStatus.ACTIVE)) { + txn.commit(); + } + } + + } + + private void rollbackSession(Session session, UnitOfWork aUnitOfWork) { + try { + this.rollbackTransaction(session, aUnitOfWork); + } finally { + if (session != null && session.isOpen()) { + session.close(); + } + + ManagedSessionContext.unbind(this.sessionFactory); + } + + } + + private void rollbackTransaction(Session session, UnitOfWork aUnitOfWork) { + if (aUnitOfWork.transactional() && session != null && session.isOpen()) { + Transaction txn = session.getTransaction(); + if (txn != null && txn.getStatus().equals(TransactionStatus.ACTIVE)) { + txn.rollback(); + } + } + + } +} diff --git a/settings.gradle b/settings.gradle index 1345537a..cdf3f8e0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,3 +4,4 @@ include "core" include "guice" include "runtime" include "examples" +include "contrib:hibernate" From 028bf474d15dbcf25397598d2c18438c80869b36 Mon Sep 17 00:00:00 2001 From: Mohit Date: Mon, 4 Nov 2024 08:01:45 +0530 Subject: [PATCH 2/2] Example for Hibernate --- examples/build.gradle | 9 ++-- .../helloworld/HelloWorldApplication.java | 41 ++++++++++++++++- .../config/HelloWorldConfiguration.java | 6 +++ .../examples/helloworld/dao/DummyDAO.java | 24 ++++++++++ .../helloworld/entity/DummyEntity.java | 20 +++++++++ .../helloworld/guice/HelloWorldModule.java | 26 ++++++++++- .../web/ExampleHibernateResource.java | 44 +++++++++++++++++++ .../web/HelloWorldResourceConfig.java | 3 +- .../src/main/resources/hello_world_config.yml | 14 ++++++ 9 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 examples/src/main/java/com/flipkart/gjex/examples/helloworld/dao/DummyDAO.java create mode 100644 examples/src/main/java/com/flipkart/gjex/examples/helloworld/entity/DummyEntity.java create mode 100644 examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/ExampleHibernateResource.java diff --git a/examples/build.gradle b/examples/build.gradle index 513f6fb6..1b6d2216 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -28,11 +28,13 @@ dependencies { implementation project(':core') implementation project(':runtime') implementation project(':guice') + implementation project(':contrib:hibernate') // In actual - // implementation "com.flipkart.grpc-jexpress:core:${jexpressVersion}" - // implementation "com.flipkart.grpc-jexpress:runtime:${jexpressVersion}" - // implementation "com.flipkart.grpc-jexpress:guice:${jexpressVersion}" +// implementation "com.flipkart.grpc-jexpress:core:${jexpressVersion}" +// implementation "com.flipkart.grpc-jexpress:runtime:${jexpressVersion}" +// implementation "com.flipkart.grpc-jexpress:guice:${jexpressVersion}" +// implementation "com.flipkart.grpc-jexpress:hibernate:${jexpressVersion}" implementation "io.grpc:grpc-netty-shaded:${grpcVersion}" implementation "io.grpc:grpc-protobuf:${grpcVersion}" @@ -52,6 +54,7 @@ dependencies { implementation 'org.glassfish.jersey.containers:jersey-container-servlet:2.6' implementation 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.9.7' implementation 'javax.servlet:javax.servlet-api:3.1.0' + implementation 'org.hibernate:hibernate-core:5.6.15.Final' } protobuf { diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/HelloWorldApplication.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/HelloWorldApplication.java index 2e6ead1a..92e22bf5 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/HelloWorldApplication.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/HelloWorldApplication.java @@ -16,12 +16,20 @@ package com.flipkart.gjex.examples.helloworld; import com.flipkart.gjex.core.Application; +import com.flipkart.gjex.core.GJEXConfiguration; +import com.flipkart.gjex.core.filter.grpc.GrpcFilter; +import com.flipkart.gjex.core.filter.http.HttpFilter; +import com.flipkart.gjex.core.job.ScheduledJob; import com.flipkart.gjex.core.setup.Bootstrap; import com.flipkart.gjex.core.setup.Environment; +import com.flipkart.gjex.db.PooledDataSourceFactory; import com.flipkart.gjex.examples.helloworld.config.HelloWorldConfiguration; import com.flipkart.gjex.examples.helloworld.guice.HelloWorldModule; import com.flipkart.gjex.guice.GuiceBundle; +import com.flipkart.gjex.hibernate.ScanningHibernateBundle; +import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -39,9 +47,40 @@ public String getName() { @Override public void initialize(Bootstrap bootstrap) { + + ScanningHibernateBundle hibernateBundle = + new ScanningHibernateBundle("entity") { + @Override + public PooledDataSourceFactory getDataSourceFactory(GJEXConfiguration var1) { + HelloWorldConfiguration configuration = (HelloWorldConfiguration) var1; + return configuration.getDataSourceFactory(); + } + + @Override + public List getGrpcFilters() { + return Collections.emptyList(); + } + + @Override + public List getHTTPFilters() { + return Collections.emptyList(); + } + + @Override + public List getHealthChecks() { + return Collections.emptyList(); + } + + @Override + public List getScheduledJobs() { + return Collections.emptyList(); + } + }; + + bootstrap.addBundle(hibernateBundle); GuiceBundle guiceBundle = new GuiceBundle.Builder() .setConfigClass(HelloWorldConfiguration.class) - .addModules(new HelloWorldModule()) + .addModules(new HelloWorldModule(hibernateBundle.getSessionFactory())) .build(); bootstrap.addBundle(guiceBundle); } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/config/HelloWorldConfiguration.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/config/HelloWorldConfiguration.java index 183da399..3853d0b3 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/config/HelloWorldConfiguration.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/config/HelloWorldConfiguration.java @@ -6,9 +6,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.flipkart.gjex.core.GJEXConfiguration; +import com.flipkart.gjex.db.DataSourceFactory; import lombok.Data; import lombok.EqualsAndHashCode; +import javax.validation.Valid; + @Data @EqualsAndHashCode(callSuper=true) @@ -21,4 +24,7 @@ public class HelloWorldConfiguration extends GJEXConfiguration { @JsonProperty("task.properties") private Map taskProperties; + @Valid + @JsonProperty("database") + private DataSourceFactory dataSourceFactory; } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/dao/DummyDAO.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/dao/DummyDAO.java new file mode 100644 index 00000000..ab6b4db7 --- /dev/null +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/dao/DummyDAO.java @@ -0,0 +1,24 @@ +package com.flipkart.gjex.examples.helloworld.dao; + +import com.flipkart.gjex.examples.helloworld.entity.DummyEntity; +import com.flipkart.gjex.hibernate.AbstractDAO; +import org.hibernate.SessionFactory; + +import javax.inject.Inject; + + +public class DummyDAO extends AbstractDAO { + + @Inject + public DummyDAO(SessionFactory sessionFactory) { + super(sessionFactory); + } + + public DummyEntity findById(Long id) { + return get(id); + } + + public DummyEntity create(DummyEntity person) { + return persist(person); + } +} diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/entity/DummyEntity.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/entity/DummyEntity.java new file mode 100644 index 00000000..904687ca --- /dev/null +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/entity/DummyEntity.java @@ -0,0 +1,20 @@ +package com.flipkart.gjex.examples.helloworld.entity; + +import com.google.type.DateTime; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "dummyEntity") +public class DummyEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private DateTime dateTime; + private String status; +} diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java index 546818dc..84c5fc7e 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java @@ -26,13 +26,22 @@ import com.flipkart.gjex.examples.helloworld.tracing.AllWhitelistTracingSampler; import com.flipkart.gjex.examples.helloworld.web.HelloWorldResourceConfig; import com.flipkart.gjex.examples.helloworld.web.javaxfilter.ExampleJavaxFilter; +import com.flipkart.gjex.hibernate.UnitOfWork; +import com.flipkart.gjex.hibernate.UnitOfWorkInterceptor; import com.google.inject.AbstractModule; +import com.google.inject.Provides; import com.google.inject.name.Names; import io.grpc.BindableService; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.examples.helloworld.GreeterGrpc; import org.glassfish.jersey.server.ResourceConfig; +import org.hibernate.SessionFactory; + +import javax.inject.Singleton; + +import static com.google.inject.matcher.Matchers.annotatedWith; +import static com.google.inject.matcher.Matchers.any; /** * Guice module for wiring sample Service to GJEX runtime @@ -41,7 +50,10 @@ */ public class HelloWorldModule extends AbstractModule { - public HelloWorldModule() {} + private final SessionFactory sessionFactory; + public HelloWorldModule(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } @Override protected void configure() { @@ -57,5 +69,15 @@ protected void configure() { bind(ResourceConfig.class).annotatedWith(Names.named("HelloWorldResourceConfig")).to(HelloWorldResourceConfig.class); bind(JavaxFilterParams.class).annotatedWith(Names.named("ExampleJavaxFilter")).toInstance(JavaxFilterParams.builder().filter(new ExampleJavaxFilter()).pathSpec("/*").build()); bind(HttpFilterParams.class).annotatedWith(Names.named("CustomHeaderHttpFilter")).toInstance(HttpFilterParams.builder().filter(new CustomHeaderHttpFilter()).pathSpec("/*").build()); - } + + UnitOfWorkInterceptor unitOfWorkInterceptor = new UnitOfWorkInterceptor(); + requestInjection(unitOfWorkInterceptor); + bindInterceptor(any(), annotatedWith(UnitOfWork.class), unitOfWorkInterceptor); + } + + @Provides + @Singleton + public SessionFactory getSessionFactory() { + return sessionFactory; + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/ExampleHibernateResource.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/ExampleHibernateResource.java new file mode 100644 index 00000000..8b716bcb --- /dev/null +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/ExampleHibernateResource.java @@ -0,0 +1,44 @@ +package com.flipkart.gjex.examples.helloworld.web; + +import com.flipkart.gjex.examples.helloworld.dao.DummyDAO; +import com.flipkart.gjex.examples.helloworld.entity.DummyEntity; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; +import java.util.Optional; + +@Singleton +@Path("/hibernate/example") +@Named +public class ExampleHibernateResource { + + private final DummyDAO dummyDAO; + + @Inject + public ExampleHibernateResource(DummyDAO dummyDAO) { + this.dummyDAO = dummyDAO; + } + + @POST + public Response createPerson(DummyEntity person) { + DummyEntity createdPerson = dummyDAO.create(person); + return Response.status(Response.Status.CREATED).entity(createdPerson).build(); + } + + @GET + @Path("/{id}") + public Response getPersonById(@PathParam("id") Long id) { + Optional person = Optional.ofNullable(dummyDAO.findById(id)); + if (person.isPresent()) { + return Response.ok(person.get()).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).build(); + } + } +} diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java index 02514bbf..4388c9e4 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java @@ -32,9 +32,10 @@ public class HelloWorldResourceConfig extends ResourceConfig { @Inject public HelloWorldResourceConfig (HelloWorldResource1 helloWorldresource1, - HelloWorldResource2 helloWorldresource2) { + HelloWorldResource2 helloWorldresource2, ExampleHibernateResource hibernateResource) { register(helloWorldresource1); register(helloWorldresource2); + register(hibernateResource); } } diff --git a/examples/src/main/resources/hello_world_config.yml b/examples/src/main/resources/hello_world_config.yml index 026d20c7..b91f2db3 100644 --- a/examples/src/main/resources/hello_world_config.yml +++ b/examples/src/main/resources/hello_world_config.yml @@ -37,3 +37,17 @@ apiProperties: taskProperties: hello.timeout: 200 + +database: + driverClass: com.mysql.jdbc.Driver + user: new_user + password: new_password + url: jdbc:mysql://127.0.0.1:3306/overlord?autoReconnect=true&useSSL=false + properties: + charSet: UTF-8 + hibernate.dialect: org.hibernate.dialect.MySQL5Dialect + hibernate.show_sql: true + hibernate.current_session_context_class: managed + checkConnectionWhileIdle: true + checkConnectionOnReturn: true + checkConnectionOnBorrow: true