11 Replies Latest reply on Apr 13, 2010 6:13 AM by Francois Swiegers

    Custom "Business" context and scope

    Francois Swiegers Newbie

      I want to implement a custom context that does not depend on a technology discriminator (like the running thread) to differentiate between running contexts. I'm looking for the ability to:

      1. Start and destroy this context manually (in code).

      2. Attach a custom key to the context for later reattachment.

      It should basically work similar to the Conversation Scope, only not limited to one conversation per thread, as there can be many active contexts at the same time.

      Currently, the application works by passing a business context object around as it delegates work. However, this makes the API ugly, and besides - is this not exactly the type of problem CDI is meant to solve? However, I can't find a mechanism in the SPI to achieve this - all the normal scopes are thread-constrained, the Dependent scope does not support reactivation of the same instance and the Context interface does not seem to support the setting of custom identifier like context keys in my custom context.

      As background - I'm using Weld SE. It is multi-threaded, but not with a thread per context - there can literally be thousands of running contexts in the application at a given time.

        • 1. Re: Custom "Business" context and scope
          Nicklas Karlsson Master

          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?

          • 2. Re: Custom "Business" context and scope
            Francois Swiegers Newbie

            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 contexts is 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.

            • 3. Re: Custom "Business" context and scope
              Nicklas Karlsson Master

              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!

              • 4. Re: Custom "Business" context and scope
                Francois Swiegers Newbie

                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.

                • 5. Re: Custom "Business" context and scope
                  Nicklas Karlsson Master

                  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 externally with some mechanism similar to the ThreadLocal approach.

                  • 6. Re: Custom "Business" context and scope
                    Francois Swiegers Newbie

                    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.

                    • 7. Re: Custom "Business" context and scope
                      Francois Swiegers Newbie

                      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 injections that 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 carry a context-bound ID out of the box, or at least remembers which CreationalContext was used to create the Instance<..> reference?

                      • 8. Re: Custom "Business" context and scope
                        Pete Muir Master

                        Hi Francois,

                        We are working on a fix for the Instance<> issue - https://jira.jboss.org/jira/browse/WELD-466

                        • 9. Re: Custom "Business" context and scope
                          Francois Swiegers Newbie

                          Thanks Pete, that is excellent news. Thanks for the feedback.

                          • 10. Re: Custom "Business" context and scope
                            Cloves Almeida Newbie

                            This long running conversation sounds like a problem to be solved by a BPM engine (like jBPM or Drools Flow). Or did I miss something?

                            • 11. Re: Custom "Business" context and scope
                              Francois Swiegers Newbie

                              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.