3

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)
...
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Coder1224
  • 1,785
  • 2
  • 17
  • 21

1 Answers1

2

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.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720