Search code examples
javacdi

How to use CDI managed bean from parent class path


I'm developing a library with CDI. This works fine. But when I try to use this library as an included Jar in a project, CDI complains that it can't resolve dependencies on managed Beans defined in the parent Project.

MyProject - MyLib.jar - MyManagedBean

So MyLib needs to inject a bean annotated with a stereotype @foo. This stereotype is applied on MyManagedBean.

@foo
public class MyManagedBean {
    //...
}

I added beans.xml to my parent project too. But it's like there is 2 different CDI Container and MyLib can't access MyProject.

Any suggestion?


Solution

  • I was able to access the parent class file by using the CDI extension. Documentation and reflection.

    This is the code i use :

    public class ConfigExtension implements Extension {
    
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
        Reflections reflections = new Reflections("");
        Set<Class<?>> types = reflections.getTypesAnnotatedWith(Tester.class);
        types.remove(info.scandi.fusion.cucumber.Tester.class);
        types.addAll(reflections.getTypesAnnotatedWith(Driver.class));
        types.addAll(reflections.getTypesAnnotatedWith(Worker.class));
    
        types.forEach(type -> {
            abd.addBean(new FusionBean((Class<T>) type, bm));
        });
    }
    
    }
    
    public class FusionBean<T> implements Bean<T>, Serializable, PassivationCapable {
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private InjectionTarget<T> it;
    private Class<T> bean;
    private BeanManager bm;
    
    public FusionBean(Class<T> workerClass, BeanManager bm) {
        this.bm = bm;
        bean = workerClass;
        AnnotatedType<T> at = bm.createAnnotatedType(bean);
        // use this to instantiate the class and inject dependencies
        it = bm.createInjectionTarget(at);
    }
    
    @Override
    public T create(CreationalContext<T> creationalContext) {
        T instance = it.produce(creationalContext);
        it.inject(instance, creationalContext);
        it.postConstruct(instance);
        return instance;
    }
    
    @Override
    public void destroy(T instance, CreationalContext<T> creationalContext) {
        it.preDestroy(instance);
        it.dispose(instance);
        creationalContext.release();
    }
    
    @Override
    public Set<Type> getTypes() {
        Set<Type> types = new HashSet<>();
        //Use Apache Common Lang to get all Interfaces and Superclasses
        types.addAll(ClassUtils.getAllInterfaces(bean));
        types.addAll(ClassUtils.getAllSuperclasses(bean));
        return types;
    }
    
    @Override
    public Set<Annotation> getQualifiers() {
        Set<Annotation> annotations = new HashSet<>();
        for (int i = 0; i < bean.getAnnotations().length; i++) {
            Class<? extends Annotation> possibleQualifier = bean.getAnnotations()[i].annotationType();
            if (bm.isQualifier(possibleQualifier)) {
                annotations.add(bean.getAnnotations()[i]);
            }
        }
        return annotations;
    }
    
    @Override
    public Class<? extends Annotation> getScope() {
        for (int i = 0; i < bean.getAnnotations().length; i++) {
            Class<? extends Annotation> possibleScope = bean.getAnnotations()[i].annotationType();
            if (bm.isStereotype(possibleScope)) {
                for (Annotation annotation : possibleScope.getAnnotations()) {
                    if (bm.isScope(annotation.annotationType())) {
                        return annotation.annotationType();
                    }
                }
            }
        }
        return null;
    }
    
    @Override
    public String getName() {
        return bean.getName();
    }
    
    @Override
    public Set<Class<? extends Annotation>> getStereotypes() {
        Set<Class<? extends Annotation>> stereotypes = new HashSet<>();
        for (int i = 0; i < bean.getAnnotations().length; i++) {
            Class<? extends Annotation> possibleStereotype = bean.getAnnotations()[i].annotationType();
            if (bm.isStereotype(possibleStereotype)) {
                stereotypes.add(possibleStereotype);
            }
        }
        return stereotypes;
    }
    
    @Override
    public boolean isAlternative() {
        for (int i = 0; i < bean.getAnnotations().length; i++) {
            if (bean.getAnnotations()[i].equals(Alternative.class)) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public Class<?> getBeanClass() {
        return bean.getClass();
    }
    
    @Override
    public Set<InjectionPoint> getInjectionPoints() {
        return it.getInjectionPoints();
    }
    
    @Override
    public boolean isNullable() {
        return false;
    }
    
    @Override
    public String getId() {
        return UUID.randomUUID().toString();
    }
    
    }
    

    The trick is CDI Extension has access to Parent Classpath. Here i use the Reflection api to get Tester, Driver et Worker classes that the user using my framework could specified.