Hmm. Can you have a sort of
Context of Contexts? You register one context and it holds a map to the other contexts and so on? Or am I missing the question?
Yes Nicklas, that is exactly what I mean. The context object then becomes a context manager, with the benefit that all context operations (like cleanup of resources after disposal) are centralized.
The way I see it, the problem with implementing this
context of contextsis that you need CDI to keep and pass a custom identifier to the Context's get methods so that the implementation can discriminate between all the managed contexts. It needs access to that identifier so that it can pass the correct reference intelligently back to the container for subsequent injection in a business contextualized bean.
I have looked at the spec and the SPI apidocs, and can't see an obvious way to achieve this. All the out-of-the-box scopes make use of a technology based identifier (mostly the active thread), which I don't think is suitable for this problem.
Or perhaps it is possible to achieve this behaviour in a completely different way? That would, of course, be first prize.
You can use reflection to access the protected getContext() or the private contexts field in the BeanManagerImpl. That way you can both be non-portable and in violation of good programming practices!
Thanks Nicklas :) Clearly not a good option.
But I do believe my problem is not atypical - surely there are many applications that uses the concept of
custom long-running context(a workflow context, for example, or a UI rendering context) that would benefit from this capability? The spec states quite clearly that the ability to develop custom contexts is a key goal of the SPI. Is there a fundamental reason why using CDI for this purpose should be a bad idea? It seems a perfect fit.
Yes but the contexts aren't designed to have identities themselves, any long-running context with
identities(e.g. the conversation id) is designed to fill the context with the correct backing data (beans for that id) for that particular context. So I think you would have to populate your context
externallywith some mechanism similar to the ThreadLocal approach.
I'm happy to manage the state and lifecycle of the context's beans manually (I'm doing it at the moment anyway, so it does not add much complexity). In fact, that is where CDI shines, because it simplifies the context management in these situations.
What other approach to ThreadLocal can possibly work? ThreadLocal is a convenient mechanism because it is statically accessible from within the Context implementation, but it requires your context to be thread-bound. As far as I can see, any approach that depends on a non-static identity will rely on the container to tell it what that identity is.
FWIW, I managed to get something working by implementing my own CreationalContext and passing that to the BeanManager's getReference(..) method. Weld is smart enough to use the same CreationalContext instance when resolving injection points for direct injections (i.e. not using the Instance<..> mechanism).
My Context Object receives the custom CreationalContext (which contains my custom context ID) and creates/retrieves instances as per the contained identity. This provides my context with the intelligence it needs to make
contextual injectionsthat are not thread bound.
It seems to work OK - all my dependent beans' @Inject points are resolved correctly and I can clean up all the created beans by invoking beanManager.getContext(MyContext.class).destroy() (a custom cleanup method) when the custom business context is destroyed.
The one big problem though is using the Instance<..> approach - Weld falls back on the default CreationalContextImpl when resolving instances using Instance<..>, rather than using the CreationalContext with which the Instance<..> was created in the first place. This situation I bypass by using a ..Factory that itself uses the BeanManager again with the custom CreationalContext (which I inject using a Producer method). That is quite tedious though, and something I believe the container should do.
It certainly is not perfect, but at least it is portable (insofar as I'm not referencing any Weld specific classes in my code) and should be thread safe.
However, I have to ask if there is a specific reason why CDI does not support the ability to
carrya context-bound ID out of the box, or at least
rememberswhich CreationalContext was used to create the Instance<..> reference?
Thanks Pete, that is excellent news. Thanks for the feedback.
long running conversationsounds like a problem to be solved by a BPM engine (like jBPM or Drools Flow). Or did I miss something?
I understand BPM is useful, but I think there is a big problem space that sits between CDI's built-in contexts and BPM, which CDI solves nicely with the ability define custom application contexts.