Injecting EJBs from JAR to WAR using CDI

Recently I faced a problem of how to use @Inject annotation in one of the CDI Beans. Our project has a following structure:

  • Business logic: JAR which packs EJB, JPA Entity Classes, …
  • User interface: WAR which packs CDI Beans, images, XHTMLs, …

Technically what I call JAR is actually EJB-JAR, because it packs EJBs, but EJB-JAR is really nothing more than a JAR archive, so let’s call it JAR for simplicity. WAR is responsible for user interface and uses JAR for business logic. So far a pretty standard architecture.

Both JAR and WAR are deployed separately and running on JBoss AS 7.1 (released in 2012, after this JBoss AS was renamed to Wildfly). Our client uses this version, so we have to work with what we have, but overly it’s not a problem.

To use business logic from the JAR we need access to our EJBs which implements it. Our CDI Beans in WAR looks like this:

@Model
public class ViewPortlet implements Serializable {
     @Inject
     TestSessionEJBLocal testBean;
}

@Model annotation is a built-in stereotype (stereotype is annotation that incorporates other annotations) from CDI. It’s for beans that are intended for the model component of the MVC pattern which needs multiple annotation that you don’t want to list every time (readability and laziness). Then we use @Inject for dependency injection of EJB named TestSessionEJBLocal which lays in JAR. For those who are at least familiar with CDI it must look pretty simple.

But after deployment we got following error:

16:45:19,003 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC00001: Failed to start service jboss.deployment.unit."TEST_WEB.war".WeldService: org.jboss.msc.service.StartException in service jboss.deployment.unit."TEST_WEB.war".WeldService: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [TestSessionEJBLocal] with qualifiers [@Default] at injection point [[field] @Inject cz.pfreiberg.test.view.portlet.ViewPortlet.testBean]
    at org.jboss.as.weld.services.WeldService.start(WeldService.java:83)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_79]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_79]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_79]
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [TestSessionEJBLocal] with qualifiers [@Default] at injection point [[field] @Inject cz.pfreiberg.test.view.portlet.ViewPortlet.testBean]
     at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:275)
     at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:244)
     at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:107)
     at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:127)
     at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:346)
     at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:331)
     at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:366)
     at org.jboss.as.weld.WeldContainer.start(WeldContainer.java:83)
     at org.jboss.as.weld.services.WeldService.start(WeldService.java:76)

What the error said? Well, that Weld (Weld is the reference implementation of CDI) couldn’t inject TestSessionEJBLocal to injection point cz.pfreiberg.test.view.portlet.ViewPortlet.testBean. We couldn’t use Weld for injection, it’s simply didn’t see JAR classes from WAR. Other variant was to use only manual JNDI lookup, but I really wanted to use @Inject for dependency injection.

So after a lot of research (special thanks go to Martin Polák), probably the only viable solution is to use separate class (EJBProducer), which uses CDI @Produces annotation and hides JNDI lookup. This class lays in WAR.

Method with @Produces annotation is used every time when you need the type which returning. You can have only one method for one type otherwise error will be thrown during deployment phase. The only problem which remains is that you still have to write JNDI for each bean separately.

import cz.pfreiberg.test.ejb.session.TestSessionEJBLocal;
 
import javax.enterprise.inject.Produces;
 
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
public class EJBProvider {
 
    public EJBProvider() {
    }
 
    /**
     * Now you can use @Inject for this EJB across your application.
     * Example: @Inject TestSessionEJBLocal testEJB;
     */
    @Produces
    public TestSessionEJBLocal getTestSessionEJBLocal() {
        return jndiLookup("java:global/TEST/TestSessionEJB!cz.pfreiberg.test.ejb.session.TestSessionEJBLocal",
                          TestSessionEJBLocal.class);
    }
 
    private <T> T jndiLookup(String name, Class<T> type) {
        try {
            InitialContext ctx = new InitialContext();
            return type.cast(ctx.lookup(name));
        } catch (NamingException e) {
            String errorMessage = "Error during JNDI lookup for " + name;
            throw new RuntimeException(errorMessage, e);
        }
    }
 
}

Now you can @Inject classes from another deployment.

One thought on “Injecting EJBs from JAR to WAR using CDI

  1. Hi, I’ve also found this solution (as creating a new InitialContext is pretty expensive):

    1. You can make a CDI Bean as a Wrapper around “TestSessionEJBLocal”. You don’t need any bean defining annotations here, if bean-discovery-mode is set to “all” in beans.xml:
    public class WrapperCdiBean implements TestSessionEJBLocal{

    @EJB
    private TestSessionEJBLocal delegate;

    // override the methods from TestSessionEJBLocal and delegate them to the injected EJB.

    }

    2. You can skip the EJBProvider. It is no longer necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *