InjectExtension.java
/*
* 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
* 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.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.LazyValue;
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;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Configuration;
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.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>
* A utility class that makes sure {@code @Inject} can be used instead of {@code @Context} for the Jakarta REST API classes
* and interfaces, such as for {@code Configuration}, or {@code Providers}.
* </p>
* <p>
* Note that {@code ContextResolver} can be injected using {@code @Context}, but the Jakarta REST specification does not require
* the implementation to be capable of doing so. Since {@code ContextResolver} is parametrized type, the injection using CDI's
* {@Inject} is not supported. The {@code ContextResolver} can be obtained from {@code Providers}.
* </p>
*/
@SuppressWarnings("unused")
class InjectExtension implements Extension {
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)) {
processAnnotatedType.veto(); // Filter bean annotated ResourceConfig
}
}
}
private void beforeDiscoveryObserver(@Observes final BeforeBeanDiscovery bbf, final BeanManager beanManager) {
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) {
return JERSEY_BOUND_INJECTABLES.get().contains(clazz);
}
private static final LazyValue<Set<Class<?>>> JERSEY_BOUND_INJECTABLES
= Values.lazy((Value<Set<Class<?>>>) () -> sumNonJerseyBoundInjectables());
private static Set<Class<?>> sumNonJerseyBoundInjectables() {
final Set<Class<?>> injectables = new HashSet<>();
//JAX-RS
injectables.add(Application.class);
injectables.add(Configuration.class);
injectables.add(ContainerRequestContext.class);
injectables.add(HttpHeaders.class);
injectables.add(ParamConverterProvider.class);
injectables.add(Providers.class);
injectables.add(Request.class);
injectables.add(ResourceContext.class);
injectables.add(ResourceInfo.class);
injectables.add(SecurityContext.class);
injectables.add(Sse.class);
injectables.add(UriInfo.class);
return injectables;
}
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;
}
}