Skip to content

Commit

Permalink
Better @Inject support. For servlet classes, a qualifier is used.
Browse files Browse the repository at this point in the history
Signed-off-by: jansupol <[email protected]>
  • Loading branch information
jansupol committed Oct 25, 2022
1 parent 0c2f28a commit b06385c
Show file tree
Hide file tree
Showing 14 changed files with 619 additions and 55 deletions.
8 changes: 8 additions & 0 deletions ext/cdi/jersey-cdi-rs-inject/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${servlet4.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>

<build>
Expand All @@ -65,6 +72,7 @@
<instructions>
<Import-Package>
${cdi.osgi.version},
javax.servlet.*;version="[2.4,5.0)";resolution:=optional,
*
</Import-Package>
</instructions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -22,12 +22,23 @@
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
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.InjectionTarget;
import javax.enterprise.inject.spi.InjectionTargetFactory;
import javax.enterprise.inject.spi.PassivationCapable;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.inject.Singleton;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.container.ResourceInfo;
Expand All @@ -40,9 +51,13 @@
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Providers;
import javax.ws.rs.sse.Sse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
* <p>
Expand All @@ -57,7 +72,12 @@
*/
@SuppressWarnings("unused")
class InjectExtension implements Extension {
private void processAnnotatedType(@Observes ProcessAnnotatedType<?> processAnnotatedType, BeanManager beanManager) {
private static final Class<?> WEB_CONFIG_CLASS =
AccessController.doPrivileged(ReflectionHelper.classForNamePA("org.glassfish.jersey.servlet.WebConfig"));
private AnnotatedType<ServletReferenceProducer> interceptorAnnotatedType;

private void processAnnotatedType(@Observes ProcessAnnotatedType<?> processAnnotatedType,
BeanManager beanManager) {
final Class<?> baseClass = (Class<?>) processAnnotatedType.getAnnotatedType().getBaseType();
if (Application.class.isAssignableFrom(baseClass) && Configuration.class.isAssignableFrom(baseClass)) {
if (!baseClass.isAnnotationPresent(Alternative.class)) {
Expand All @@ -67,8 +87,103 @@ private void processAnnotatedType(@Observes ProcessAnnotatedType<?> processAnnot
}

private void beforeDiscoveryObserver(@Observes final BeforeBeanDiscovery bbf, final BeanManager beanManager) {
final CdiComponentProvider cdiComponentProvider = beanManager.getExtension(CdiComponentProvider.class);
cdiComponentProvider.addHK2DepenendencyCheck(InjectExtension::isHK2Dependency);
if (WEB_CONFIG_CLASS != null) {
interceptorAnnotatedType = beanManager.createAnnotatedType(ServletReferenceProducer.class);
bbf.addAnnotatedType(interceptorAnnotatedType, ServletReferenceProducer.class.getName());
}
CdiComponentProvider.addHK2DepenendencyCheck(InjectExtension::isHK2Dependency);
}

private void afterDiscoveryObserver(@Observes final AfterBeanDiscovery abd, final BeanManager beanManager) {
if (WEB_CONFIG_CLASS != null) {
abd.addBean(new ServletReferenceProducerBean(beanManager));
}
}

@Singleton
private final class ServletReferenceProducerBean implements Bean<ServletReferenceProducer>, PassivationCapable {
private final Set<Annotation> qualifiers = new HashSet<>();
private final Set<Type> types = new HashSet<>(2);
private final InjectionTarget<ServletReferenceProducer> interceptorTarget;
private final String id = UUID.randomUUID().toString();

private ServletReferenceProducerBean(BeanManager beanManager) {
qualifiers.add(new CdiJerseyContextAnnotation());
qualifiers.add(new CdiAnyAnnotation());

types.add(ServletReferenceProducer.class);
types.add(Object.class);

final AnnotatedType<ServletReferenceProducer> interceptorType = interceptorAnnotatedType;
final InjectionTargetFactory<ServletReferenceProducer> injectionTargetFactory =
beanManager.getInjectionTargetFactory(interceptorType);

interceptorTarget = injectionTargetFactory.createInjectionTarget(null);
}
@Override
public Set<Type> getTypes() {
return types;
}

@Override
public Set<Annotation> getQualifiers() {
return qualifiers;
}

@Override
public Class<? extends Annotation> getScope() {
return RequestScoped.class;
}

@Override
public String getName() {
return ServletReferenceProducer.class.getName();
}

@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}

@Override
public boolean isAlternative() {
return false;
}

@Override
public ServletReferenceProducer create(CreationalContext<ServletReferenceProducer> creationalContext) {
final ServletReferenceProducer result = interceptorTarget.produce(creationalContext);
interceptorTarget.inject(result, creationalContext);
interceptorTarget.postConstruct(result);
return result;
}

@Override
public void destroy(ServletReferenceProducer servletProducer,
CreationalContext<ServletReferenceProducer> creationalContext) {
interceptorTarget.preDestroy(servletProducer);
interceptorTarget.dispose(servletProducer);
creationalContext.release();
}

@Override
public Class<?> getBeanClass() {
return ServletReferenceProducer.class;
}

@Override
public Set<InjectionPoint> getInjectionPoints() {
return interceptorTarget.getInjectionPoints();
}

public boolean isNullable() {
return false;
}

@Override
public String getId() {
return id;
}
}

private static final boolean isHK2Dependency(Class<?> clazz) {
Expand All @@ -95,20 +210,17 @@ private static Set<Class<?>> sumNonJerseyBoundInjectables() {
injectables.add(Sse.class);
injectables.add(UriInfo.class);

//Servlet if available
addOptionally("javax.servlet.http.HttpServletRequest", injectables);
addOptionally("javax.servlet.http.HttpServletResponse", injectables);
addOptionally("javax.servlet.ServletConfig", injectables);
addOptionally("javax.servlet.ServletContext", injectables);
addOptionally("javax.servlet.FilterConfig", injectables);

return injectables;
}

private static void addOptionally(String className, Set<Class<?>> set) {
final Class<?> optionalClass = AccessController.doPrivileged(ReflectionHelper.classForNamePA(className));
if (optionalClass != null) {
set.add(optionalClass);
}
private static class CdiJerseyContextAnnotation
extends javax.enterprise.util.AnnotationLiteral<JerseyContext> implements JerseyContext {
private static final long serialVersionUID = 1L;
}

private static class CdiAnyAnnotation
extends javax.enterprise.util.AnnotationLiteral<Any> implements Any {
private static final long serialVersionUID = 1L;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.ext.cdi1x.inject.internal;

import javax.inject.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* <p>
* Qualifier used for injecting the CDI beans using {@code @Inject}. Used only for beans that originate in Specification other
* than Jakarta RESTful Web Services (such as Servlet). Such beans must not be produced with {@code @Default} qualifier so that
* there is no ambiguity for the CDI injection.
* </p>
* <p>
* Jakarta REST Spec. Section 11 demands {@code HttpServletRequest}, {@code HttpServletResponse}, {@code ServletContext},
* {@code ServletConfig}, and {@code FilterConfig} to be available by injections using {@code @Context}. For CDI, these
* servlet classes are available with {@link JerseyContext} qualifier.
* </p>
* <p>
* Note that {@code @Context} injection is not aware of the qualifier and using {@code &#64;Context} in conjuction with
* {@code &#64;JerseyContext} will not work.
* </p>
* <p>
* Can be used as e.g.
*
* <pre>
* &#64;Inject
* &#64;JerseyContext //internal
* HttpServletRequest httpServletRequest;
* </pre>
* or as iterable of all {@code HttpServletRequest} beans
* <pre>
* &#64;Inject
* &#64;Any
* Instance&lt;HttpServletRequest&gt; httpServletRequests;
* </pre>
* </p>
* @since 2.38
*/
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface JerseyContext {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.ext.cdi1x.inject.internal;

import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider;
import org.glassfish.jersey.internal.inject.InjectionManager;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A CDI producer producing servlet beans in a {@code RequestScope}.
*/
class ServletReferenceProducer {
@Inject
InjectionManager injectionManager;

@Inject
BeanManager beanManager;

@Produces
@JerseyContext
@RequestScoped
public HttpServletRequest produceHttpServletRequest() {
return injectionManager().getInstance(HttpServletRequest.class);
}

@Produces
@JerseyContext
@RequestScoped
public HttpServletResponse produceHttpServletResponse() {
return injectionManager().getInstance(HttpServletResponse.class);
}

@Produces
@JerseyContext
@RequestScoped
public ServletContext produceServletContext() {
return injectionManager().getInstance(ServletContext.class);
}

@Produces
@JerseyContext
@RequestScoped
public ServletConfig produceServletConfig() {
return injectionManager().getInstance(ServletConfig.class);
}

@Produces
@JerseyContext
@RequestScoped
public FilterConfig produceFilterConfig() {
return injectionManager().getInstance(FilterConfig.class);
}

private InjectionManager injectionManager() {
InjectionManager injectionManager = beanManager.getExtension(CdiComponentProvider.class).getEffectiveInjectionManager();
if (injectionManager != null && !injectionManager.isShutdown()) {
return injectionManager;
}
return this.injectionManager;
}
}
Loading

0 comments on commit b06385c

Please sign in to comment.