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 | |
parent | f16999e04017a5c38cdafed505498189cb24a66c (diff) |
Revert "Container-jersey2: Scala code replaced with Java"
Diffstat (limited to 'container-jersey2/src/main')
8 files changed, 239 insertions, 319 deletions
diff --git a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ComponentGraphProvider.java b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ComponentGraphProvider.java deleted file mode 100644 index 7ff9646cb27..00000000000 --- a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ComponentGraphProvider.java +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018 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 com.yahoo.container.di.config.ResolveDependencyException; -import com.yahoo.container.di.config.RestApiContext; -import com.yahoo.container.jaxrs.annotation.Component; -import org.glassfish.hk2.api.Injectee; -import org.glassfish.hk2.api.InjectionResolver; -import org.glassfish.hk2.api.ServiceHandle; - -import javax.inject.Singleton; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Resolves jdisc container components for jersey 2 components. - * - * @author Tony Vaagenes - * @author ollivir - */ -@Singleton // jersey2 requirement: InjectionResolvers must be in the Singleton scope -public class ComponentGraphProvider implements InjectionResolver<Component> { - private Collection<RestApiContext.Injectable> injectables; - - public ComponentGraphProvider(Collection<RestApiContext.Injectable> injectables) { - this.injectables = injectables; - } - - @Override - public Object resolve(Injectee injectee, ServiceHandle<?> root) { - Class<?> wantedClass; - Type type = injectee.getRequiredType(); - if (type instanceof Class) { - wantedClass = (Class<?>) type; - } else { - throw new UnsupportedOperationException("Only classes are supported, got " + type); - } - - List<RestApiContext.Injectable> componentsWithMatchingType = new ArrayList<>(); - for (RestApiContext.Injectable injectable : injectables) { - if (wantedClass.isInstance(injectable.instance)) { - componentsWithMatchingType.add(injectable); - } - } - - if (componentsWithMatchingType.size() == 1) { - return componentsWithMatchingType.get(0).instance; - } else { - String injectionDescription = "class '" + wantedClass + "' to inject into Jersey resource/provider '" - + injectee.getInjecteeClass() + "')"; - if (componentsWithMatchingType.size() > 1) { - String ids = componentsWithMatchingType.stream().map(c -> c.id.toString()).collect(Collectors.joining(",")); - throw new ResolveDependencyException("Multiple components found of " + injectionDescription + ": " + ids); - } else { - throw new ResolveDependencyException("Could not find a component of " + injectionDescription + "."); - } - } - } - - @Override - public boolean isMethodParameterIndicator() { - return true; - } - - @Override - public boolean isConstructorParameterIndicator() { - return true; - } -} diff --git a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyApplication.java b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyApplication.java deleted file mode 100644 index 4c4e43bc8d5..00000000000 --- a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyApplication.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 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 javax.ws.rs.core.Application; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * @author Tony Vaagenes - * @author ollivir - */ -public class JerseyApplication extends Application { - private Set<Class<?>> classes; - - public JerseyApplication(Collection<Class<?>> resourcesAndProviderClasses) { - this.classes = new HashSet<>(resourcesAndProviderClasses); - } - - @Override - public Set<Class<?>> getClasses() { - return classes; - } -} diff --git a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyServletProvider.java b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyServletProvider.java deleted file mode 100644 index ab094e77650..00000000000 --- a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/JerseyServletProvider.java +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 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 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 org.eclipse.jetty.servlet.ServletHolder; -import org.glassfish.hk2.api.InjectionResolver; -import org.glassfish.hk2.api.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 java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -import static com.yahoo.container.servlet.jersey.util.ResourceConfigUtil.registerComponent; - -/** - * @author Tony Vaagenes - * @author ollivir - */ -public class JerseyServletProvider implements Provider<ServletHolder> { - private final ServletHolder jerseyServletHolder; - - public JerseyServletProvider(RestApiContext restApiContext) { - this.jerseyServletHolder = new ServletHolder(new ServletContainer(resourceConfig(restApiContext))); - } - - private ResourceConfig resourceConfig(RestApiContext restApiContext) { - final ResourceConfig resourceConfig = ResourceConfig - .forApplication(new JerseyApplication(resourcesAndProviders(restApiContext.getBundles()))); - - registerComponent(resourceConfig, componentInjectorBinder(restApiContext)); - registerComponent(resourceConfig, jacksonDatatypeJdk8Provider()); - resourceConfig.register(MultiPartFeature.class); - - return resourceConfig; - } - - private static Collection<Class<?>> resourcesAndProviders(Collection<BundleInfo> bundles) { - final List<Class<?>> ret = new ArrayList<>(); - - for (BundleInfo bundle : bundles) { - for (String classEntry : bundle.getClassEntries()) { - Optional<String> className = detectResourceOrProvider(bundle.classLoader, classEntry); - className.ifPresent(cname -> ret.add(loadClass(bundle.symbolicName, bundle.classLoader, cname))); - } - } - return ret; - } - - private static Optional<String> detectResourceOrProvider(ClassLoader bundleClassLoader, String classEntry) { - try (InputStream inputStream = getResourceAsStream(bundleClassLoader, classEntry)) { - ResourceOrProviderClassVisitor visitor = ResourceOrProviderClassVisitor.visit(new ClassReader(inputStream)); - return Optional.ofNullable(visitor.getClassName()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static InputStream getResourceAsStream(ClassLoader bundleClassLoader, String classEntry) { - InputStream is = bundleClassLoader.getResourceAsStream(classEntry); - if (is == null) { - throw new RuntimeException("No entry " + classEntry + " in bundle " + bundleClassLoader); - } else { - return is; - } - } - - private static Class<?> loadClass(String bundleSymbolicName, ClassLoader classLoader, String className) { - try { - return classLoader.loadClass(className); - } catch (Exception e) { - throw new RuntimeException("Failed loading class " + className + " from bundle " + bundleSymbolicName, e); - } - } - - private static Binder componentInjectorBinder(RestApiContext restApiContext) { - final ComponentGraphProvider componentGraphProvider = new ComponentGraphProvider(restApiContext.getInjectableComponents()); - final TypeLiteral<InjectionResolver<Component>> componentAnnotationType = new TypeLiteral<InjectionResolver<Component>>() { - }; - - return new AbstractBinder() { - @Override - public void configure() { - bind(componentGraphProvider).to(componentAnnotationType); - } - }; - } - - private static JacksonJaxbJsonProvider jacksonDatatypeJdk8Provider() { - JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(); - provider.setMapper(new ObjectMapper().registerModule(new Jdk8Module()).registerModule(new JavaTimeModule())); - return provider; - } - - @Override - public ServletHolder get() { - return jerseyServletHolder; - } - - @Override - public void deconstruct() { - } -} diff --git a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.java b/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.java deleted file mode 100644 index 9abf573c73a..00000000000 --- a/container-jersey2/src/main/java/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.java +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018 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 org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import javax.ws.rs.Path; -import javax.ws.rs.ext.Provider; - -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -/** - * @author Tony Vaagenes - * @author ollivir - */ -public class ResourceOrProviderClassVisitor extends ClassVisitor { - private String className = null; - private boolean isPublic = false; - private boolean isAbstract = false; - - private boolean isInnerClass = false; - private boolean isStatic = false; - - private boolean isAnnotated = false; - - public ResourceOrProviderClassVisitor() { - super(Opcodes.ASM5); - } - - public Optional<String> getJerseyClassName() { - if (isJerseyClass()) { - return Optional.of(getClassName()); - } else { - return Optional.empty(); - } - } - - public boolean isJerseyClass() { - return isAnnotated && isPublic && !isAbstract && (!isInnerClass || isStatic); - } - - public String getClassName() { - assert (className != null); - return org.objectweb.asm.Type.getObjectType(className).getClassName(); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - isPublic = isPublic(access); - className = name; - isAbstract = isAbstract(access); - } - - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - assert (className != null); - - if (name.equals(className)) { - isInnerClass = true; - isStatic = isStatic(access); - } - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - isAnnotated |= annotationClassDescriptors.contains(desc); - return null; - } - - private static Set<String> annotationClassDescriptors = new HashSet<>(); - - static { - annotationClassDescriptors.add(Type.getDescriptor(Path.class)); - annotationClassDescriptors.add(Type.getDescriptor(Provider.class)); - } - - private static boolean isPublic(int access) { - return isSet(Opcodes.ACC_PUBLIC, access); - } - - private static boolean isStatic(int access) { - return isSet(Opcodes.ACC_STATIC, access); - } - - private static boolean isAbstract(int access) { - return isSet(Opcodes.ACC_ABSTRACT, access); - } - - private static boolean isSet(int bits, int access) { - return (access & bits) == bits; - } - - public static ResourceOrProviderClassVisitor visit(ClassReader classReader) { - ResourceOrProviderClassVisitor visitor = new ResourceOrProviderClassVisitor(); - classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); - return visitor; - } -} diff --git a/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ComponentGraphProvider.scala b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ComponentGraphProvider.scala new file mode 100644 index 00000000000..cabde3680a4 --- /dev/null +++ b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ComponentGraphProvider.scala @@ -0,0 +1,40 @@ +// 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 javax.inject.Singleton + +import com.yahoo.container.di.config.{ResolveDependencyException, RestApiContext} +import com.yahoo.container.jaxrs.annotation.Component +import org.glassfish.hk2.api.{ServiceHandle, Injectee, InjectionResolver} + +/** + * Resolves jdisc container components for jersey 2 components. + * Similar to Gjoran's ComponentGraphProvider for jersey 1. + * @author tonytv + */ +@Singleton //jersey2 requirement: InjectionResolvers must be in the Singleton scope +class ComponentGraphProvider(injectables: Traversable[RestApiContext.Injectable]) extends InjectionResolver[Component] { + override def resolve(injectee: Injectee, root: ServiceHandle[_]): AnyRef = { + val wantedClass = injectee.getRequiredType match { + case c: Class[_] => c + case unsupported => throw new UnsupportedOperationException("Only classes are supported, got " + unsupported) + } + + val componentsWithMatchingType = injectables.filter{ injectable => + wantedClass.isInstance(injectable.instance) } + + val injectionDescription = + s"class '$wantedClass' to inject into Jersey resource/provider '${injectee.getInjecteeClass}')" + + if (componentsWithMatchingType.size > 1) + throw new ResolveDependencyException(s"Multiple components found of $injectionDescription: " + + componentsWithMatchingType.map(_.id).mkString(",")) + + componentsWithMatchingType.headOption.map(_.instance).getOrElse { + throw new ResolveDependencyException(s"Could not find a component of $injectionDescription.") + } + } + + override def isMethodParameterIndicator: Boolean = true + override def isConstructorParameterIndicator: Boolean = true +} diff --git a/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyApplication.scala b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyApplication.scala new file mode 100644 index 00000000000..eea41003984 --- /dev/null +++ b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/JerseyApplication.scala @@ -0,0 +1,16 @@ +// 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 javax.ws.rs.core.Application + +import scala.collection.JavaConverters._ + +/** + * @author tonytv + */ +class JerseyApplication(resourcesAndProviderClasses: Set[Class[_]]) extends Application { + private val classes: java.util.Set[Class[_]] = resourcesAndProviderClasses.asJava + + override def getClasses = classes + override def getSingletons = super.getSingletons +} 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() {} +} + diff --git a/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.scala b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.scala new file mode 100644 index 00000000000..c015f11360e --- /dev/null +++ b/container-jersey2/src/main/scala/com/yahoo/container/servlet/jersey/ResourceOrProviderClassVisitor.scala @@ -0,0 +1,74 @@ +// 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 javax.ws.rs.Path +import javax.ws.rs.ext.Provider + +import org.objectweb.asm.{ClassVisitor, Opcodes, Type, AnnotationVisitor, ClassReader} + + +/** + * @author tonytv + */ +class ResourceOrProviderClassVisitor private () extends ClassVisitor(Opcodes.ASM5) { + private var className: String = null + private var isPublic: Boolean = false + private var isAbstract = false + + private var isInnerClass: Boolean = false + private var isStatic: Boolean = false + + private var isAnnotated: Boolean = false + + def getJerseyClassName: Option[String] = { + if (isJerseyClass) Some(getClassName) + else None + } + + def isJerseyClass: Boolean = { + isAnnotated && isPublic && !isAbstract && + (!isInnerClass || isStatic) + } + + def getClassName = { + assert (className != null) + Type.getObjectType(className).getClassName + } + + override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) { + isPublic = ResourceOrProviderClassVisitor.isPublic(access) + className = name + isAbstract = ResourceOrProviderClassVisitor.isAbstract(access) + } + + override def visitInnerClass(name: String, outerName: String, innerName: String, access: Int) { + assert (className != null) + + if (name == className) { + isInnerClass = true + isStatic = ResourceOrProviderClassVisitor.isStatic(access) + } + } + + override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = { + isAnnotated |= ResourceOrProviderClassVisitor.annotationClassDescriptors(desc) + null + } +} + + +object ResourceOrProviderClassVisitor { + val annotationClassDescriptors = Set(classOf[Path], classOf[Provider]) map Type.getDescriptor + + def isPublic = isSet(Opcodes.ACC_PUBLIC) _ + def isStatic = isSet(Opcodes.ACC_STATIC) _ + def isAbstract = isSet(Opcodes.ACC_ABSTRACT) _ + + private def isSet(bits: Int)(access: Int): Boolean = (access & bits) == bits + + def visit(classReader: ClassReader): ResourceOrProviderClassVisitor = { + val visitor = new ResourceOrProviderClassVisitor + classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES) + visitor + } +} |