Skip to content

Commit

Permalink
Merge pull request #15 from RADAR-base/release-0.3.0
Browse files Browse the repository at this point in the history
Release 0.3.0
  • Loading branch information
blootsvoets authored Sep 23, 2020
2 parents c14e767 + fd075b5 commit d86294d
Show file tree
Hide file tree
Showing 58 changed files with 1,899 additions and 29 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ out/
# Gradle
######################
.gradle/
/build/
build/
.gradletasknamecache

######################
Expand Down Expand Up @@ -155,7 +155,7 @@ Desktop.ini
.eslintcache

# output directory
/out/
out/

# don't ignore jars in /libs
!/libs/**/*.jar
!libs/**/*.jar
30 changes: 19 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version "1.4.0"
id 'org.jetbrains.kotlin.jvm'
id 'com.jfrog.bintray' version '1.8.5' apply false
}

allprojects {
group = 'org.radarbase'
version = '0.3.0'

ext {
jerseyVersion = "2.31"
grizzlyVersion = "2.4.4"
okhttpVersion = "4.8.1"
junitVersion = "5.6.2"
}
}

description = 'Library for Jersey authorization, exception handling and configuration with the RADAR platform'
group = 'org.radarbase'
version = '0.2.4'

ext {
githubRepoName = 'RADAR-base/radar-jersey'
Expand All @@ -17,17 +27,13 @@ ext {
managementPortalVersion = "0.5.8"
jakartaWsRsVersion = "2.1.6"
jakartaAnnotationVersion = "1.3.5"
jerseyVersion = "2.31"
grizzlyVersion = "2.4.4"
jacksonVersion = "2.11.2"
jacksonModuleVersion = "2.11.2"
okhttpVersion = "4.8.1"
slf4jVersion = "1.7.30"
javaxXmlBindVersion = "2.3.1"
javaxJaxbCoreVersion = "2.3.0.1"
javaxJaxbRuntimeVersion = "2.3.3"
javaxActivation = "1.1.1"
junitVersion = "5.6.2"
}

repositories {
Expand All @@ -45,18 +51,21 @@ dependencies {
api("org.glassfish.jersey.core:jersey-server:$jerseyVersion")

api("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")

implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")

implementation("org.glassfish.jersey.containers:jersey-container-grizzly2-http:$jerseyVersion")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jacksonModuleVersion")

// exception template rendering
implementation 'com.github.spullara.mustache.java:compiler:0.9.6'

implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
implementation "org.slf4j:slf4j-api:$slf4jVersion"

implementation("org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion")
api("org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion")

runtimeOnly("org.glassfish.jersey.media:jersey-media-json-jackson:$jerseyVersion")

Expand All @@ -74,7 +83,6 @@ dependencies {

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")
}
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kotlinVersion=1.4.10
55 changes: 55 additions & 0 deletions radar-jersey-hibernate/build.gradle
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"
20 changes: 20 additions & 0 deletions radar-jersey-hibernate/src/README.md
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
}
}
```
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)
}
}
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)
}
}
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)
}
}

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()
}
}
Loading

0 comments on commit d86294d

Please sign in to comment.