4

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.

Readren
  • 994
  • 1
  • 10
  • 18

2 Answers2

2

Sorry, not a real answer, but too much to write in a comment:

There are reasons why entities do not support dependency injection - mainly because their lifecycle is decoupled from the lifecycle of managed beans.

While I certainly see use cases for DI in entities, I'd double (and triple) check if the benefits of this approach outweigh the risk. You might find yourself hacking the persistence context in some sort of second-level-cache hell ;)

Jan Groth
  • 14,039
  • 5
  • 40
  • 55
  • Does the risk you mention apply if all the injected properties where transient? – Readren Sep 05 '12 at 22:10
  • That is just one facet. I see the biggest issue in the fact that you have not much control or influence on creation / caching / serialization of entities. This can be okay for your use case, but it's certainly confusing - if not possibly erroneous. – Jan Groth Sep 06 '12 at 07:00
  • The instances of `@Entity` classes are still created the standard way by the persistence unit by means of `javax.persistence.Query` instances and the like. The difference in my approuch is that they are not simple POJOs but objects with behavior. And the injected properties are all transient and lazyly initialized with the help of a service locator. The problem y need to solve is inside the service locator. I need it to be able to obtain the instance of `Conversation` associated with the request that is the owner of the current `Thread`. – Readren Sep 07 '12 at 22:37
  • I know the entities themselves will lack the benefits of CDI, but the injected dependencies will not. So, when an entity has to perform an action that requires CDI, it will delegate this functionality to a injected object. Are I missing something? – Readren Sep 07 '12 at 23:05
1

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.

Community
  • 1
  • 1
Readren
  • 994
  • 1
  • 10
  • 18