Search code examples
javadependency-injectionjerseyjersey-2.0hk2

How do I utilize supportsNullCreation() in Jersey?


I have an injectable provider that may or may return null. I am getting an exception when it is null. I registered the provider as a Singleton, can I possibly register it as a type of SingletonContext that I customize to return true for supportsNullCreation()? I think if I can do that then even if findOrCreate() returns null, my code will still run which is what I want.

@ApplicationPath("rest")
public class MyApplication extends ResourceConfig 
{
    public MyApplication()
    {
        ...
    // Provider of DB
    this.register( new AbstractBinder()
    {
       @Override
       public void configure()
       {
 bindFactory(DbManager.class).to(EntityManagerFactory.class).in(Singleton.class);
       }
    });
}

Then it is used like this:

@Singleton
@Path("myservice")
public class WebServiceClass
{
   // NOTE: Right now I have to comment this to run without a DB
   @Inject
   private EntityManagerFactory entityManagerFactory = null;
   ...

The exception I get is this...

java.lang.IllegalStateException: Context 
 org.jvnet.hk2.internal.SingletonContext@6cae5847 findOrCreate returned a null for 
descriptor SystemDescriptor(
    implementation=com.db.DbManager
    contracts={javax.persistence.EntityManagerFactory}
    scope=javax.inject.Singleton
    qualifiers={}
    descriptorType=PROVIDE_METHOD
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@7050f2b1
    proxiable=null
    proxyForSameScope=null
    analysisName=null
    id=145
    locatorId=0
    identityHashCode=863132354
    reified=true)
    at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2075)
...

Solution

  • I would recommend changing the design a bit. Using the EntityManagerFactory in the resource class is not very great design. You are left with code like

    public class Resource {
        private EntityManagerFctory emf;
    
        @POST
        public Response get(Entity e) {
            EntityManager em = emf.createEntityManager();
            em.getTransaction().begin();
            em.persist(e);
            em.getTransaction().commit();
            em.close();
        }
    }
    

    There are a lot of things wrong with this picture. For one you are breaking the [Single Responsibility Principle][1]. Secondly this doesn't allow you to elegantly handle the null EMF, even if it was possible. You have this all over the place

    if (emf != null) {
        // do code above
    } else {
        // do something else.
    }
    

    Also it is no great for testing. The common pattern is to use a DAO layer. Personally I even add a service layer in between the DAO and the REST layer, but you can get away with just a DAO layer.

    For example what I would do is create a common abstraction interface for the data access calls.

    public interface DataService {
        Data getData();
    }
    

    Then create an implementation for db access

    public class WithDbService implements DataService {
        private EntityManagerFactory emf;
    
        public WithDbService(EntityManagerFactory emf) {
            this.emf = emf;
        }
    
        @Override
        public Data getData() {
            ...
        }
    }
    

    Then create another implementation without db access.

    public class WithoutDbService implements DataService {
        @Override
        public Data getData() {}
    }
    

    Then you can use a Factory to create the DataService. What you will do is use the ServiceLocator to try and find the EMF. If it is not null, return the WithDbService else return the WithoutDbService

    public class DataServiceFatory implements Factory<DataService> {
    
        private DataService dataService;
    
        @Inject
        public DataServiceFactory(ServiceLocator locator) {
            // abbreviated for brevity
            EMF emf = locator.getService(EMF.class);
            if (emf != null) {
                dataService = new WithDbService(emf);
            } else {
                dataService = new WithoutDbService();
            }
        }
    
        @Override
        public DataService provider() { return dataService; }
    }
    [...]
    bindFactory(DataServiceFactory.class).to(DataService.class).in(..);
    

    Then you can just inject DataService every where. As long as the two implementations follow the contract, it should work just fine.

    There may be some design improvements, but it is a big step up from using the EMF directly in the resource class.