Search code examples
jakarta-eeejbcdiwebsphere-liberty

CDI not working in objects created from factory pattern implementation


I am trying to wrap my head around something here: Injection only works on the first layer of my application, but then it stops and all @Inject annotated properties are null in the objects returned from my factory pattern implementation. I read a lot about people having problems with getting CDI to work with JAX-RS but this seems not to be my problem. I have the feeling that I am missing some annotations, or I don't see the wood in front of all the trees (as we say here) ;-)

Edit: Did a sample project with the code I posted here to double check. Now I realise that I over simplified: in fact I am using a Factory Pattern to obtain my service, which might interrupt the managed context. Please see the augmented example:

Let's go.

First Layer: JAX-RS Application, all good

@RequestScoped
@Path("/somePath/someResource")
public class SomeResource {
   @Inject
   ISomeServiceFactory someServiceFactory;

   @POST
   @Produces(MediaType.APPLICATION_JSON)
   @Consumes(MediaType.APPLICATION_JSON)
   public SomeResponse someMethod(@PathParam("foo") final String foo) {
      ISomeService myService = someServiceFactory.getService(ServiceCatalog.valueOf(foo));   // works, we jump in there!
      myService.someMethod();
   }

}

public enum ServiceCatalog {
  STANDARD("standard"), ADVANCED("advanced"), FOO("foo");
  // ...
} 

The broken service factory that is selecting an implementation based of known parameter (Enum) values from the REST API Call:

public interface ISomeServiceFactory {
  public ISomeService getService(ServiceCatalog serviceType);
} 

@Stateless
public class SomeServiceFactory implements ISomeServiceFactory {
  public ISomeService getService(ServiceCatalog serviceType) {
    if(serviceType.equals(ServiceCatalog.FOO)) {
      return new FooService();  // will break CDI context
    } else if (...) {
      // ...
    } else {
      return new DefaultService(); // will also break CDI context
    }
  }
}

Second Layer: Some EJB, trouble here

// Interface: 

public interface ISomeService {
  public void someMethod();
}

// Implementation:

@Stateless
public class SomeService implements ISomeService {
  @Inject
  private ISomeDao someDao;    // this will be null !!!

  @Override
  public void someMethod() {
    someDao.doSomething()   // exception thrown as someDao == null
  }
}

The Dao that is supposed to be injected

public interface ISomeDao {
  public void someMethod();
}

@Stateless
public class SomeDao implements ISomeDao {
  public void someMethod() {}
}

Now at runtime WebSphere Liberty tells me (among other bindings...) this:

 com.ibm.ws.ejbcontainer.runtime.AbstractEJBRuntime
 I CNTR0167I: The server is binding the com.ISomeDao interface of the
 SomeDao enterprise bean in the my.war module of the my application.  
 The binding location is: java:global/my/SomeDao!com.ISomeDao

I have a beans.xml:

 <beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee    http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
   bean-discovery-mode="all">
 </beans>

It seems as if the new SomeService() breaks everything, could you advice on how to implement the factory in a way that enables CDI further down the road? Thanks.


Solution

  • In the amended question, you show that there is not actually an EJB injected into the web service, it is new'ed up indirectly via the factory.

    An object only has CDI injection performed into it when the container creates the object, either by itself being injected somewhere or it being one of the managed EE components.

    Net, you can't new-up an EJB or any CDI bean and have any CDI services in it.