1 2 Previous Next 27 Replies Latest reply on Feb 26, 2007 9:16 AM by Ales Justin

    Scoped Kernels

    Adrian Brock Master

      Based on the discussion here:
      http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4015013#4015013

      I want to fork off part of the discussion which is how to implement the scoped kernel.

      Let's start with how the current singleton kernel controller works.
      The alogorithm goes something like (uninstall is mainly the reverse):

      atEveryInstall()
      {
       do
       {
       // Go through the states in order from lowest (NotInstalled) to highest (Installed)
       for (State state : statesInOrder)
       {
       // Get the contexts that are ready for the next state
       // i.e. dependencies are satisifed
       for (Context c : readyToPromoteToNextState(s))
       {
       // Double check because of "manual" and recursive calls
       if (notAlreadyPromoted(c))
       promoteToNextState(c);
       }
       }
       }
       // We loop because promotions in later states can satisfy dependencies
       // in earlier states.
       until (nothing changes state);
      }
      


      With scoped kernels the parent kernel needs to know about its children.
      A context in the scoped kernel can depend upon the parent kernel
      but not vice-versa.

      So as well as going through the contexts in this kernel
      there needs to be an additional:
      for (Controller child : childControllers)
      {
       child.atEveryInstall();
      }
      


      i.e. Once we have no more changes in the parent controller
      (i.e. the do until loop exits) we invoke the children.

      This alogorithm will avoid any problems with recursion or deadlocks
      since everything is done in one order parent -> child.

        • 1. Re: Scoped Kernels
          Adrian Brock Master

          Now let's look at a concrete example I was thinking about over the weekend.

          We want to define a "subsystem". This is a scoped kernel such that
          nobody else can play with our beans. i.e. it is a private implementation deployment.
          A simple case would be the old JBossJTA subsystem.

          <deployment>
           <scope level="subsystem" qualifier="JBoss JTA"/>
          
           <bean name="XIDFactory">
           ...
           </bean>
          
           <bean name="TransactionManager">
           <property name="xidFactory><inject bean="XIDFactory"/></property>
           ...
           </bean>
          </deployment>
          


          So far so good. Nobody else can see our XIDFactory, it is private to our scope.

          BUT others want to use the TransactionManager.

          In practice of course, they should be using the TransactionManagerLocator
          class, since we want to integrate in other places where we don't
          construct the TM. So this example is a bit academic.

          This could be formalised as having a root deployment:
          <deployment>
           <bean name="TransactionManager"
           <constructor factoryClass="TransactionManagerLocator" factoryMethod="getInstance"/>
           </bean>
          </deployment>
          


          This is good because it will work in all environments the transaction manager locator
          class supports, it allows IOC injection again, and the locator class
          knows how to find the TransactionManager defined in the "JBoss JTA" scope
          using a private factory method when run inside JBoss.

          It does however have one drawback.
          The dependencies are not really being checked/satisifed.

          Since we are using a private factory method in the TransactionManagerLocator
          somebody could forget to deploy the "JBoss JTA" scope deployment and it
          will still work. It will just use a default configuration for the TransactionManager.

          Maybe this is what we want? But probably only in this use cases.
          Although, in a lot of other use cases, the binding is not really coming
          from direct POJO injection, but from jndi bindings, etc.
          e.g. DataSources.

          If we tweak the example a bit and assume we do want people to be
          able to directly inject the TransactionManager (and there is nothing
          like the TransactionManagerLocator) then we need a mechanism to
          promote the TransactionManager into the default scope.

          One mechanism would be to do an "export". i.e. I define the bean
          in a scoped kernel, but I also want it available in the global scope.
          <bean name="TransactionManager" export="true">
          


          There would be two ways to implement this.

          1) The context really exists in the global scope, but for the sake of
          dependencies it uses the "JBoss JTA" scope.

          2) We register and "alias" in the global scope.

          I think (2) will be a lot simpler to implement.

          Essentially, the alias would just act as a "proxy/delegate" to the real
          context in the scoped kernel. The root kernel would recognise the
          context as a proxy and not try to manage it.

          And Scott has been asking for a generic alias mechanism anyway. :-)

          • 2. Re: Scoped Kernels
            Adrian Brock Master

             


            2) We register an "alias" in the global scope.

            I think (2) will be a lot simpler to implement.

            Essentially, the alias would just act as a "proxy/delegate" to the real
            context in the scoped kernel. The root kernel would recognise the
            context as a proxy and not try to manage it.


            The reason why the alias would be simpler to implement
            is because managing the scoped bean from the global scope introduces
            all sorts of problems with ordering that are likely to lead to deadlocks.
            e.g. Having to take locks in the scoped kernel from the global scope
            to look at the state, etc.

            • 3. Re: Scoped Kernels
              Ales Justin Master

              Should I simply (re)move all the dependent information (allContexts, contextsByState, errorContexts, installing) from the parent controller, and stick it into the scoped one in PreInstallAction (and a simple reverse on the uninstall). Or is there something I should be careful about?

              • 4. Re: Scoped Kernels
                Ales Justin Master

                On the uninstall:

                 public void undeploy(DeploymentUnit unit, BeanMetaData deployment)
                 {
                 controller.uninstall(deployment.getName());
                 }
                


                How to get to the Scoped controller that holds the actual ControllerContext?
                Look in the current controller if the context is there and uninstall it, else go to child controllers and call the uninstall on them - the one that returns non-null is the one that unistalled the context?

                • 5. Re: Scoped Kernels
                  Ales Justin Master

                  Ok, forget about the previous two posts.

                  I have a prototype impl ready and working (it is doing what expected and it doesn't break any of the existing tests). Just need to write some real tests for this new feature :-).
                  I'll do this tomorrow, and if the tests pass, I'll commit the code (since it doesn't effect the previous non-scoped stuff) and then we can discuss this further.

                  Scott, or should I totally wait for Friday to pass by, and then commit?

                  • 6. Re: Scoped Kernels
                    Scott Stark Master

                     

                    "alesj" wrote:

                    Scott, or should I totally wait for Friday to pass by, and then commit?

                    I'll update the current 2.0 branch from trunk today so you can commit when its ready.


                    • 7. Re: Scoped Kernels
                      Ales Justin Master

                      I'm quite done with scoping, but I have encountered a few problems:

                      1) When a bean that represents the classloader for other beans is deployed in some scope, other dependent beans (from same scope) are unable to find him - since they are not described yet == no scope knowlegde.

                      2) If I deploy bean with the same name and the same class in 2 different scopes, KernelControllerContext scope lacks some differentiation between different context instances - fullScope with INSTANCE and CLASS is the same for both context instances. I currently added context's hash under WORK level, to be able to test this.

                      3) While testing, how to get the actual bean / context from bootstrapping kernel / controller, since the real bean / context is present in some scoped child controller.

                      • 8. Re: Scoped Kernels
                        Ales Justin Master

                         

                        "alesj" wrote:

                        3) While testing, how to get the actual bean / context from bootstrapping kernel / controller, since the real bean / context is present in some scoped child controller.

                        I've added this:
                         private class TestController extends AbstractController
                         {
                         private AbstractController delegate;
                        
                         public TestController(AbstractController controller) throws Exception
                         {
                         this.delegate = controller;
                         }
                        
                         public ControllerContext getContext(Object name, ControllerState state)
                         {
                         return findContext(delegate, name, state);
                         }
                        
                         private ControllerContext findContext(AbstractController controller, Object name, ControllerState state)
                         {
                         ControllerContext context = controller.getContext(name, state);
                         if (context != null)
                         {
                         return context;
                         }
                         else
                         {
                         for (AbstractController childController : controller.getControllers())
                         {
                         ControllerContext ctx = findContext(childController, name, state);
                         if (ctx != null)
                         return ctx;
                         }
                         }
                         return null;
                         }
                         }
                        


                        I have some public methods in AbstractController that it would be better if they were not public. I'll fix them later - when we decide what to put in SPI.


                        • 9. Re: Scoped Kernels
                          Scott Stark Master

                          I think we need some notion of scoped names that allows this to be done easily. This seems to be needed in order to resolve item 2) as well. A proper alias would then be a simple name in a scope that links to the scoped name to which the alias resolves.

                          Regarding 1), I'm not clear on what the relationship is between the kernels, names spaces, and type systems are. I'm assuming that whether or not the type systems are isolated/shared is strictly a function of the bean configuration in the kernels. Based on the initialization algorithm Adrian described, it should be possible to pickup the beans in a given state across the graph of kernel instances. Are you traversing all contexts across all kernels for a given state?

                          • 10. Re: Scoped Kernels
                            Ales Justin Master

                             

                            "scott.stark@jboss.org" wrote:
                            Are you traversing all contexts across all kernels for a given state?

                            I'm doing a simple current --> parent chain context lookup.

                            The problem is when a CL bean is defined in some scope, it is part of the ScopedController after PreInstall state. But the CL bean dependant beans are still in the 'Not installed' state, since they need CL for MetaData, so they are still having a bootstrap Controller assigned to them - which doesn't know about context's in his child Controllers == ScopedControllers.

                            • 11. Re: Scoped Kernels
                              Adrian Brock Master

                               

                              "alesj" wrote:
                              I'm quite done with scoping, but I have encountered a few problems:

                              1) When a bean that represents the classloader for other beans is deployed in some scope, other dependent beans (from same scope) are unable to find him - since they are not described yet == no scope knowlegde.


                              You can't use a classloader in a scope if the scope requires
                              that classloader to exist before it is constructed.

                              It's like trying to have a deployment depend upon a classloader
                              in that deployment. In that case, you fix it by doing:
                              <classloader><null/></classloader>
                              


                              So it doesn't depend upon itself.


                              2) If I deploy bean with the same name and the same class in 2 different scopes, KernelControllerContext scope lacks some differentiation between different context instances - fullScope with INSTANCE and CLASS is the same for both context instances. I currently added context's hash under WORK level, to be able to test this.


                              I don't understand this. 2 contexts in different scopes should not have
                              the same ScopeKey.


                              3) While testing, how to get the actual bean / context from bootstrapping kernel / controller, since the real bean / context is present in some scoped child controller.


                              There are a number of apis that need updating to understand Scope.
                              i.e. passing in the ScopeKey as an additional parameter.

                              The old api should invoke the new api with the default scope.

                              Retrieving the context is one of them.

                              There are other apis that need to be able to retrieve all contexts in all scopes
                              and include the scope in the identity.
                              The checking of incomplete contexts in the kernel deployer
                              or the repository service is one of the latter.

                              • 12. Re: Scoped Kernels
                                Ales Justin Master

                                 

                                "adrian@jboss.org" wrote:

                                I don't understand this. 2 contexts in different scopes should not have
                                the same ScopeKey.

                                The problem that I encountered was when I had two beans with the same class and same name deployed into 2 different scopes, I always got annotations meta data for the first one deployed --> obvious, since keys matched --> no scope differentiation.

                                "adrian@jboss.org" wrote:

                                There are a number of apis that need updating to understand Scope.
                                i.e. passing in the ScopeKey as an additional parameter.

                                The old api should invoke the new api with the default scope.

                                Retrieving the context is one of them.

                                There are other apis that need to be able to retrieve all contexts in all scopes
                                and include the scope in the identity.
                                The checking of incomplete contexts in the kernel deployer
                                or the repository service is one of the latter.

                                If you could just 'mark' all the necessary API methods that need updateing, I'll go and 'fix' them.

                                • 13. Re: Scoped Kernels
                                  Adrian Brock Master

                                   

                                  "alesj" wrote:

                                  "adrian@jboss.org" wrote:

                                  There are a number of apis that need updating to understand Scope.
                                  i.e. passing in the ScopeKey as an additional parameter.

                                  The old api should invoke the new api with the default scope.

                                  Retrieving the context is one of them.

                                  There are other apis that need to be able to retrieve all contexts in all scopes
                                  and include the scope in the identity.
                                  The checking of incomplete contexts in the kernel deployer
                                  or the repository service is one of the latter.

                                  If you could just 'mark' all the necessary API methods that need updateing, I'll go and 'fix' them.


                                  Anything that takes a "name" and returns a ControllerContext
                                  is in the first category.

                                  e.g.
                                   ControllerContext getInstalledContext(Object name);
                                  


                                  Anyhing that returns a collection of controller contexts is in the second category
                                   Set<ControllerContext> getNotInstalled();
                                  


                                  I don't think there is anything that returns a list of names
                                  but there are places like the incomplete deployment code
                                  that needs to understand that the full unique name is no longer just
                                  getName() but also needs to use ConstrollerContext.getScopeKey()

                                  • 14. Re: Scoped Kernels
                                    Ales Justin Master

                                     

                                    "adrian@jboss.org" wrote:
                                    but also needs to use ConstrollerContext.getScopeKey()


                                    From previous problem (duplicate 'context') temp fix, I currently have Context's hashCode under WORK level to diff the contexts. What's the appropriate fix?

                                    What about this new scope key - gained from class/bean (deployment) annotations? Should it be considered once it is resolved - with some update to current context scope - or is this how I used it ok --> install ScopeKey.

                                    1 2 Previous Next