user2771655 user2771655 - 1 month ago 17
Java Question

Use CDI to load Beans dynamically - EJB3

I am using EJB3 in my application and I want to know can I

inject
beans dynamically at runtime based on some parameter ?

Because I have a
@Local
interface which extends several beans which works in different manner. So at run time I want to load each bean depend on the logic. So far I used usual
JNDI lookup
But I would like to do it with
@Inject
. Is there a way that I can inject the bean dynamically?

Here is my existing
lookup
code. I pass the bean name as
jndi
here and build the instance dynamically.

IService bean = (IService) initialContext.lookup(jndi+"Bean/local");
bean.initializeTimer(firstDate, period, request);





UPDATE



I have three beans for the moment.

first bean is SingleServiceBean

@Stateless
public class SingleServiceBean implements IService{

@Override
public void doSomething (){
log.debug("inside do something");
}

private final Log log = LogFactory.getLog(SingleServiceBean.class.getName());
}


Second bean is PeriodicService

@Stateless
public class PeriodicServiceBean implements IService{

@Override
public void doSomething (){
log.debug("inside do something");
}

private final Log log = LogFactory.getLog(PeriodicServiceBean.class.getName());
}


Third bean

@Stateless
public class AsyncServiceBean implements IService{

@Override
public void doSomething (){
log.debug("inside do something");
}

private final Log log = LogFactory.getLog(AsyncServiceBean.class.getName());
}





2nd Update



Qualifier :

@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Services {
String type();

}


AnnotationLiteral

public class ServiceQualifier extends AnnotationLiteral<Services> implements Services{

private static final long serialVersionUID = 6471734834552932687L;
private String type;

public String TypeQualifier(String t) {
this.type = t;
return type;
}

public String type() {
return type;
}
}


And new bean class like below

@Services(type = "SingleService" )
@Stateless
public class SingleServiceBean implements IService{
.....
}


then I add the below lines in my bean caller class

@Inject
private Instance<IService> iServiceInstance;

public void someMethod() {

// this line gives me error by red underline in .select(...)
IService service = iServiceInstance.select(new ServiceQualifier().TypeQualifier("SingleService")).get();
}


Error I get in eclipse : "The method select(Annotation...) in the type Instance is not applicable for the arguments (String)"

Answer

Assuming you're using local interfaces, you would need to use a qualifier in CDI (rather than JNDI location). On your impl, do something like this:

@Local
@Stateless
@MyQualifier("someValue")
public class MyServiceOne implements IService {
    ...
}

Where MyQualifier could have any value - even the JNDI name. Then when you want to resolve it, do this:

@Inject
private Instance<IService> iServiceInstance;

...

IService service = iServiceInstance.select(new MyQualifierLiteral("someValue")).get();

MyQualifierLiteral here is an AnnotationLiteral that implements MyQualifier. This is what it should look like:

import javax.enterprise.util.AnnotationLiteral;

public class MyQualifierLiteral extends AnnotationLiteral<MyQualifier> implements MyQualifier {

    private final String value;

    public MyQualifierLiteral(final String value) {
        this.value = value;
    }

    @Override
    public String value() {
        return this.value;
    }

}