diff options
author | Henning Baldersheim <balder@yahoo-inc.com> | 2018-06-04 17:41:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-04 17:41:04 +0200 |
commit | 7f385ad3515184d0fa4099cc57e85a2060cfbd80 (patch) | |
tree | dacb621d2def964e2a001de927f28b3a18770f3d /container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala | |
parent | f16999e04017a5c38cdafed505498189cb24a66c (diff) |
Revert "Container-jersey2: Scala code replaced with Java"
Diffstat (limited to 'container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala')
-rw-r--r-- | container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala new file mode 100644 index 00000000000..f0eff54dc16 --- /dev/null +++ b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyServletProvider.scala @@ -0,0 +1,109 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.servlet.jersey + +import java.io.{IOException, InputStream} + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider +import com.yahoo.container.di.componentgraph.Provider +import com.yahoo.container.di.config.RestApiContext +import com.yahoo.container.di.config.RestApiContext.BundleInfo +import com.yahoo.container.jaxrs.annotation.Component +import com.yahoo.container.servlet.jersey.util.ResourceConfigUtil.registerComponent +import org.eclipse.jetty.servlet.ServletHolder +import org.glassfish.hk2.api.{InjectionResolver, TypeLiteral} +import org.glassfish.hk2.utilities.Binder +import org.glassfish.hk2.utilities.binding.AbstractBinder +import org.glassfish.jersey.media.multipart.MultiPartFeature +import org.glassfish.jersey.server.ResourceConfig +import org.glassfish.jersey.servlet.ServletContainer +import org.objectweb.asm.ClassReader + +import scala.collection.JavaConverters._ +import scala.util.control.Exception + + +/** + * @author tonytv + */ +class JerseyServletProvider(restApiContext: RestApiContext) extends Provider[ServletHolder] { + private val jerseyServletHolder = new ServletHolder(new ServletContainer(resourceConfig(restApiContext))) + + private def resourceConfig(restApiContext: RestApiContext) = { + val resourceConfig = ResourceConfig.forApplication( + new JerseyApplication(resourcesAndProviders(restApiContext.getBundles.asScala))) + + registerComponent(resourceConfig, componentInjectorBinder(restApiContext)) + registerComponent(resourceConfig, jacksonDatatypeJdk8Provider) + resourceConfig.register(classOf[MultiPartFeature]) + + resourceConfig + } + + def resourcesAndProviders(bundles: Traversable[BundleInfo]) = + (for { + bundle <- bundles.view + classEntry <- bundle.getClassEntries.asScala + className <- detectResourceOrProvider(bundle.classLoader, classEntry) + } yield loadClass(bundle.symbolicName, bundle.classLoader, className)).toSet + + + def detectResourceOrProvider(bundleClassLoader: ClassLoader, classEntry: String): Option[String] = { + using(getResourceAsStream(bundleClassLoader, classEntry)) { inputStream => + val visitor = ResourceOrProviderClassVisitor.visit(new ClassReader(inputStream)) + visitor.getJerseyClassName + } + } + + private def getResourceAsStream(bundleClassLoader: ClassLoader, classEntry: String) = { + bundleClassLoader.getResourceAsStream(classEntry) match { + case null => throw new RuntimeException(s"No entry $classEntry in bundle $bundleClassLoader") + case stream => stream + } + + } + + def using[T <: InputStream, R](stream: T)(f: T => R): R = { + try { + f(stream) + } finally { + Exception.ignoring(classOf[IOException]) { + stream.close() + } + } + } + + def loadClass(bundleSymbolicName: String, classLoader: ClassLoader, className: String) = { + try { + classLoader.loadClass(className) + } catch { + case e: Exception => throw new RuntimeException(s"Failed loading class $className from bundle $bundleSymbolicName", e) + } + } + + def componentInjectorBinder(restApiContext: RestApiContext): Binder = { + val componentGraphProvider = new ComponentGraphProvider(restApiContext.getInjectableComponents.asScala) + val componentAnnotationType = new TypeLiteral[InjectionResolver[Component]] {} + + new AbstractBinder { + override def configure() { + bind(componentGraphProvider).to(componentAnnotationType) + } + } + } + + def jacksonDatatypeJdk8Provider: JacksonJaxbJsonProvider = { + val provider = new JacksonJaxbJsonProvider() + provider.setMapper( + new ObjectMapper() + .registerModule(new Jdk8Module) + .registerModule(new JavaTimeModule)) + provider + } + + override def get() = jerseyServletHolder + override def deconstruct() {} +} + |