Merged in plt-17-api-server-infra (pull request #30)

Jersey / Jackson / JAX-RS added to nodes
This commit is contained in:
Rick Parker 2016-03-11 10:09:23 +00:00
commit 00294e78cc
3 changed files with 89 additions and 2 deletions

View File

@ -16,7 +16,8 @@ buildscript {
ext.quasar_version = '0.7.5-SNAPSHOT' ext.quasar_version = '0.7.5-SNAPSHOT'
ext.asm_version = '0.5.3' ext.asm_version = '0.5.3'
ext.artemis_version = '1.2.0' ext.artemis_version = '1.2.0'
ext.jetty_version = '9.3.7.v20160115' ext.jetty_version = '9.1.1.v20140108'
ext.jersey_version = '2.22.2'
repositories { repositories {
mavenCentral() mavenCentral()
@ -99,6 +100,22 @@ dependencies {
compile "org.jolokia:jolokia-agent-war:2.0.0-M1" compile "org.jolokia:jolokia-agent-war:2.0.0-M1"
compile "commons-fileupload:commons-fileupload:1.3.1" compile "commons-fileupload:commons-fileupload:1.3.1"
// Jersey for JAX-RS implementation for use in Jetty
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
// NOTE there is a Jackson version clash between jersey-media-json-jackson (v2.5.4) and jackson-module-kotlin (v.2.5.5)
// Have not found an Issue in the issue tracker for Jersey for this issue
compile ("org.glassfish.jersey.media:jersey-media-json-jackson:${jersey_version}") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-core'
}
compile ("com.fasterxml.jackson.module:jackson-module-kotlin:2.5.5-2") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
}
compile "com.fasterxml.jackson.core:jackson-annotations:2.5.5"
// Unit testing helpers. // Unit testing helpers.
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'com.google.jimfs:jimfs:1.1' // in memory java.nio filesystem. testCompile 'com.google.jimfs:jimfs:1.1' // in memory java.nio filesystem.

View File

@ -0,0 +1,51 @@
package api
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import java.time.LocalDate
import javax.ws.rs.ext.ContextResolver
import javax.ws.rs.ext.Provider
/**
* Primary purpose is to install Kotlin extensions for Jackson ObjectMapper so data classes work
* and to organise serializers / deserializers for java.time.* classes as necessary
*/
@Provider
class Config: ContextResolver<ObjectMapper> {
val defaultObjectMapper = createDefaultMapper()
override fun getContext(type: java.lang.Class<*>): ObjectMapper {
return defaultObjectMapper
}
companion object {
private fun createDefaultMapper(): ObjectMapper {
val mapper = ObjectMapper()
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // Although we shouldn't really use java.util.* but instead java.time.*
val timeModule = SimpleModule("java.time")
timeModule.addSerializer(LocalDate::class.java, ToStringSerializer)
timeModule.addDeserializer(LocalDate::class.java, LocalDateDeserializer)
mapper.registerModule(timeModule)
mapper.registerModule(KotlinModule())
return mapper
}
}
object ToStringSerializer: JsonSerializer<Any>() {
override fun serialize(obj: Any, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.toString())
}
}
object LocalDateDeserializer: JsonDeserializer<LocalDate>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): LocalDate {
return LocalDate.parse(parser.text)
}
}
}

View File

@ -8,6 +8,7 @@
package core.node package core.node
import api.Config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import core.messaging.LegallyIdentifiableNode import core.messaging.LegallyIdentifiableNode
import core.messaging.MessagingService import core.messaging.MessagingService
@ -18,6 +19,9 @@ import core.utilities.loggerFor
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.handler.HandlerCollection import org.eclipse.jetty.server.handler.HandlerCollection
import org.eclipse.jetty.servlet.ServletContextHandler import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.servlet.ServletHolder
import org.glassfish.jersey.server.ServerProperties
import org.glassfish.jersey.servlet.ServletContainer
import org.eclipse.jetty.webapp.WebAppContext import org.eclipse.jetty.webapp.WebAppContext
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
@ -25,6 +29,7 @@ import java.nio.channels.FileLock
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
import kotlin.reflect.KClass
class ConfigurationException(message: String) : Exception(message) class ConfigurationException(message: String) : Exception(message)
@ -73,12 +78,21 @@ class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration
}) })
} }
// Data upload and download to services (attachments, rates oracles etc) // API, data upload and download to services (attachments, rates oracles etc)
handlerCollection.addHandler(ServletContextHandler().apply { handlerCollection.addHandler(ServletContextHandler().apply {
contextPath = "/" contextPath = "/"
setAttribute("storage", storage) setAttribute("storage", storage)
addServlet(DataUploadServlet::class.java, "/upload/*") addServlet(DataUploadServlet::class.java, "/upload/*")
addServlet(AttachmentDownloadServlet::class.java, "/attachments/*") addServlet(AttachmentDownloadServlet::class.java, "/attachments/*")
setAttribute("services", services)
val jerseyServlet = addServlet(ServletContainer::class.java, "/api/*")
// Give the app a slightly better name in JMX rather than a randomly generated one
jerseyServlet.setInitParameter(ServerProperties.APPLICATION_NAME, "node.api")
jerseyServlet.setInitParameter(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, "true")
jerseyServlet.initOrder = 0 // Initialise at server start
// Add your API provider classes (annotated for JAX-RS) here
setProviders(jerseyServlet, Config::class)
}) })
server.handler = handlerCollection server.handler = handlerCollection
@ -86,6 +100,11 @@ class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration
return server return server
} }
private fun setProviders(jerseyServlet: ServletHolder, vararg providerClasses: KClass<out Any>) {
val providerClassNames = providerClasses.map { it.java.canonicalName }.joinToString()
jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, providerClassNames)
}
override fun start(): Node { override fun start(): Node {
alreadyRunningNodeCheck() alreadyRunningNodeCheck()
super.start() super.start()