6 Replies Latest reply on Jan 28, 2010 11:27 AM by kabirkhan

    Activating OnDemand beans from child controller

    kabirkhan
      Fixing http://lists.jboss.org/pipermail/jboss-development/2010-January/015434.html I am adding a ScopedOnDemandDependencyTestCase (extends OnDemandDependencyTestCase similar to (Scoped)DuplicateAliasTestCase), but have found a deeper problem. If an OnDemand bean is installed in the main controller and is depended on by a bean in a child controller the OnDemand bean never gets activated and the child controller bean's dependency never resolved.
      I install Bean1 with OnDemand, goes to Describe state
      I install scoped Bean2 with Automatic and a dependency on Bean1, goes to Instantiated state fine
      When trying to move Bean2 to Configured state it does not resolve for the following reasons:
      When enabling ondemand beans in AbstractDependencyItem.resolve(), it was looking at the wrong controller, I changed:
               if (unresolvedContext != null && ControllerMode.ON_DEMAND.equals(unresolvedContext.getMode()))
               {
                  try
                  {
                     controller.enableOnDemand(unresolvedContext);
      
      to
       
              if (unresolvedContext != null && ControllerMode.ON_DEMAND.equals(unresolvedContext.getMode()))
               {
                  try
                  {
                     unresolvedContext.getController().enableOnDemand(unresolvedContext);
      

       

      Now Bean1 correctly gets its requiredState set to INSTALLED. Unfortunately this is not enough.
      In the normal/unscoped mode the mechanism is that AbstractDI.resolve() returns false, and we end up back in the resolveContexts() loop which finds Bean1 is not in its requiredState (INSTALLED).
      In the scoped test, it does not get picked up since Bean2 was installed in the scoped controller whose resolveContexts() does not involve the main controller so Bean1 is never found.
      I'll see if I can fix this
        • 1. Re: Activating OnDemand beans from child controller
          kabirkhan

          The messages you added to avoid the errors shown in the dev list post now get triggered for contexts that have been uninstalled. As you can see I modified them a bit. If I have an OnDemand bean called Bean1 and Bean2 and Bean3 depend on it, then if I

          -uninstall Bean2

          -when uninstalling Bean3 I get an error message when checking Bean3's dependencies. It finds Bean1 and then checks Bean1's DependsOnMe. The message I get is "Could not find reverse dependency 'Name2' while uninstalling on demand contexts for AbstractDependencyItem@6c3c9c31{name=Name3 dependsOn=Name1 whenRequired=Configured resolved=true}"

           

             protected void uninstallUnusedOnDemandContexts(ControllerContext context, boolean trace)
             {
                lockWrite();
                try
                {
                   DependencyInfo dependencies = context.getDependencyInfo();
                   if (dependencies != null)
                   {
                      Set<DependencyItem> iDependOn = dependencies.getIDependOn(null);
                      if (iDependOn.isEmpty() == false)
                      {
                         for (DependencyItem item : iDependOn)
                         {
                            if (item.isResolved()) //TODO Is this check necessary
                            {
                               Object name = item.getIDependOn();
                               if (name == null)
                                  continue;
             
                               ControllerContext other = getContext(name, null);
                               if (other == null)
                               {
                                  log.warn("Could not find dependency '" + name + "' while uninstalling on demand contexts for " + item);
                                  continue;
                               }
                               if (other.getMode() != ControllerMode.ON_DEMAND)
                                  continue;
             
                               DependencyInfo otherDependencies = other.getDependencyInfo();
                               if (otherDependencies == null)
                                  continue;
                               
                               Set<DependencyItem> dependsOnOther = otherDependencies.getDependsOnMe(null);
                               boolean isRequired = false;
                               for (DependencyItem dependsOnOtherItem : dependsOnOther)
                               {
                                  ControllerContext dependsContext = getContext(dependsOnOtherItem.getName(), null);
                                  if (dependsContext == null)
                                  {
                                     log.warn("Could not find reverse dependency '" + dependsOnOtherItem.getName() + "' while uninstalling on demand contexts for " + item);
                                     continue;
                                  }
                                  
                                  ControllerState requiredState = item.getWhenRequired();
                                  ControllerState actualState = dependsContext.getState();
                                  
                                  if (requiredState.equals(actualState) || stateModel.isBeforeState(requiredState, actualState))
                                  {
                                     isRequired = true;
                                     break;
                                  }
                               }
                               if (!isRequired)
                               {
                                  //For some reason uninstallContext() uninstalls to the state below the passed in one, add one
                                  ControllerState state = stateModel.getNextState(ControllerMode.ON_DEMAND.getRequiredState());
                                  uninstallContext(other, state, trace);
                               }
                            }
                         }
                      }
                   }
                }
                finally
                {
                   unlockWrite();
                }
             }
          
           
          

           

          This simple test shows that DependsOnMe never gets cleared:

           

          public class DependsOnMeTestCase extends AbstractDependencyTest
          {
             public DependsOnMeTestCase(String name)
             {
                super(name);
             }
          
             public void testDependsOnMeCorrectOrder() throws Throwable
             {
                ControllerContext context1 = assertInstall(getDelegate1());
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEmpty(context1.getDependencyInfo().getDependsOnMe(null));
                
                ControllerContext context2 = assertInstall(getDelegate2());
                assertEmpty(context2.getDependencyInfo().getDependsOnMe(null));
                assertEquals(1, context2.getDependencyInfo().getIDependOn(null).size());
                assertEquals("Name1", context2.getDependencyInfo().getIDependOn(null).iterator().next().getIDependOn());
                
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEquals(1, context1.getDependencyInfo().getDependsOnMe(null).size());
                assertEquals("Name1", context1.getDependencyInfo().getDependsOnMe(null).iterator().next().getIDependOn());
             }
             
             public void testDependsOnMeWrongOrder() throws Throwable
             {
                ControllerContext context2 = assertInstall(getDelegate2(), ControllerState.PRE_INSTALL);
                assertEmpty(context2.getDependencyInfo().getDependsOnMe(null));
                assertEquals(1, context2.getDependencyInfo().getIDependOn(null).size());
                assertEquals("Name1", context2.getDependencyInfo().getIDependOn(null).iterator().next().getIDependOn());
                
                ControllerContext context1 = assertInstall(getDelegate1());
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEquals(1, context1.getDependencyInfo().getDependsOnMe(null).size());
                assertEquals("Name1", context1.getDependencyInfo().getDependsOnMe(null).iterator().next().getIDependOn());
          
                assertEmpty(context2.getDependencyInfo().getDependsOnMe(null));
                assertEquals(1, context2.getDependencyInfo().getIDependOn(null).size());
                assertEquals("Name1", context2.getDependencyInfo().getIDependOn(null).iterator().next().getIDependOn());
             }
             
             public void testDependsOnMeUninstallRightOrder() throws Throwable
             {
                ControllerContext context1 = assertInstall(getDelegate1());
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEmpty(context1.getDependencyInfo().getDependsOnMe(null));
                
                ControllerContext context2 = assertInstall(getDelegate2());
                assertEmpty(context2.getDependencyInfo().getDependsOnMe(null));
                assertEquals(1, context2.getDependencyInfo().getIDependOn(null).size());
                assertEquals("Name1", context2.getDependencyInfo().getIDependOn(null).iterator().next().getIDependOn());
                
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEquals(1, context1.getDependencyInfo().getDependsOnMe(null).size());
                assertEquals("Name1", context1.getDependencyInfo().getDependsOnMe(null).iterator().next().getIDependOn());
                
                assertUninstall(context2);
                assertEmpty(context2.getDependencyInfo().getDependsOnMe(null));
                assertEquals(1, context2.getDependencyInfo().getIDependOn(null).size());
                assertEquals("Name1", context2.getDependencyInfo().getIDependOn(null).iterator().next().getIDependOn());
          
                context1 = assertContext("Name1", ControllerState.INSTALLED);
                assertEmpty(context1.getDependencyInfo().getIDependOn(null));
                assertEmpty(context1.getDependencyInfo().getDependsOnMe(null));  // FAILS - dependsOnMe still contains Name2
             }
             
             
             protected TestDelegate getDelegate1()
             {
                return new TestDelegate("Name1");
             }
             
             protected TestDelegate getDelegate2()
             {
                TestDelegate result = new TestDelegate("Name2");
                result.addDependency(new AbstractDependencyItem("Name2", "Name1", ControllerState.DESCRIBED, ControllerState.INSTALLED));
                return result;
             }
          
          }
          
           
          
          • 2. Re: Activating OnDemand beans from child controller
            alesj

            This simple test shows that DependsOnMe never gets cleared:

            This is a known issue:

            * http://community.jboss.org/thread/96478?tstart=0

            * http://community.jboss.org/thread/96993?tstart=0

             

            But why is this an issue?

            • 3. Re: Activating OnDemand beans from child controller
              kabirkhan
              This is an issue when uninstalling a bean and checking the OnDemand beans we depend on. We check the OnDemand beans dependsOnMe to see if the contexts are still installed. It works fine now, but we do get that warning for beans that have been uninstalled already since dependsOnMe is never cleaned up. Anyway, as discussed offline we can wait until we do some more work on the dependencies.
              • 4. Re: Activating OnDemand beans from child controller
                kabirkhan

                kabir.khan@jboss.com wrote:

                 

                Fixing http://lists.jboss.org/pipermail/jboss-development/2010-January/015434.html I am adding a ScopedOnDemandDependencyTestCase (extends OnDemandDependencyTestCase similar to (Scoped)DuplicateAliasTestCase), but have found a deeper problem. If an OnDemand bean is installed in the main controller and is depended on by a bean in a child controller the OnDemand bean never gets activated and the child controller bean's dependency never resolved.
                I install Bean1 with OnDemand, goes to Describe state
                I install scoped Bean2 with Automatic and a dependency on Bean1, goes to Instantiated state fine
                When trying to move Bean2 to Configured state it does not resolve for the following reasons:
                When enabling ondemand beans in AbstractDependencyItem.resolve(), it was looking at the wrong controller, I changed:
                         if (unresolvedContext != null && ControllerMode.ON_DEMAND.equals(unresolvedContext.getMode()))
                         {
                            try
                            {
                               controller.enableOnDemand(unresolvedContext);
                
                to
                 
                        if (unresolvedContext != null && ControllerMode.ON_DEMAND.equals(unresolvedContext.getMode()))
                         {
                            try
                            {
                               unresolvedContext.getController().enableOnDemand(unresolvedContext);
                

                 

                Now Bean1 correctly gets its requiredState set to INSTALLED. Unfortunately this is not enough.
                In the normal/unscoped mode the mechanism is that AbstractDI.resolve() returns false, and we end up back in the resolveContexts() loop which finds Bean1 is not in its requiredState (INSTALLED).
                In the scoped test, it does not get picked up since Bean2 was installed in the scoped controller whose resolveContexts() does not involve the main controller so Bean1 is never found.
                I'll see if I can fix this

                 

                After a trying a few different things what I have now is.

                -Leaving DependencyItem as is

                -Modifying AbstractController.enableOnDemand() to set onDemandEnabled for the initiating controller (i.e. scoped in this case) and also for the controller the OnDemand context actually belongs to.

                -Modifying the AbstractController.resolveContexts() loop to record if onDemandEnabled was true, and if it was when finishing the loop to call resolveContexts() on the parent controller.

                • 5. Re: Activating OnDemand beans from child controller
                  kabirkhan

                  kabir.khan@jboss.com wrote:

                   

                  After a trying a few different things what I have now is.

                  -Leaving DependencyItem as is

                  -Modifying AbstractController.enableOnDemand() to set onDemandEnabled for the initiating controller (i.e. scoped in this case) and also for the controller the OnDemand context actually belongs to.

                  -Modifying the AbstractController.resolveContexts() loop to record if onDemandEnabled was true, and if it was when finishing the loop to call resolveContexts() on the parent controller.

                  Committed against https://jira.jboss.org/jira/browse/JBKERNEL-79

                  • 6. Re: Activating OnDemand beans from child controller
                    kabirkhan

                    kabir.khan@jboss.com wrote:

                    This simple test shows that DependsOnMe never gets cleared:

                     

                    This discussion lives here: http://www.jboss.org/index.html?module=bb&op=viewtopic&t=101389