-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from RADAR-base/release-0.3.0
Release 0.3.0
- Loading branch information
Showing
58 changed files
with
1,899 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
kotlinVersion=1.4.10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
plugins { | ||
id 'org.jetbrains.kotlin.jvm' | ||
} | ||
|
||
description = 'Library for Jersey with Hibernate with the RADAR platform' | ||
|
||
repositories { | ||
jcenter() | ||
maven { url = "https://dl.bintray.com/radar-cns/org.radarcns" } | ||
} | ||
|
||
ext.extra = [ | ||
"hibernateVersion": "5.4.20.Final", | ||
"liquibaseVersion": "3.10.2", | ||
"postgresVersion": "42.2.16", | ||
"h2Version": "1.4.200", | ||
] | ||
|
||
dependencies { | ||
api(project(":")) | ||
api("org.hibernate:hibernate-core:${project.extra["hibernateVersion"]}") | ||
runtimeOnly("org.hibernate:hibernate-c3p0:${project.extra["hibernateVersion"]}") | ||
implementation("org.liquibase:liquibase-core:${project.extra["liquibaseVersion"]}") | ||
|
||
runtimeOnly("org.postgresql:postgresql:${project.extra["postgresVersion"]}") | ||
|
||
testRuntimeOnly("org.glassfish.grizzly:grizzly-http-server:$grizzlyVersion") | ||
testRuntimeOnly("org.glassfish.jersey.containers:jersey-container-grizzly2-servlet:$jerseyVersion") | ||
testImplementation("com.h2database:h2:${project.extra["h2Version"]}") | ||
|
||
testImplementation("org.junit.jupiter:junit-jupiter:$junitVersion") | ||
testImplementation 'org.hamcrest:hamcrest-all:1.3' | ||
testImplementation("com.squareup.okhttp3:okhttp:$okhttpVersion") | ||
|
||
testRuntimeOnly("ch.qos.logback:logback-classic:1.2.3") | ||
} | ||
|
||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { | ||
kotlinOptions { | ||
jvmTarget = "11" | ||
apiVersion = "1.4" | ||
languageVersion = "1.4" | ||
} | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
testLogging { | ||
events "passed", "skipped", "failed" | ||
exceptionFormat "full" | ||
showStandardStreams = true | ||
} | ||
} | ||
|
||
apply from: "$rootDir/gradle/publishing.gradle" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# radar-jersey-hibernate | ||
|
||
Extension module of radar-jersey to use Hibernate in a Jersey project. Default database configuration is for PostgreSQL, but any JDBC driver compatible with Hibernate can be used. The module is activated by adding `HibernateResourceEnhancer` to your `EnhancerFactory` with a given database configuration. When this is done, `Provider<EntityManager>` can be injected via the `Context` annotation. | ||
|
||
To make full use if the module, let any database-using classes extend `HibernateRepository`. Any database operations must be wrapped in a `transact { doSomething() }` or `createTransaction { doSomething() }.use { result -> doSomething result }`. Both closures have EntityManager as `this` object, meaning that `createQuery` and derivatives should be called without referencing an additional `EntityManager`. | ||
|
||
By default, liquibase is used to manage database versioning. It can be disabled by setting `DatabaseConfig.liquibase.enable` to `false`. If enabled, liquibase expects the master changelog file to reside in resources at `DatabaseConfig.liquibase.changelogs` (default `db/changelog/changes/db.changelog-master.xml`.) | ||
|
||
Example repository code: | ||
|
||
```kotlin | ||
class ProjectRepositoryImpl( | ||
@Context em: Provider<EntityManager> | ||
): ProjectRepository, HibernateRepository(em) { | ||
fun list(): List<ProjectDao> = transact { | ||
createQuery("SELECT p FROM Project p", ProjectDao::class.java) | ||
.resultList | ||
} | ||
} | ||
``` |
42 changes: 42 additions & 0 deletions
42
...-jersey-hibernate/src/main/kotlin/org/radarbase/jersey/hibernate/DatabaseHealthMetrics.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.radarbase.jersey.hibernate | ||
|
||
import liquibase.database.DatabaseFactory | ||
import liquibase.database.jvm.JdbcConnection | ||
import org.radarbase.jersey.hibernate.RadarEntityManagerFactory.Companion.connection | ||
import org.radarbase.jersey.hibernate.config.DatabaseConfig | ||
import org.radarbase.jersey.service.HealthService | ||
import org.radarbase.jersey.service.HealthService.Metric | ||
import org.radarbase.jersey.util.CachedValue | ||
import org.slf4j.LoggerFactory | ||
import java.time.Duration | ||
import javax.inject.Provider | ||
import javax.persistence.EntityManager | ||
import javax.ws.rs.core.Context | ||
|
||
class DatabaseHealthMetrics( | ||
@Context private val entityManager: Provider<EntityManager>, | ||
@Context dbConfig: DatabaseConfig | ||
): Metric(name = "db") { | ||
private val cachedStatus = CachedValue( | ||
Duration.ofSeconds(dbConfig.healthCheckValiditySeconds), | ||
Duration.ofSeconds(dbConfig.healthCheckValiditySeconds)) { | ||
testConnection() | ||
} | ||
|
||
override val status: HealthService.Status | ||
get() = cachedStatus.get { it == HealthService.Status.UP } | ||
|
||
override val metrics: Any | ||
get() = mapOf("status" to status) | ||
|
||
private fun testConnection(): HealthService.Status = try { | ||
entityManager.get().connection().close() | ||
HealthService.Status.UP | ||
} catch (ex: Throwable) { | ||
HealthService.Status.DOWN | ||
} | ||
|
||
companion object { | ||
private val logger = LoggerFactory.getLogger(DatabaseHealthMetrics::class.java) | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
...jersey-hibernate/src/main/kotlin/org/radarbase/jersey/hibernate/DatabaseInitialization.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package org.radarbase.jersey.hibernate | ||
|
||
import liquibase.Contexts | ||
import liquibase.Liquibase | ||
import liquibase.database.DatabaseFactory | ||
import liquibase.database.jvm.JdbcConnection | ||
import liquibase.resource.ClassLoaderResourceAccessor | ||
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.radarbase.jersey.hibernate.RadarEntityManagerFactory.Companion.connection | ||
import org.radarbase.jersey.hibernate.RadarEntityManagerFactoryFactory.Companion.useEntityManager | ||
import org.radarbase.jersey.hibernate.config.DatabaseConfig | ||
import org.slf4j.LoggerFactory | ||
import java.sql.Connection | ||
import javax.persistence.EntityManagerFactory | ||
import javax.ws.rs.core.Context | ||
import javax.ws.rs.ext.Provider | ||
|
||
@Provider | ||
class DatabaseInitialization( | ||
@Context private val entityManagerFactory: javax.inject.Provider<EntityManagerFactory>, | ||
@Context private val dbConfig: DatabaseConfig, | ||
) : ApplicationEventListener { | ||
override fun onEvent(event: ApplicationEvent) { | ||
logger.info("Application state: {}", event.type) | ||
if (event.type != ApplicationEvent.Type.INITIALIZATION_APP_FINISHED) return | ||
try { | ||
entityManagerFactory.get().useEntityManager { | ||
// make first connection | ||
it.connection().use { connection -> | ||
if (dbConfig.liquibase.enable) { | ||
initializeLiquibase(connection) | ||
} | ||
} | ||
} | ||
} catch (ex: Throwable) { | ||
throw IllegalStateException("Cannot initialize database.", ex) | ||
} | ||
} | ||
|
||
private fun initializeLiquibase(connection: Connection) { | ||
logger.info("Initializing Liquibase") | ||
val database = DatabaseFactory.getInstance() | ||
.findCorrectDatabaseImplementation( | ||
JdbcConnection(connection)) | ||
Liquibase(dbConfig.liquibase.changelogs, ClassLoaderResourceAccessor(), database).use { | ||
it.update(null as Contexts?) | ||
} | ||
} | ||
|
||
override fun onRequest(requestEvent: RequestEvent?): RequestEventListener? = null | ||
|
||
companion object { | ||
private val logger = LoggerFactory.getLogger(DatabaseInitialization::class.java) | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
radar-jersey-hibernate/src/main/kotlin/org/radarbase/jersey/hibernate/HibernateRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package org.radarbase.jersey.hibernate | ||
|
||
import org.radarbase.jersey.exception.HttpInternalServerException | ||
import org.radarbase.jersey.hibernate.config.CloseableTransaction | ||
import org.slf4j.LoggerFactory | ||
import javax.inject.Provider | ||
import javax.persistence.EntityManager | ||
import javax.persistence.EntityTransaction | ||
|
||
open class HibernateRepository( | ||
@Suppress("MemberVisibilityCanBePrivate") | ||
protected val entityManager: Provider<EntityManager> | ||
) { | ||
/** | ||
* Run a transaction and commit it. If an exception occurs, the transaction is rolled back. | ||
*/ | ||
open fun <T> transact(transactionOperation: EntityManager.() -> T) = createTransaction { | ||
it.use { transactionOperation() } | ||
} | ||
|
||
/** | ||
* Start a transaction without committing it. If an exception occurs, the transaction is rolled back. | ||
*/ | ||
open fun <T> createTransaction(transactionOperation: EntityManager.(CloseableTransaction) -> T): T { | ||
val entityManager = entityManager.get() | ||
val currentTransaction = entityManager.transaction | ||
?: throw HttpInternalServerException("transaction_not_found", "Cannot find a transaction from EntityManager") | ||
|
||
currentTransaction.begin() | ||
try { | ||
return entityManager.transactionOperation(object : CloseableTransaction { | ||
override val transaction: EntityTransaction = currentTransaction | ||
|
||
override fun close() { | ||
try { | ||
transaction.commit() | ||
} catch (ex: Exception) { | ||
logger.error("Rolling back operation", ex) | ||
if (currentTransaction.isActive) { | ||
currentTransaction.rollback() | ||
} | ||
throw ex | ||
} | ||
} | ||
}) | ||
} catch (ex: Exception) { | ||
logger.error("Rolling back operation", ex) | ||
if (currentTransaction.isActive) { | ||
currentTransaction.rollback() | ||
} | ||
throw ex | ||
} | ||
} | ||
|
||
|
||
companion object { | ||
private val logger = LoggerFactory.getLogger(HibernateRepository::class.java) | ||
} | ||
} | ||
|
33 changes: 33 additions & 0 deletions
33
...sey-hibernate/src/main/kotlin/org/radarbase/jersey/hibernate/RadarEntityManagerFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.radarbase.jersey.hibernate | ||
|
||
import org.glassfish.jersey.internal.inject.DisposableSupplier | ||
import org.hibernate.engine.spi.SharedSessionContractImplementor | ||
import org.hibernate.internal.SessionImpl | ||
import org.slf4j.LoggerFactory | ||
import javax.inject.Provider | ||
import javax.persistence.EntityManager | ||
import javax.persistence.EntityManagerFactory | ||
import javax.ws.rs.core.Context | ||
|
||
class RadarEntityManagerFactory( | ||
@Context private val emf: EntityManagerFactory | ||
) : DisposableSupplier<EntityManager> { | ||
|
||
override fun get(): EntityManager { | ||
logger.debug("Creating EntityManager...") | ||
return emf.createEntityManager() | ||
} | ||
|
||
override fun dispose(instance: EntityManager?) { | ||
instance?.let { | ||
logger.debug("Disposing EntityManager") | ||
it.close() | ||
} | ||
} | ||
|
||
companion object { | ||
private val logger = LoggerFactory.getLogger(RadarEntityManagerFactory::class.java) | ||
|
||
fun EntityManager.connection() = unwrap(SessionImpl::class.java).connection() | ||
} | ||
} |
Oops, something went wrong.