CdiInjectionManager.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.inject.weld.internal.managed;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.InjectionTargetFactory;
import javax.enterprise.inject.spi.Unmanaged;
import javax.inject.Singleton;
import javax.ws.rs.RuntimeType;
import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding;
import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding;
import org.glassfish.jersey.inject.weld.internal.bean.JerseyBean;
import org.glassfish.jersey.inject.weld.internal.inject.MatchableBinding;
import org.glassfish.jersey.internal.inject.Binder;
import org.glassfish.jersey.internal.inject.Binding;
import org.glassfish.jersey.internal.inject.Bindings;
import org.glassfish.jersey.internal.inject.ClassBinding;
import org.glassfish.jersey.internal.inject.ForeignDescriptor;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.ServiceHolder;
import org.glassfish.jersey.internal.inject.ServiceHolderImpl;
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
/**
* Implementation of {@link InjectionManager} used on the server side.
*/
@Singleton
public class CdiInjectionManager implements InjectionManager {
private final BeanManager beanManager;
private final Binder bindings;
private boolean isCompleted = false;
Set<Class<?>> managedBeans;
// Keeps all binders and bindings added to the InjectionManager during the bootstrap.
public CdiInjectionManager(BeanManager beanManager, Binder bindings) {
this.beanManager = beanManager;
this.bindings = bindings;
}
@Override
public void register(Binding binding) {
if (isManagedClass(binding)) {
return;
}
if (InstanceBinding.class.isInstance(binding)) {
final Collection<Binding> preBindings = bindings.getBindings();
MatchableBinding.Matching<InitializableInstanceBinding> matching = MatchableBinding.Matching.noneMatching();
for (Binding preBinding : preBindings) {
if (InitializableInstanceBinding.class.isInstance(preBinding)) {
matching = matching.better(((InitializableInstanceBinding) preBinding).matches((InstanceBinding) binding));
if (matching.isBest()) {
break;
}
}
}
if (matching.matches()) {
matching.getBinding().init(((InstanceBinding) binding).getService());
} else if (findClassBinding(binding.getImplementationType()) == null) {
throw new IllegalStateException("Not initialized " + ((InstanceBinding<?>) binding).getService());
}
} else if (SupplierInstanceBinding.class.isInstance(binding)) {
final Collection<Binding> preBindings = bindings.getBindings();
MatchableBinding.Matching<InitializableSupplierInstanceBinding> matching = MatchableBinding.Matching.noneMatching();
for (Binding preBinding : preBindings) {
if (InitializableSupplierInstanceBinding.class.isInstance(preBinding)) {
matching = matching.better(
((InitializableSupplierInstanceBinding) preBinding).matches((SupplierInstanceBinding) binding));
if (matching.isBest()) {
break;
}
}
}
if (matching.matches()) {
matching.getBinding().init(((SupplierInstanceBinding) binding).getSupplier());
} else {
throw new IllegalStateException("Not initialized " + ((SupplierInstanceBinding<?>) binding).getSupplier());
}
} else if (ClassBinding.class.isInstance(binding)) {
if (findClassBinding(binding.getImplementationType()) == null) {
// final Collection<Binding> preBindings = bindings.getBindings();
// boolean found = false;
// for (Binding preBinding : preBindings) {
// if (ClassBinding.class.isInstance(preBinding)
// && ((ClassBinding) preBinding).getImplementationType()
// .equals(((ClassBinding) binding).getImplementationType())) {
// found = true;
// break;
// }
// }
// if (!found) {
throw new IllegalStateException("ClassBinding for " + binding.getImplementationType() + " not preregistered");
}
}
}
private <T> ClassBinding<T> findClassBinding(Class<T> implementationType) {
final Collection<Binding> preBindings = bindings.getBindings();
boolean found = false;
for (Binding preBinding : preBindings) {
if (ClassBinding.class.isInstance(preBinding)
&& ((ClassBinding) preBinding).getImplementationType().equals(implementationType)) {
return (ClassBinding<T>) preBinding;
}
}
return null;
}
private boolean isManagedClass(Binding binding) {
return managedBeans != null
&& binding.getImplementationType() != null
&& (managedBeans.contains(binding.getImplementationType())
|| (managedBeans.contains(binding.getImplementationType().getSuperclass())));
}
@Override
public void register(Iterable<Binding> bindings) {
for (Binding binding : bindings) {
register(binding);
}
}
@Override
public void register(Binder binder) {
for (Binding binding : Bindings.getBindings(this, binder)) {
register(binding);
}
}
@Override
public void register(Object provider) throws IllegalArgumentException {
throw new IllegalArgumentException(LocalizationMessages.CDI_2_PROVIDER_NOT_REGISTRABLE(provider.getClass()));
}
@Override
public boolean isRegistrable(Class<?> clazz) {
return false;
}
@Override
public <T> T create(Class<T> createMe) {
Unmanaged.UnmanagedInstance<T> unmanaged = new Unmanaged<>(createMe).newInstance();
return unmanaged.produce().get();
}
@Override
public <T> T createAndInitialize(Class<T> createMe) {
Unmanaged.UnmanagedInstance<T> unmanaged = new Unmanaged<>(createMe).newInstance();
return unmanaged.produce()
.inject()
.postConstruct()
.get();
}
@Override
@SuppressWarnings("unchecked")
public <T> List<ServiceHolder<T>> getAllServiceHolders(Class<T> contractOrImpl, Annotation... qualifiers) {
List<ServiceHolder<T>> result = new ArrayList<>();
for (Bean<?> bean : beanManager.getBeans(contractOrImpl, qualifiers)) {
if (!isRuntimeTypeBean(bean)) {
continue;
}
CreationalContext<?> ctx = createCreationalContext(bean);
T reference = (T) beanManager.getReference(bean, contractOrImpl, ctx);
int rank = 1;
if (bean instanceof JerseyBean) {
rank = ((JerseyBean) bean).getRank();
}
result.add(new ServiceHolderImpl<>(reference, (Class<T>) bean.getBeanClass(), bean.getTypes(), rank));
}
return result;
}
@Override
public <T> T getInstance(Class<T> contractOrImpl, Annotation... qualifiers) {
return getInstanceInternal(contractOrImpl, qualifiers);
}
@Override
public <T> T getInstance(Class<T> contractOrImpl) {
return getInstanceInternal(contractOrImpl);
}
@Override
public <T> T getInstance(Type contractOrImpl) {
return getInstanceInternal(contractOrImpl);
}
@SuppressWarnings("unchecked")
protected <T> T getInstanceInternal(Type contractOrImpl, Annotation... qualifiers) {
// if (contractOrImpl.getTypeName().contains("HelloResource")) {
// T t = (T) CDI.current().select((Class) contractOrImpl, qualifiers).get();
// try {
// System.out.println(t.getClass().getMethod("hello").invoke(t));
//// t.getClass().getMethod("hello").invoke(t);
// } catch (IllegalAccessException e) {
// e.printStackTrace();
// } catch (InvocationTargetException e) {
// e.printStackTrace();
// } catch (NoSuchMethodException e) {
// e.printStackTrace();
// }
// return t;
// }
Set<Bean<?>> beans = beanManager.getBeans(contractOrImpl, qualifiers);
if (beans.isEmpty()) {
return null; //ScopesTest
}
final Iterator<?> beansIterator = beans.iterator();
Bean<?> bean = (Bean<?>) beansIterator.next();
while (beansIterator.hasNext() && !JerseyBean.class.isInstance(bean) && !isRuntimeTypeBean(bean)) {
bean = (Bean<?>) beansIterator.next(); // prefer Jersey binding
}
CreationalContext<T> ctx = createCreationalContext((Bean<T>) bean);
return (T) beanManager.getReference(bean, contractOrImpl, ctx);
}
@Override
@SuppressWarnings("unchecked")
public Object getInstance(ForeignDescriptor foreignDescriptor) {
Bean bean = (Bean) foreignDescriptor.get();
CreationalContext ctx = createCreationalContext(bean);
return bean.create(ctx);
}
@Override
@SuppressWarnings("unchecked")
public ForeignDescriptor createForeignDescriptor(Binding binding) {
Class<?> clazz;
if (ClassBinding.class.isAssignableFrom(binding.getClass())) {
clazz = ((ClassBinding<?>) binding).getService();
} else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) {
clazz = ((InstanceBinding<?>) binding).getService().getClass();
} else {
throw new RuntimeException(
org.glassfish.jersey.internal.LocalizationMessages
.UNKNOWN_DESCRIPTOR_TYPE(binding.getClass().getSimpleName()));
}
Set<Bean<?>> beans = beanManager.getBeans(clazz);
if (beans.isEmpty()) {
return null;
}
Bean bean = beans.iterator().next();
CreationalContext ctx = createCreationalContext(bean);
return ForeignDescriptor.wrap(bean, instance -> bean.destroy(instance, ctx));
}
@Override
@SuppressWarnings("unchecked")
public <T> List<T> getAllInstances(Type contractOrImpl) {
List<T> result = new ArrayList<>();
for (Bean<?> bean : beanManager.getBeans(contractOrImpl)) {
CreationalContext<?> ctx = createCreationalContext(bean);
Object reference = beanManager.getReference(bean, contractOrImpl, ctx);
result.add((T) reference);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public void inject(Object instance) {
CreationalContext creationalContext = createCreationalContext(null);
AnnotatedType annotatedType = beanManager.createAnnotatedType((Class) instance.getClass());
InjectionTargetFactory injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType);
InjectionTarget injectionTarget = injectionTargetFactory.createInjectionTarget(null);
injectionTarget.inject(instance, creationalContext);
}
@Override
public void preDestroy(Object preDestroyMe) {
}
@Override
public void completeRegistration() throws IllegalStateException {
// bindings.bind(Bindings.service(this).to(InjectionManager.class));
// bindings.install(new ContextInjectionResolverImpl.Binder(this::getBeanManager));
//
// this.beanManager = new DefaultBeanManagerProvider().getBeanManager();
// beanManager = new DefaultBeanManagerProvider().getBeanManager();
//
// AbstractBinder masterBinder = beanManager.getExtension(SeBeanRegisterExtension.class).bindings;
// masterBinder.getBindings().addAll(bindings.getBindings());
// bindings.bind(Bindings.service(this).to(InjectionManager.class));
// bindings.install(new ContextInjectionResolverImpl.Binder(this::getBeanManager));
if (!isCompleted) {
register(Bindings.service(this).to(InjectionManager.class));
isCompleted = false;
}
}
@Override
public void shutdown() {
}
@Override
public boolean isShutdown() {
return false;
}
protected Binder getBindings() {
return bindings;
}
protected BeanManager getBeanManager() {
return beanManager;
}
protected <T> CreationalContext<T> createCreationalContext(Bean<T> bean) {
return (CreationalContext<T>) beanManager.createCreationalContext(bean);
}
// protected void registerInjectionResolver(InjectionResolverBinding<?> injectionResolverBinding) {
// // beanManager.getExtension(BinderRegisterExtension.class).addInjectionResolver(injectionResolverBinding.getResolver());
// }
/**
* Identifies Jersey beans that are from different runtime (CLIENT vs SERVER). Used to exclude Jersey beans of incorrect
* {@link RuntimeType}.
* @param bean the given CDI bean.
* @return true iff the given bean is not a Jersey Bean or the Jersey Bean is of the proper {@code RuntimeType}.
*/
protected boolean isRuntimeTypeBean(Bean<?> bean) {
return !JerseyBean.class.isInstance(bean) || ((JerseyBean) bean).getRutimeType() == RuntimeType.SERVER;
}
@Override
public void inject(Object injectMe, String classAnalyzer) {
// TODO: Used only in legacy CDI integration.
throw new UnsupportedOperationException();
}
@Override
public <T> T getInstance(Class<T> contractOrImpl, String classAnalyzer) {
// TODO: Used only in legacy CDI integration.
throw new UnsupportedOperationException();
}
}