Is it possible to obtain a CDI conversation
instance programatically only knowing that the current thread
is the one being used to process the CDI request
associated with the wanted conversation? And if possible then how?
In particular, what I want to do is this:
@ConversationScoped
public class UnitOfWork {...}
public class Client {
@Inject transient UnitOfWork uof;
...
}
public class Room {
@Inject transient UnitOfWork uof;
...
}
but using a programatic mechanism to initialize the uof
instance variables instead of applying the @Inject annotation (because Client
and Room
are entities and they doesn't support injection).
I already tryed to inject the UnitOfWork
by means of a BeanManager
obtained by the following static method:
public static <B> B getManagedBean(Class<B> type, Annotation... qualifiers) {
try {
BeanManager beanManager = InitialContext.doLookup("java:comp/BeanManager");
Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers);
Bean<B> bean = (Bean<B>) beanManager.resolve(beans);
CreationalContext<B> cc = beanManager.createCreationalContext(bean);
return bean.create(cc);
} catch (NamingException e) {
throw new RuntimeException("", e);
}
}
but the problem is that beans given by means of the above method are new ones (every call gives a new instance), and I need that Client
and Room
share the same conversation scoped instance of UnitOfWork
.
The answer was very near but I overlooked it. Yes, it is possible to obtain the bean class instance of any bean contained by any of the active contexts (the ones associated with the current thread), by means of the BeanManager. This method does the job:
public static <B> B getContextualBeanInstance(Class<B> type, Annotation... qualifiers) {
try {
BeanManager beanManager = InitialContext.doLookup("java:comp/BeanManager");
Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers);
Bean<?> bean = beanManager.resolve(beans);
CreationalContext<?> cc = beanManager.createCreationalContext(bean);
return (B) beanManager.getReference(bean, type, cc);
} catch (NamingException e) {
throw new RuntimeException("", e);
}
}
The only difference with the method I mentioned in the question post is that this one uses BeanManager#getReference(..)
instead of Bean#create(..)
.
If you want to support parameterized bean types, change the type of the type
parameter from Class<B>
to Type
.
If the bean is @Dependent scoped, you should take care of the destruction of the bean class instance to avoid memory leaks. Here I explain how to do it nicely.