Saturday, May 5, 2012

EJB is null inside RESTful web service

While developing my enterprise application and learning Java EE at the same time, I came to a situation where injecting an EJB inside a RESTful web service class would be the same as nothing. The EJB variable was always null.

So, what did I change to fix this?
This was my source code before:
@Path("/mypath")
public class MyREST {
    
    @EJB
    private MyFacadeLocal myFacade;
    
    public MyREST() {
    }

    @GET
    @Produces("text/plain")
    public String respond() {        
        return "DONE";
    }
}
I forgot to tell the scope of the service. I managed to make it work by turning it request scoped (although from what I read it should be, by default, request scoped):
@RequestScoped
@Path("/mypath")
public class MyREST {
    
    @EJB
    private MyFacadeLocal myFacade;
    
    public MyREST() {
    }

    @GET
    @Produces("text/plain")
    public String respond() {        
        return "DONE";
    }
}

If I make it session scoped, I end up with the issue: Error creating managed object for class.

If I make it @Stateless, it also works properly and seems to be the recommended way:
@Stateless
@Path("/mypath")
public class MyREST {
    
    @EJB
    private MyFacadeLocal myFacade;
    
    public MyREST() {
    }

    @GET
    @Produces("text/plain")
    public String respond() {        
        return "DONE";
    }
}

Notice that MyFacadeLocal is actually an interface for the session bean (which doesn't have the @LocalBean annotation itself). If I want to directly inject the session bean (called MyFacade) I need to have the @LocalBean annotation in it. If not, the EJB inside the RESTful web service may not be properly deployed and the following error may appear:
java.lang.IllegalStateException: Exception attempting to inject Remote ejb-ref name=rest.MyREST/myFacade,Remote 3.x interface =ejb.MyFacade,ejb-link=null,lookup=,mappedName=,jndi-name=ejb.MyFacade,refType=Session into class rest.MyREST: Lookup failed for 'java:comp/env/rest.MyREST/myFacade' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming}

Here's MyFacade without requiring an interface (assuming you'll be using it locally anyway):
@LocalBean
@Stateless
public class MyFacade extends AbstractFacade {
    @PersistenceContext(unitName = "myjpaunit")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public MyFacade() {
        super(My.class);
    }
    
    @Override
    public int count() {
        return super.count();
    }

    @Override
    public void create(My entity) {
        super.create(entity);
    }

    @Override
    public void edit(My entity) {
        super.edit(entity);
    }

    @Override
    public My find(Object id) {
        return super.find(id);
    }

    @Override
    public List findAll() {
        return super.findAll();
    }

    @Override
    public List findRange(int[] range) {
        return super.findRange(range);
    }

    @Override
    public void remove(My entity) {
        super.remove(entity);
    }
}

Another cause for having a null EJB is if, for any reason, an exception is thrown inside of it. Java EE will not inject it if running the EJB methods that are supposed to be called through the dependent object leads to exceptions. So, if you comment every call in your dependent object and then just check if the EJB is null, you'll see it isn't. But if you make use of a malfunctioning method, the EJB will just become null.
Useful links:
Glassfish embedded with JUnit for EJB testing

No comments:

Post a Comment

Feel free to share your thoughts!