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)
...
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.