com.sun.jersey.server.impl.cdi.CDIExtension Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jersey-bundle Show documentation
Show all versions of jersey-bundle Show documentation
A bundle containing code of all jar-based modules that provide
JAX-RS and Jersey-related features. Such a bundle is *only intended* for
developers that do not use Maven's dependency system.
The bundle does not include code for contributes, tests and samples.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.jersey.server.impl.cdi;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.CookieParam;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Encoded;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedCallable;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import javax.enterprise.inject.spi.ProcessManagedBean;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import com.sun.jersey.api.core.ExtendedUriInfo;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.core.HttpRequestContext;
import com.sun.jersey.api.core.HttpResponseContext;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.api.core.ResourceContext;
import com.sun.jersey.api.model.Parameter;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.core.util.FeaturesAndProperties;
import com.sun.jersey.server.impl.InitialContextHelper;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.MessageBodyWorkers;
import com.sun.jersey.spi.container.ExceptionMapperContext;
import com.sun.jersey.spi.container.WebApplication;
import com.sun.jersey.spi.inject.Errors;
import com.sun.jersey.spi.inject.Injectable;
/**
*
* @author robc
*/
public class CDIExtension implements Extension {
private static final Logger LOGGER = Logger.getLogger(CDIExtension.class.getName());
private static class ContextAnnotationLiteral extends AnnotationLiteral implements Context {};
private final Context contextAnnotationLiteral = new ContextAnnotationLiteral();
private static class InjectAnnotationLiteral extends AnnotationLiteral implements Inject {};
private final Inject injectAnnotationLiteral = new InjectAnnotationLiteral();
private static class SyntheticQualifierAnnotationImpl extends AnnotationLiteral implements SyntheticQualifier {
private int value;
public SyntheticQualifierAnnotationImpl(int value) {
this.value = value;
}
@Override
public int value() {
return value;
}
}
private Map webApplications = new HashMap();
private ResourceConfig resourceConfig;
private Set> knownParameterQualifiers;
private Set> staticallyDefinedContextBeans;
private Map, Parameter.Source> paramQualifiersMap;
private Map, Set> discoveredParameterMap;
private Map syntheticQualifierMap;
private int nextSyntheticQualifierValue = 0;
private List toBeInitializedLater;
private static String JNDI_CDIEXTENSION_NAME = "CDIExtension";
private static String JNDI_CDIEXTENSION_CTX = "com/sun/jersey/config";
/*
* Setting this system property to "true" will force use of the BeanManager to look up the bean for the active CDIExtension,
* rather than going through a thread local.
*/
private static final String LOOKUP_EXTENSION_IN_BEAN_MANAGER_SYSTEM_PROPERTY = "com.sun.jersey.server.impl.cdi.lookupExtensionInBeanManager";
public static final boolean lookupExtensionInBeanManager = getLookupExtensionInBeanManager();
private static boolean getLookupExtensionInBeanManager() {
return Boolean.parseBoolean(System.getProperty(LOOKUP_EXTENSION_IN_BEAN_MANAGER_SYSTEM_PROPERTY, "false"));
}
/*
* Returns the instance of CDIExtension that was initialized previously in this same thread, if any.
*/
public static CDIExtension getInitializedExtension() {
try {
InitialContext ic = InitialContextHelper.getInitialContext();
if (ic == null) {
throw new RuntimeException();
}
return (CDIExtension)lookupJerseyConfigJNDIContext(ic).lookup(JNDI_CDIEXTENSION_NAME);
} catch (NamingException ex) {
throw new RuntimeException(ex);
}
}
public CDIExtension() {}
private void initialize(BeanManager manager) {
// initialize in a separate method because Weld creates a proxy for the extension
// and we don't want to waste time initializing it
// workaround for Weld proxy bug
if (!lookupExtensionInBeanManager) {
try {
InitialContext ic = InitialContextHelper.getInitialContext();
if (ic != null) {
javax.naming.Context jerseyConfigJNDIContext = createJerseyConfigJNDIContext(ic);
jerseyConfigJNDIContext.rebind(JNDI_CDIEXTENSION_NAME, this);
}
} catch (NamingException ex) {
throw new RuntimeException(ex);
}
}
// annotations to be turned into qualifiers
Set> set = new HashSet>();
set.add(CookieParam.class);
set.add(FormParam.class);
set.add(HeaderParam.class);
set.add(MatrixParam.class);
set.add(PathParam.class);
set.add(QueryParam.class);
set.add(Context.class);
knownParameterQualifiers = Collections.unmodifiableSet(set);
// used to map a qualifier to a Parameter.Source
Map, Parameter.Source> map = new HashMap, Parameter.Source>();
map.put(CookieParam.class,Parameter.Source.COOKIE);
map.put(FormParam.class,Parameter.Source.FORM);
map.put(HeaderParam.class,Parameter.Source.HEADER);
map.put(MatrixParam.class,Parameter.Source.MATRIX);
map.put(PathParam.class,Parameter.Source.PATH);
map.put(QueryParam.class,Parameter.Source.QUERY);
map.put(Context.class,Parameter.Source.CONTEXT);
paramQualifiersMap = Collections.unmodifiableMap(map);
// pre-defined contextual types
Set> set3 = new HashSet>();
// standard types
set3.add(Application.class);
set3.add(HttpHeaders.class);
set3.add(Providers.class);
set3.add(Request.class);
set3.add(SecurityContext.class);
set3.add(UriInfo.class);
// Jersey extensions
set3.add(ExceptionMapperContext.class);
set3.add(ExtendedUriInfo.class);
set3.add(FeaturesAndProperties.class);
set3.add(HttpContext.class);
set3.add(HttpRequestContext.class);
set3.add(HttpResponseContext.class);
set3.add(MessageBodyWorkers.class);
set3.add(ResourceContext.class);
set3.add(WebApplication.class);
staticallyDefinedContextBeans = Collections.unmodifiableSet(set3);
// tracks all discovered parameters
Map, Set> map2 = new HashMap, Set>();
for (Class extends Annotation> qualifier : knownParameterQualifiers) {
map2.put(qualifier, new HashSet());
}
discoveredParameterMap = Collections.unmodifiableMap(map2);
// tracks the synthetic qualifiers we have to create to handle a specific
// combination of JAX-RS injection annotation + default value + encoded
syntheticQualifierMap = new HashMap();
// things to do in a second time, i.e. once Jersey has been initialized,
// as opposed to when CDI delivers the SPI events to its extensions
toBeInitializedLater = new ArrayList();
}
private static interface JNDIContextDiver {
javax.naming.Context stepInto(javax.naming.Context currentContext, String currentName) throws NamingException;
}
private static javax.naming.Context diveIntoJNDIContext(javax.naming.Context initialContext, JNDIContextDiver diver) throws NamingException {
Name jerseyConfigCtxName = initialContext.getNameParser("").parse(JNDI_CDIEXTENSION_CTX);
javax.naming.Context currentContext = initialContext;
for (int i=0; i qualifier : knownParameterQualifiers) {
event.addQualifier(qualifier);
}
}
/*
* Holds information on one site (constructor/method argument or field) to be patched.
*/
private static class PatchInformation {
private DiscoveredParameter parameter;
private SyntheticQualifier syntheticQualifier;
private Annotation annotation;
private boolean mustAddInject;
public PatchInformation(DiscoveredParameter parameter, SyntheticQualifier syntheticQualifier, boolean mustAddInject) {
this(parameter, syntheticQualifier, null, mustAddInject);
}
public PatchInformation(DiscoveredParameter parameter, SyntheticQualifier syntheticQualifier, Annotation annotation, boolean mustAddInject) {
this.parameter = parameter;
this.syntheticQualifier = syntheticQualifier;
this.annotation = annotation;
this.mustAddInject = mustAddInject;
}
public DiscoveredParameter getParameter() {
return parameter;
}
public SyntheticQualifier getSyntheticQualifier() {
return syntheticQualifier;
}
public Annotation getAnnotation() {
return annotation;
}
public boolean mustAddInject() {
return mustAddInject;
}
}
void processAnnotatedType(@Observes ProcessAnnotatedType event) {
LOGGER.fine("Handling ProcessAnnotatedType event for " + event.getAnnotatedType().getJavaClass().getName());
AnnotatedType type = event.getAnnotatedType();
/*
// only scan managed beans
if (!type.isAnnotationPresent(ManagedBean.class)) {
return;
}
// only scan root resource classes for now
if (!type.isAnnotationPresent(Path.class)) {
return;
}
*/
// first pass to determine if we need to patch any sites
// we also record any qualifiers with parameters we encounter
// so we can create beans for them later
// TODO - maybe we should detect cases in which the constructor selection
// rules in CDI and JAX-RS are in conflict -- CDI should win, but
// the result may surprise the user
boolean classHasEncodedAnnotation = type.isAnnotationPresent(Encoded.class);
Set> mustPatchConstructors = new HashSet>();
Map, PatchInformation> parameterToPatchInfoMap = new HashMap, PatchInformation>();
for (AnnotatedConstructor constructor : type.getConstructors()) {
if (processAnnotatedConstructor(constructor, classHasEncodedAnnotation, parameterToPatchInfoMap)) {
mustPatchConstructors.add(constructor);
}
}
Set> mustPatchFields = new HashSet>();
Map, PatchInformation> fieldToPatchInfoMap = new HashMap, PatchInformation>();
for (AnnotatedField super T> field : type.getFields()) {
if (processAnnotatedField(field, type.getJavaClass(), classHasEncodedAnnotation, fieldToPatchInfoMap)) {
mustPatchFields.add(field);
}
}
Set> mustPatchMethods = new HashSet>();
Set> setterMethodsWithoutInject = new HashSet>();
for (AnnotatedMethod super T> method : type.getMethods()) {
if (processAnnotatedMethod(method, type.getJavaClass(), classHasEncodedAnnotation, parameterToPatchInfoMap, setterMethodsWithoutInject)) {
mustPatchMethods.add(method);
}
}
boolean typeNeedsPatching = !(mustPatchConstructors.isEmpty() && mustPatchFields.isEmpty() && mustPatchMethods.isEmpty());
// second pass
if (typeNeedsPatching) {
AnnotatedTypeImpl newType = new AnnotatedTypeImpl(type);
Set> newConstructors = new HashSet>();
for (AnnotatedConstructor constructor : type.getConstructors()) {
AnnotatedConstructorImpl newConstructor = new AnnotatedConstructorImpl(constructor, newType);
if (mustPatchConstructors.contains(constructor)) {
patchAnnotatedCallable(constructor, newConstructor, parameterToPatchInfoMap);
}
else {
copyParametersOfAnnotatedCallable(constructor, newConstructor);
}
newConstructors.add(newConstructor);
}
Set> newFields = new HashSet>();
for (AnnotatedField super T> field : type.getFields()) {
if (mustPatchFields.contains(field)) {
PatchInformation patchInfo = fieldToPatchInfoMap.get(field);
Set annotations = new HashSet();
if (patchInfo.mustAddInject()) {
annotations.add(injectAnnotationLiteral);
}
if (patchInfo.getSyntheticQualifier() != null) {
annotations.add(patchInfo.getSyntheticQualifier());
Annotation skippedQualifier = patchInfo.getParameter().getAnnotation();
for (Annotation annotation : field.getAnnotations()) {
if (annotation != skippedQualifier) {
annotations.add(annotation);
}
}
}
else {
annotations.addAll(field.getAnnotations());
}
if (patchInfo.getAnnotation() != null) {
annotations.add(patchInfo.getAnnotation());
}
newFields.add(new AnnotatedFieldImpl(field, annotations, newType));
}
else {
// copy and reparent
newFields.add(new AnnotatedFieldImpl(field, newType));
}
}
Set> newMethods = new HashSet>();
for (AnnotatedMethod super T> method : type.getMethods()) {
if (mustPatchMethods.contains((AnnotatedMethod)method)) {
if (setterMethodsWithoutInject.contains((AnnotatedMethod)method)) {
Set annotations = new HashSet();
annotations.add(injectAnnotationLiteral);
for (Annotation annotation : method.getAnnotations()) {
if (!knownParameterQualifiers.contains(annotation.annotationType())) {
annotations.add(annotation);
}
}
AnnotatedMethodImpl newMethod = new AnnotatedMethodImpl(method, annotations, newType);
patchAnnotatedCallable(method, newMethod, parameterToPatchInfoMap);
newMethods.add(newMethod);
}
else {
AnnotatedMethodImpl newMethod = new AnnotatedMethodImpl(method, newType);
patchAnnotatedCallable(method, newMethod, parameterToPatchInfoMap);
newMethods.add(newMethod);
}
}
else {
AnnotatedMethodImpl newMethod = new AnnotatedMethodImpl(method, newType);
copyParametersOfAnnotatedCallable(method, newMethod);
newMethods.add(newMethod);
}
}
newType.setConstructors(newConstructors);
newType.setFields(newFields);
newType.setMethods(newMethods);
event.setAnnotatedType(newType);
LOGGER.fine(" replaced annotated type for " + type.getJavaClass());
}
}
private boolean processAnnotatedConstructor(AnnotatedConstructor constructor,
boolean classHasEncodedAnnotation,
Map, PatchInformation> parameterToPatchInfoMap) {
boolean mustPatch = false;
if (constructor.getAnnotation(Inject.class) != null) {
boolean methodHasEncodedAnnotation = constructor.isAnnotationPresent(Encoded.class);
for (AnnotatedParameter parameter : constructor.getParameters()) {
for (Annotation annotation : parameter.getAnnotations()) {
Set discovered = discoveredParameterMap.get(annotation.annotationType());
if (discovered != null) {
if (knownParameterQualifiers.contains(annotation.annotationType())) {
if (methodHasEncodedAnnotation ||
classHasEncodedAnnotation ||
parameter.isAnnotationPresent(DefaultValue.class)) {
mustPatch = true;
}
boolean encoded = parameter.isAnnotationPresent(Encoded.class) || methodHasEncodedAnnotation || classHasEncodedAnnotation;
DefaultValue defaultValue = parameter.getAnnotation(DefaultValue.class);
if (defaultValue != null) {
mustPatch = true;
}
DiscoveredParameter jerseyParameter = new DiscoveredParameter(annotation, parameter.getBaseType(), defaultValue, encoded);
discovered.add(jerseyParameter);
LOGGER.fine(" recorded " + jerseyParameter);
parameterToPatchInfoMap.put(parameter, new PatchInformation(jerseyParameter, getSyntheticQualifierFor(jerseyParameter), false));
}
}
}
}
}
return mustPatch;
}
private boolean processAnnotatedMethod(AnnotatedMethod super T> method,
Class token,
boolean classHasEncodedAnnotation,
Map, PatchInformation> parameterToPatchInfoMap,
Set> setterMethodsWithoutInject) {
boolean mustPatch = false;
if (method.getAnnotation(Inject.class) != null) {
// a method already annotated with @Inject -- we assume the user is
// aware of CDI and all we need to do is to detect the need for
// a synthetic qualifier so as to take @DefaultValue and @Encoded into
// account
boolean methodHasEncodedAnnotation = method.isAnnotationPresent(Encoded.class);
for (AnnotatedParameter super T> parameter : method.getParameters()) {
for (Annotation annotation : parameter.getAnnotations()) {
Set discovered = discoveredParameterMap.get(annotation.annotationType());
if (discovered != null) {
if (knownParameterQualifiers.contains(annotation.annotationType())) {
if (methodHasEncodedAnnotation ||
classHasEncodedAnnotation ||
parameter.isAnnotationPresent(DefaultValue.class)) {
mustPatch = true;
}
boolean encoded = parameter.isAnnotationPresent(Encoded.class) || methodHasEncodedAnnotation || classHasEncodedAnnotation;
DefaultValue defaultValue = parameter.getAnnotation(DefaultValue.class);
if (defaultValue != null) {
mustPatch = true;
}
DiscoveredParameter jerseyParameter = new DiscoveredParameter(annotation, parameter.getBaseType(), defaultValue, encoded);
discovered.add(jerseyParameter);
LOGGER.fine(" recorded " + jerseyParameter);
parameterToPatchInfoMap.put(parameter, new PatchInformation(jerseyParameter, getSyntheticQualifierFor(jerseyParameter), false));
}
}
}
}
}
else {
// a method *not* annotated with @Inject -- here we only deal with
// setter methods with a JAX-RS "qualifier" (Context, QueryParam, etc.)
// on the method itself
if (isSetterMethod(method)) {
boolean methodHasEncodedAnnotation = method.isAnnotationPresent(Encoded.class);
for (Annotation annotation : method.getAnnotations()) {
Set discovered = discoveredParameterMap.get(annotation.annotationType());
if (discovered != null) {
if (knownParameterQualifiers.contains(annotation.annotationType())) {
mustPatch = true;
setterMethodsWithoutInject.add(method);
for (AnnotatedParameter super T> parameter : method.getParameters()) {
boolean encoded = parameter.isAnnotationPresent(Encoded.class) || methodHasEncodedAnnotation || classHasEncodedAnnotation;
DefaultValue defaultValue = parameter.getAnnotation(DefaultValue.class);
if (defaultValue == null) {
defaultValue = method.getAnnotation(DefaultValue.class);
}
DiscoveredParameter jerseyParameter = new DiscoveredParameter(annotation, parameter.getBaseType(), defaultValue, encoded);
discovered.add(jerseyParameter);
LOGGER.fine(" recorded " + jerseyParameter);
SyntheticQualifier syntheticQualifier = getSyntheticQualifierFor(jerseyParameter);
// if there is no synthetic qualifier, add to the parameter the annotation that was on the method itself
Annotation addedAnnotation = syntheticQualifier == null ? annotation : null;
parameterToPatchInfoMap.put(parameter, new PatchInformation(jerseyParameter, syntheticQualifier, addedAnnotation, false));
}
break;
}
}
}
}
}
return mustPatch;
}
private boolean isSetterMethod(AnnotatedMethod method) {
Method javaMethod = method.getJavaMember();
if ((javaMethod.getModifiers() & Modifier.PUBLIC) != 0 &&
(javaMethod.getReturnType() == Void.TYPE) &&
(javaMethod.getName().startsWith("set"))) {
List> parameters = method.getParameters();
if (parameters.size() == 1) {
return true;
}
}
return false;
}
private boolean processAnnotatedField(AnnotatedField super T> field,
Class token,
boolean classHasEncodedAnnotation,
Map, PatchInformation> fieldToPatchInfoMap) {
boolean mustPatch = false;
for (Annotation annotation : field.getAnnotations()) {
if (knownParameterQualifiers.contains(annotation.annotationType())) {
boolean mustAddInjectAnnotation = !field.isAnnotationPresent(Inject.class);
if (field.isAnnotationPresent(Encoded.class) ||
classHasEncodedAnnotation ||
mustAddInjectAnnotation ||
field.isAnnotationPresent(DefaultValue.class)) {
mustPatch = true;
}
Set discovered = discoveredParameterMap.get(annotation.annotationType());
if (discovered != null) {
boolean encoded = field.isAnnotationPresent(Encoded.class) || classHasEncodedAnnotation;
DefaultValue defaultValue = field.getAnnotation(DefaultValue.class);
DiscoveredParameter parameter = new DiscoveredParameter(annotation, field.getBaseType(), defaultValue, encoded);
discovered.add(parameter);
LOGGER.fine(" recorded " + parameter);
fieldToPatchInfoMap.put(field, new PatchInformation(parameter, getSyntheticQualifierFor(parameter), mustAddInjectAnnotation));
}
}
}
return mustPatch;
}
private void patchAnnotatedCallable(AnnotatedCallable super T> callable,
AnnotatedCallableImpl newCallable,
Map, PatchInformation> parameterToPatchInfoMap) {
List> newParams = new ArrayList>();
for (AnnotatedParameter super T> parameter : callable.getParameters()) {
PatchInformation patchInfo = parameterToPatchInfoMap.get(parameter);
if (patchInfo != null) {
Set annotations = new HashSet();
// in reality, this cannot happen
if (patchInfo.mustAddInject()) {
annotations.add(injectAnnotationLiteral);
}
if (patchInfo.getSyntheticQualifier() != null) {
annotations.add(patchInfo.getSyntheticQualifier());
Annotation skippedQualifier = patchInfo.getParameter().getAnnotation();
for (Annotation annotation : parameter.getAnnotations()) {
if (annotation != skippedQualifier) {
annotations.add(annotation);
}
}
}
else {
annotations.addAll(parameter.getAnnotations());
}
if (patchInfo.getAnnotation() != null) {
annotations.add(patchInfo.getAnnotation());
}
newParams.add(new AnnotatedParameterImpl(parameter, annotations, newCallable));
}
else {
newParams.add(new AnnotatedParameterImpl(parameter, newCallable));
}
}
newCallable.setParameters(newParams);
}
private void copyParametersOfAnnotatedCallable(AnnotatedCallable super T> callable, AnnotatedCallableImpl newCallable) {
// copy and reparent all the parameters
List> newParams = new ArrayList>();
for (AnnotatedParameter super T> parameter : callable.getParameters()) {
newParams.add(new AnnotatedParameterImpl(parameter, newCallable));
}
newCallable.setParameters(newParams);
}
private SyntheticQualifier getSyntheticQualifierFor(DiscoveredParameter parameter) {
SyntheticQualifier result = syntheticQualifierMap.get(parameter);
if (result == null) {
// only create a synthetic qualifier if we're dealing with @DefaultValue
// or @Encoded; this way the application can still use vanilla param
// annotations as qualifiers
if (parameter.isEncoded() || parameter.getDefaultValue() != null) {
result = new SyntheticQualifierAnnotationImpl(nextSyntheticQualifierValue++);
syntheticQualifierMap.put(parameter, result);
LOGGER.fine(" created synthetic qualifier " + result);
}
}
return result;
}
// taken from ReflectionHelper
private static Class getClassOfType(Type type) {
if (type instanceof Class) {
return (Class)type;
} else if (type instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType)type;
Type t = arrayType.getGenericComponentType();
if (t instanceof Class) {
Class c = (Class)t;
try {
// TODO is there a better way to get the Class object
// representing an array
Object o = Array.newInstance(c, 0);
return o.getClass();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
} else if (type instanceof ParameterizedType) {
ParameterizedType subType = (ParameterizedType)type;
Type t = subType.getRawType();
if (t instanceof Class) {
return (Class)t;
}
}
return null;
}
void processInjectionTarget(@Observes ProcessInjectionTarget event) {
LOGGER.fine("Handling ProcessInjectionTarget event for " + event.getAnnotatedType().getJavaClass().getName());
}
/*
void processBean(@Observes ProcessBean> event) {
LOGGER.fine("Handling ProcessBean event for " + event.getBean().getBeanClass().getName());
}
*/
void processManagedBean(@Observes ProcessManagedBean> event) {
LOGGER.fine("Handling ProcessManagedBean event for " + event.getBean().getBeanClass().getName());
// TODO - here we should check that all the rules have been followed
// and call addDefinitionError for each problem we encountered
Bean> bean = event.getBean();
for (InjectionPoint injectionPoint : bean.getInjectionPoints()) {
StringBuilder sb = new StringBuilder();
sb.append(" found injection point ");
sb.append(injectionPoint.getType());
for (Annotation annotation : injectionPoint.getQualifiers()) {
sb.append(" ");
sb.append(annotation);
}
LOGGER.fine(sb.toString());
}
}
void afterBeanDiscovery(@Observes AfterBeanDiscovery event) {
LOGGER.fine("Handling AfterBeanDiscovery event");
addPredefinedContextBeans(event);
// finally define beans for all qualifiers we discovered
BeanGenerator beanGenerator = new BeanGenerator("com/sun/jersey/server/impl/cdi/generated/Bean");
for (Set parameters : discoveredParameterMap.values()) {
for (DiscoveredParameter parameter : parameters) {
Annotation annotation = parameter.getAnnotation();
Class> klass = getClassOfType(parameter.getType());
if (annotation.annotationType() == Context.class &&
staticallyDefinedContextBeans.contains(klass) &&
!parameter.isEncoded() &&
parameter.getDefaultValue() == null) {
continue;
}
SyntheticQualifier syntheticQualifier = syntheticQualifierMap.get(parameter);
Annotation theQualifier = syntheticQualifier != null ? syntheticQualifier : annotation;
Set annotations = new HashSet();
annotations.add(theQualifier);
// TODO - here we pass a single annotation as the second argument,
// i.e. the qualifier itself, but to be true to Jersey semantics we
// should pass in all the annotations that were on the original program
// element.
// The problem here is that (1) we don't have the original program
// element any more and (2) a single DiscoveredParameter may have
// been encountered in multiple places with different annotations
Parameter jerseyParameter = new Parameter(
new Annotation[]{ annotation },
annotation,
paramQualifiersMap.get(annotation.annotationType()),
parameter.getValue(),
parameter.getType(),
klass,
parameter.isEncoded(),
(parameter.getDefaultValue() == null ? null : parameter.getDefaultValue().value()));
Class> beanClass = beanGenerator.createBeanClass();
ParameterBean bean = new ParameterBean(beanClass, parameter.getType(), annotations, parameter, jerseyParameter);
toBeInitializedLater.add(bean);
event.addBean(bean);
LOGGER.fine("Added bean for parameter " + parameter + " and qualifier " + theQualifier);
}
}
}
/*
* Adds a CDI bean for each @Context type we support out of the box
*/
private void addPredefinedContextBeans(AfterBeanDiscovery event) {
// all standard types first
// @Context Application
event.addBean(new PredefinedBean(Application.class, contextAnnotationLiteral));
// @Context HttpHeaders
event.addBean(new PredefinedBean(HttpHeaders.class, contextAnnotationLiteral));
// @Context Providers
event.addBean(new PredefinedBean(Providers.class, contextAnnotationLiteral));
// @Context Request
event.addBean(new PredefinedBean(Request.class, contextAnnotationLiteral));
// @Context SecurityContext
event.addBean(new PredefinedBean(SecurityContext.class, contextAnnotationLiteral));
// @Context UriInfo
event.addBean(new PredefinedBean(UriInfo.class, contextAnnotationLiteral));
// now the Jersey extensions
// @Context ExceptionMapperContext
event.addBean(new PredefinedBean(ExceptionMapperContext.class, contextAnnotationLiteral));
// @Context ExtendedUriInfo
event.addBean(new PredefinedBean(ExtendedUriInfo.class, contextAnnotationLiteral));
// @Context FeaturesAndProperties
event.addBean(new PredefinedBean(FeaturesAndProperties.class, contextAnnotationLiteral));
// @Context HttpContext
event.addBean(new PredefinedBean(HttpContext.class, contextAnnotationLiteral));
// @Context HttpRequestContext
event.addBean(new PredefinedBean(HttpRequestContext.class, contextAnnotationLiteral));
// @Context HttpResponseContext
event.addBean(new PredefinedBean(HttpResponseContext.class, contextAnnotationLiteral));
// @Context MessageBodyWorkers
event.addBean(new PredefinedBean(MessageBodyWorkers.class, contextAnnotationLiteral));
// @Context ResourceContext
event.addBean(new PredefinedBean(ResourceContext.class, contextAnnotationLiteral));
// @Context WebApplication
event.addBean(new ProviderBasedBean(WebApplication.class, new Provider() {
public WebApplication get() {
return lookupWebApplication();
}
}, contextAnnotationLiteral));
}
private WebApplication lookupWebApplication() {
return lookupWebApplication(Thread.currentThread().getContextClassLoader());
}
private WebApplication lookupWebApplication(final ClassLoader cl) {
return webApplications.get(cl);
}
void setWebApplication(WebApplication wa) {
webApplications.put(Thread.currentThread().getContextClassLoader(), wa);
}
WebApplication getWebApplication() {
return lookupWebApplication();
}
void setResourceConfig(ResourceConfig rc) {
resourceConfig = rc;
}
ResourceConfig getResourceConfig() {
return resourceConfig;
}
/*
* Called after the WebApplication and ResourceConfig have been set,
* i.e. when Jersey is in a somewhat initialized state.
*
* By contrast, all the CDI driven code earlier in this source file
* runs before Jersey gets a chance to initialize itself.
*/
void lateInitialize() {
try {
for (InitializedLater object : toBeInitializedLater) {
object.later();
}
}
finally {
// clear the JNDI reference as soon as possible
if (!lookupExtensionInBeanManager) {
try {
InitialContext ic = InitialContextHelper.getInitialContext();
if (ic != null) {
lookupJerseyConfigJNDIContext(ic).unbind(JNDI_CDIEXTENSION_NAME);
}
} catch (NamingException ex) {
throw new RuntimeException(ex);
}
}
}
}
/*
* Constructs an object by delegating to the ServerInjectableProviderFactory of the WebApplication
*/
class PredefinedBean extends AbstractBean {
private Annotation qualifier;
public PredefinedBean(Class klass, Annotation qualifier) {
super(klass, qualifier);
this.qualifier = qualifier;
}
@Override
public T create(CreationalContext creationalContext) {
Injectable injectable = lookupWebApplication().getServerInjectableProviderFactory().
getInjectable(qualifier.annotationType(), null, qualifier, getBeanClass(), ComponentScope.Singleton);
if (injectable == null) {
Errors.error("No injectable for " + getBeanClass().getName());
return null;
}
return injectable.getValue();
}
}
/*
* Constructs an object by delegating to the Injectable for a Jersey parameter
*/
class ParameterBean extends AbstractBean implements InitializedLater {
private final DiscoveredParameter discoveredParameter;
private final Parameter parameter;
private final Map> injectables = new ConcurrentHashMap>();
private final Map processed = new ConcurrentHashMap();
public ParameterBean(Class> klass, Type type, Set qualifiers,
DiscoveredParameter discoveredParameter, Parameter parameter) {
super(klass, type, qualifiers);
this.discoveredParameter = discoveredParameter;
this.parameter = parameter;
}
@Override
public void later() {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (injectables.containsKey(contextClassLoader)) {
return;
}
if (processed.containsKey(contextClassLoader))
return;
processed.put(contextClassLoader, true);
boolean registered = lookupWebApplication(contextClassLoader).getServerInjectableProviderFactory().
isParameterTypeRegistered(parameter);
if (!registered) {
Errors.error("Parameter type not registered " + discoveredParameter);
}
Injectable injectable = lookupWebApplication(contextClassLoader).getServerInjectableProviderFactory().
getInjectable(parameter, ComponentScope.PerRequest);
if (injectable == null) {
Errors.error("No injectable for parameter " + discoveredParameter);
} else {
injectables.put(contextClassLoader, injectable);
}
}
@Override
public T create(CreationalContext creationalContext) {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (!injectables.containsKey(contextClassLoader)) {
later();
if (!injectables.containsKey(contextClassLoader)) {
return null;
}
}
final Injectable injectable = injectables.get(contextClassLoader);
try {
return injectable.getValue();
} catch (IllegalStateException e) {
if (injectable instanceof AbstractHttpContextInjectable) {
return (T)((AbstractHttpContextInjectable)injectables).getValue(lookupWebApplication(contextClassLoader).getThreadLocalHttpContext());
}
else {
throw e;
}
}
}
}
}