Search code examples
javaejb-3.0jndicdi

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)"


Solution

  • 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 @Any
    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;
        }
    
    }