Reputation: 2893
Is the following allowed in Java EE?
I have a @Singleton
session bean that works as a registry and automatically discovers certain strategies that are used throughout the application, like this:
public interface Strategy {
Class<?> supportedType();
}
@Singleton
public class StrategyRegistry {
@Inject
private Instance<Strategy> strategies;
private Map<Class<?>, Strategy> registry = new HashMap<>();
@PostConstruct
public void startup() {
Iterator<Strategy> it = strategies;
while (it.hasNext()) {
Strategy s = it.next();
registry.put(s.supportedType(), s);
}
}
// return provided strategy to caller
public Strategy strategyFor(Class<?> clz) {
return registry.get(clz);
}
}
Now my questions is whether sharing the @Inject
ed CDI beans to other clients (via #strategyFor(...)
) is allowed or whether this can lead to unexpected behavior or side effects?
The strategies are stateless and thread-safe, so in general it should be possible to use them concurrently.
Note that the owner of the CDI beans above is a @Singleton
and the CDI beans are currently @Dependent
, so if I'm reading the spec correctly they should be available for the whole lifetime of the application.
Originally we used EJB stateless beans, and strategyFor(...)
only returned the proxy to the actual bean, which I considered reasonably safe. This is no longer possible on Wildfly 8 with recent WELD versions, and thus we switched to CDI beans. With CDI beans, however, the actual bean is returned, not a proxy.
If this is not allowed, is the following a safe alternative?
@Inject private BeanManager beanManager;
public Strategy strategyFor(Class<?> clz) {
Strategy s = registry.get(clz);
if (s == null) return null;
return beanManager.getReference(s.getClass());
}
Upvotes: 1
Views: 391
Reputation: 1943
I don't see why not, as @Depend (default scope) is a pseudo scope therefor the bean manager is no longer interested in the bean. Its life cycle from there on depends purely on the outer bean or other classes that hold direct references to it - Its a plain old POJO
Upvotes: 1
Reputation: 3533
See the following
@Qualifier
@Retention(RUNTIME)
@Target({FIELD,METHOD,TYPE,PARAMETER})
public interface StrategyContext {
//Nonbinding and default so that you can have one producer method for all strategies
@NonBinding
Class<?> supportedType() default Object.class;
}
@Singleton
public class StrategyRegistry {
private Map<Class<?>, Strategy> registry = new HashMap<>();
@Inject
void startup(@Any final Instance<Strategy> strategies) {
for(Strategy strategy:strategies)
registry.put(Strategy.supportedType(), Strategy);
}
}
@Produces
@StrategyContext
@ApplicationScoped
public Strategy strategyFor(final InjectionPoint ip) {
final StrategyContext sc = ip.getAnnotated().getAnnotation(StrategyContext.class);
final Class<?> supportedType = sc.supportedType();
return registry.get(supportedType);
}
}
And then wherever you want to use it.
@Stateless
public class MyService {
@Inject
@StrategyContext(supportedType=MySupportedType.class)
private Strategy strategy;
}
On the producer and the injection point:
NOTE If at runtime, the strategy may not exist and hence the method may return null, then annotate the producer with @Dependent, not @ApplicationScope and hence at the injection point, don't inject the raw strategy, but:
@Inject
@StrategyContext(supportedtype=MySupportedType.class)
private Instance<Strategy> strategy
...
if(!strategy.isUnsatisfied()) { strategy.get().doSomething();}
Upvotes: 1