Reputation: 24411
I'm trying to find a way to conditionally produce an @Alternative/@Specialized bean in CDI 1.2 if a given JNDI resource is present.
My use case is that I have a default bean which uses an in-memory data structure. However, if a given JNDI resource is present, I want to use that resource instead in a different implementation of my service. My problem is that as soon as I use a @Resource(lookup='jndiName')
annotation, Weld throws an exception if the resource is not found.
I wanted to use a producer to conditionally create the resource-based bean, but if I try to inject the @Resource
and it is missing, Weld fails.
For example:
interface MyService{
void doSomething();
}
// In memory implementation that I always want to have available for injection if no other MyService bean implementation is available
@ApplicationScoped
public class InMemory implements MyService{
Map<String, String> persistence = new HashMap<>();
public void doSomething(){ persistence.put("now", new Date());}
}
// Bean to be available IFF a JNDI based cache is found
public class JndiPersistence implements MyService{
Cache<String, String> persistence;
public JndiPersistence(Cache persistence){ this.persistence = persistence);}
public void doSomething(){ persistence.put("now", new Date());}
}
// client class which uses the service
public class DataManager(){
private MyService myservice;
@Inject
public DataManager( MyService myservice ){ this.myservice = myservice; }
// calls the injected service bean
public void manageMyData(){
myservice.doSomething();
}
Finally, the producer:
public class JndiProducer{
@Resource(lookup="java:comp/env/persistenceCache")
Cache cache;
@Produces
@ApplicationScoped
public MyService jndiBean(){
return new JndiPersistence( cache );
}
}
I have tried changing my Resource injection to Instance<Cache>
but if the jndi name is missing, it still throws an exception and doesn't even make it to the Producer. Finally, I am not sure how to make the entire Producer conditional on the resource being present and override the initial bean.
What is the correct approach for this in CDI? Is this even feasible, or is there a better approach to use instead?
Upvotes: 0
Views: 511
Reputation: 40296
The way I would go is a conditional producer with manual JNDI interactions.
I would start by making sure CDI ignores both implementations of MyService
:
@Vetoed
public class InMemory implements MyService { ... }
@Vetoed
public class JndiPersistence implements MyService { ... }
Then go ahead with the producer (please note code is approximate, just to show the general principle, it might require adjustments/fine tuning):
// no real need for scoping the producer, unless the environment/configuration demands it
public class MyServiceProducer {
private MyService instance;
@Produces
@ApplicationScoped // this scoping applies to the produced bean
public MyService getMyService() {
// I *think* CDI guarantees that this will not be accessed concurrently,
// so no synchronization needed
if (instance == null) {
try {
InitialContext ic = new InitialContext();
Cache cache = (Cache) ic.lookup("java:comp/env/persistenceCache");
instance = new JndiPersistence(cache);
} catch (NamingException ne) {
// you can probably ignore it...
// use correct judgement though: you may want to log it
// or even fail altogether (i.e. rethrow it) e.g. if you sense
// that JNDI should be there but it is malfunctioning
instance = new InMemory();
}
}
return instance;
}
}
Upvotes: 2