Daniel Kaplan
Daniel Kaplan

Reputation: 67370

Can a class loaded with @Inject use an @EJB?

TL;DR: Web Service uses an @Injected class, @Injected class uses @EJBs. @EJBs are null. Why are they null and how do I fix this?


I'm using Glassfish 3 and I have a @Stateless @WebService that is @Injecting a class with a @Dependent annotation on it. I'd like this class to be able to use other stateless ejbs as fields like this:

@EJB(name = "ejb/MySessionBean", beanName = "MySessionBean")
private MySessionLocal mySessionLocal;

But, when I try to call this web service, these @EJB fields are null (although the @Dependent class itself seems to be injected into the web service correctly). Is it possible to do what I'm trying to do?

I should add that my Web Service and my EJBs are in an EJB jar in an ear's root. The @Dependent class is inside a jar in the ear's lib/ directory.

UPDATE: I've discovered that the @EJBs work correctly (are not null) if I move the @Dependent class into the same jar as the web service. To me this suggests a classloader issue? An ear's ejb jar can @Inject a class in a "lib/*.jar", but a class in a "lib/*.jar" can't get an @EJB from a ejb jar in the ear's root.

It's still unclear to me if this is by design.

Upvotes: 2

Views: 293

Answers (2)

Daniel Kaplan
Daniel Kaplan

Reputation: 67370

I have found a workaround, though I'd prefer not to have to use it. Its always been the case that I can get a reference to the @EJB by doing this (in my "lib/*.jar"):

//inside EjbLookup.java

public <T> T lookupEjb(String sessionBeanClassName) {
    return lookup("java:comp/env/ejb/" + sessionBeanClassName);
}

public <T> T lookup(String name) {
    try {
        Context c = new InitialContext();
        return (T) c.lookup(name);
    } catch (NamingException ne) {
        log.error(ne.getMessage(), ne);
        throw new RuntimeException(ne);
    }
}

So I can create a class like this in my "lib/*.jar":

import javax.enterprise.inject.Produces;

public class EjbProducer {

    private EjbLookup ejbLookup = new EjbLookup();

    @Produces
    public MySessionLocal getMySessionLocal() {
        return ejbLookup.lookupEjb("MySessionBean");
    }

}

And now I can @Inject the EJB anywhere in my "lib/*.jar" by doing this:

@Inject
private MySessionLocal mySessionLocal;

On the other hand, if I attempt to use this code inside the EJB jar itself, I get an error about how the stateless ejb could not be created. Perhaps that has more to do with the way you're supposed to use JNDI than CDI though. I can work around this by using @EJB when I'm inside the ejb jar and using @Inject when I'm in the lib.

Upvotes: 0

Laird Nelson
Laird Nelson

Reputation: 16196

An ear's ejb jar can @Inject a class in a "lib/*.jar", but a class in a "lib/*.jar" can't get an @EJB from a ejb jar in the ear's root.

It's still unclear to me if this is by design.

I believe this is by design. A library (something in the .ear file's library directory) does not have to be processed by the machinery that fills @EJB-annotated slots. To put it another way, only a Java EE module (an EJB jar, a web application) will have its @EJB-annotated fields "filled".

CDI, by contrast, has no such restrictions (provided that the relevant META-INF/beans.xml files exist), so it can "fill" @Inject-annotated fields with beans sourced from any bean archive.

Upvotes: 1

Related Questions