[jboss-cvs] JBossAS SVN: r91582 - in projects/kernel/trunk/dependency/src: test/java/org/jboss/test/dependency/controller/support and 1 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Wed Jul 22 17:18:58 EDT 2009


Author: kabir.khan at jboss.com
Date: 2009-07-22 17:18:58 -0400 (Wed, 22 Jul 2009)
New Revision: 91582

Added:
   projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/PushControllerStateModel.java
   projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/support/UninstallAsynchronousInProgressTestDelegate.java
Modified:
   projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/AbstractController.java
   projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/test/AsynchronousTestCase.java
Log:
[JBKERNEL-25] Test and fixes for uninstalling an asynchronous context in progress

Modified: projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/AbstractController.java
===================================================================
--- projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/AbstractController.java	2009-07-22 21:06:01 UTC (rev 91581)
+++ projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/AbstractController.java	2009-07-22 21:18:58 UTC (rev 91582)
@@ -33,6 +33,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -94,9 +95,6 @@
    /** The contexts that are currently being installed by the executor */
    ContextsInstalledByExecutor contextsInstalledByExecutor = new ContextsInstalledByExecutor();
    
-   /** Indicates that a context is to be installed by the executor */
-   private final static Thread SCHEDULED_FOR_EXECUTOR = new Thread();
-
    /** The parent controller */
    private AbstractController parentController;
 
@@ -670,7 +668,7 @@
          if (errorContexts.remove(name) != null && trace)
             log.trace("Tidied up context in error state: " + name);
 
-         ControllerContext context = getRegisteredControllerContext(name, false);
+         ControllerContext context = getRegisteredContextAndInterruptAsynchronousInstall(name);
          if (context != null)
          {
             if (trace)
@@ -722,7 +720,25 @@
       }
    }
 
+   
    /**
+    * Obtains the context, having interrupted its installation process if it is an asynchronous context 
+    * currently being installed
+    * @param name The name of the context 
+    * @return the context or null if not found
+    */
+   private ControllerContext getRegisteredContextAndInterruptAsynchronousInstall(Object name)
+   {
+      ControllerContext context = getRegisteredControllerContext(name, false);
+      if (context != null)
+      {
+         contextsInstalledByExecutor.interruptTaskAndBlock(context, this);
+         context = getRegisteredControllerContext(name, false);
+      }
+      return context;
+   }
+       
+   /**
     * Install a context
     *
     * @param context the context
@@ -1168,12 +1184,12 @@
          Executor foundExecutor = searchForExecutor();
          if (foundExecutor != null)
          {
-            contextsInstalledByExecutor.markForThreadExecution(context);
+            InstallControllerContextTask task = new InstallControllerContextTask(context, trace);
+            contextsInstalledByExecutor.markForTaskExecution(context, task);
 
             if (trace)
                log.trace("Recorded for asynchronous installation " + context.getName());
-
-            InstallControllerContextTask task = new InstallControllerContextTask(context, trace);
+            
             try
             {
                foundExecutor.execute(task);
@@ -1183,7 +1199,7 @@
             {
                Object ctx = trace ? context : context.getName();
                log.warn("Asynchronous execution rejected by executor for context " + ctx + ":" + e.getMessage());
-               contextsInstalledByExecutor.disassociateWithThread(context);
+               contextsInstalledByExecutor.disassociateWithTask(context);
             }
          }
       }
@@ -1353,8 +1369,11 @@
 
                               for (ControllerContext dependent : dependents)
                               {
+                                 contextsInstalledByExecutor.interruptTaskAndBlock(dependent, this);
                                  if (isBeforeState(dependent.getState(), whenRequired) == false)
+                                 {  
                                     uninstallContext(dependent, whenRequired, trace);
+                                 }
                               }
                            }
                         }
@@ -2232,8 +2251,100 @@
       else
          return states.get(index);
    }
+
+   /**
+    * A task being handled asyynchronously by the executor
+    * 
+    */
+   abstract class InterruptibleControllerTask implements Runnable
+   {
+      /**
+       * The thread used to handle the asynchronous task
+       */
+      Thread thread;
+      
+      /**
+       * True if the task is interrupted  
+       */
+      volatile boolean interrupted;
+
+      /**
+       * Latch counted down by interrupted tasks once they have reached a state where they finish 
+       */
+      CountDownLatch interruptedLatch;
+      
+      /**
+       * Gets the thread that is currently used to install the asyncronous task
+       * @return The thread, or null if still queued in the executor
+       */
+      Thread getThread()
+      {
+         return thread;
+      }      
+      
+      /**
+       * Interrupt the task, and if associated with a thread
+       * return a countdown latch that will have a count of 0
+       * once the job has broken out
+       * @return the latch or null if not yet interrupted
+       */
+      synchronized CountDownLatch interrupt()
+      {
+         interrupted = true;
+         if (thread != null)
+         {
+            interruptedLatch = new CountDownLatch(1);
+            return interruptedLatch;
+         }
+         return null;
+      }
+
+      /**
+       * Check the interrupted status
+       * @return true if interrupted
+       */
+      boolean isInterrupted()
+      {
+         return interrupted;
+      }
+      
+      /**
+       * Associate the current thread with the task, or clear it in contextsInstalledByExecutor
+       * if it has been interrupted. This should be the first thing tasks do once they run.
+       * @return true if interrupted
+       */
+      synchronized boolean associateWithThreadOrDisassociateIfInterrupted(ControllerContext context)
+      {
+         if (interrupted)
+         {
+            contextsInstalledByExecutor.disassociateWithTask(context);
+            return true;
+         }
+         thread = Thread.currentThread();
+         return false;
+      }
+      
+      /**
+       * Disassociate the thread with the task, and if interrupted count down the latch so those
+       * awaiting the latch know the task has finished.
+       */
+      synchronized void disassociateWithThreadAndSignalEnd()
+      {
+         thread = null;
+         if (interruptedLatch != null)
+         {
+            interruptedLatch.countDown();
+         }
+      }
+   }
+
    
-   class InstallControllerContextTask implements Runnable
+   /**
+    * Interruptible task used to install a controller context asynchronously.
+    * It will install the context as far as possible towards its required state,
+    * before resolving the other contexts.
+    */
+   class InstallControllerContextTask extends InterruptibleControllerTask implements Runnable
    {
       ControllerContext context;
       ClassLoader classLoader;
@@ -2248,12 +2359,14 @@
       
       public void run()
       {
+         if (associateWithThreadOrDisassociateIfInterrupted(context))
+            return;
+         
          if (trace)
             log.trace(Thread.currentThread().getName() + " starting asyncronous install of " + context.getName());
          
          lockWrite();
          ClassLoader tcl = SecurityActions.setContextClassLoader(classLoader);
-         contextsInstalledByExecutor.associateWithThread(context);
          try
          {
             //Move the given context as far through the states as possible
@@ -2264,11 +2377,11 @@
             }
             finally
             {
-               contextsInstalledByExecutor.disassociateWithThread(context);
+               contextsInstalledByExecutor.disassociateWithTask(context);;
             }
             
             //The given context had its state changed, now see if anybody was dependent on it
-            if (stateChanged)
+            if (stateChanged && !interrupted)
             {
                resolveContexts(trace);
             }
@@ -2284,6 +2397,8 @@
                log.trace(Thread.currentThread().getName() + " asynchronous install done for " + context.getName());
             SecurityActions.resetContextClassLoader(tcl);
             unlockWrite();
+            
+            disassociateWithThreadAndSignalEnd();
          }
       }
       
@@ -2298,7 +2413,7 @@
          
          boolean resolved = true;
 
-         while(resolved && currentIndex < requiredIndex)
+         while(resolved && currentIndex < requiredIndex && !interrupted)
          {
             resolved = false;
             ControllerState toState = states.get(currentIndex + 1);
@@ -2306,6 +2421,7 @@
             {
                try
                {
+//                  PushControllerStateModel.push(AbstractController.this);
                   if (resolveDependencies(context, toState))
                   {
                      resolved = true;
@@ -2319,6 +2435,10 @@
                   errorContexts.put(context.getName(), context);
                   context.setError(error);
                }
+//               finally
+//               {
+//                  PushControllerStateModel.pop();
+//               }
                
                if (resolved)
                {
@@ -2335,46 +2455,109 @@
          return stateChanged;
       }
    }
+
    
+   
+   /**
+    * Class used to keep track of contexts that are scheduled for asynchronous install, 
+    * or are in the process of being installed asynchronously. 
+    * 
+    */
    private static class ContextsInstalledByExecutor
    {
       /** The contexts that are currently being installed by the executor */
-      Map<ControllerContext, Thread> executorThreadsByContext = new ConcurrentHashMap<ControllerContext, Thread>();
-
+      Map<ControllerContext, InterruptibleControllerTask> executorTasksByContext = new ConcurrentHashMap<ControllerContext, InterruptibleControllerTask>();
+      /**
+       * Checks whether context should be installed asynchronously, by checking the controller mode and the current thread
+       * @param context The context
+       * @return Whether the context should be installed in the executor
+       */
       boolean shouldInstallAsynchronously(ControllerContext context)
       {
          if (context.getMode() == ControllerMode.ASYNCHRONOUS)
          {
-            final Thread ctxThread = executorThreadsByContext.get(context);
-            return ctxThread == null || ctxThread != Thread.currentThread();
+            final InterruptibleControllerTask task = executorTasksByContext.get(context);
+            return task == null || task.getThread() != Thread.currentThread();
          }
          return false;
       }
       
-      void markForThreadExecution(ControllerContext context)
+      /**
+       * Records the context and the asynchronous task to install it
+       * @param context The context to be installed asynchronously
+       * @param task The task that will be used to install the context 
+       */
+      void markForTaskExecution(ControllerContext context, InterruptibleControllerTask task)
       {
-         executorThreadsByContext.put(context, SCHEDULED_FOR_EXECUTOR);
+         executorTasksByContext.put(context, task);
       }
       
-      void associateWithThread(ControllerContext context)
+      /**
+       * Removes the task that was used to install the context
+       * @param context The context whose task we want to remove
+       */
+      void disassociateWithTask(ControllerContext context)
       {
-         executorThreadsByContext.put(context, Thread.currentThread());
+         executorTasksByContext.remove(context);
       }
       
-      void disassociateWithThread(ControllerContext context)
+      /**
+       * Checks if the context is recorded for asynchronous install, and if the thread used is different
+       * from the current thread. 
+       * @param context The context to check
+       * @return true if installed by another thread
+       */
+      boolean isInstalledByOtherThread(ControllerContext context)
       {
-         executorThreadsByContext.remove(context);
+         final InterruptibleControllerTask task = executorTasksByContext.get(context);
+         return task != null && Thread.currentThread() != task.getThread();         
       }
       
-      boolean isInstalledByOtherThread(ControllerContext context)
+      /**
+       * Checks in the context is recorded for asynchronous install
+       * @param context The context to check
+       * @return true if recorded for asynchronous install
+       */
+      boolean isBeingInstalled(ControllerContext context)
       {
-         final Thread ctxThread = executorThreadsByContext.get(context);
-         return ctxThread != null && Thread.currentThread() != ctxThread;         
+         return executorTasksByContext.get(context) != null;
       }
       
-      boolean isBeingInstalled(ControllerContext context)
+      /**
+       * Checks if the passed in context is recorded for asynchronous install,
+       * and if it is interrupts the asynchronous task.<p>
+       *
+       * This method must be called with the write lock taken
+       * 
+       * @param context The context to interrupt
+       */
+      void interruptTaskAndBlock(ControllerContext context, AbstractController controller)
       {
-         return executorThreadsByContext.get(context) != null;
+         InterruptibleControllerTask task = executorTasksByContext.get(context);
+         if (task != null)
+         {
+            controller.unlockWrite();
+            try
+            {
+               CountDownLatch latch = task.interrupt();
+               if (latch != null)
+               {
+                  try
+                  {
+                     latch.await();
+                  }
+                  catch(InterruptedException e)
+                  {
+                     Thread.currentThread().interrupt();
+                  }
+               }
+            }
+            finally
+            {
+               controller.lockWrite();
+            }
+         }
       }
+      
    }
 }

Added: projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/PushControllerStateModel.java
===================================================================
--- projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/PushControllerStateModel.java	                        (rev 0)
+++ projects/kernel/trunk/dependency/src/main/java/org/jboss/dependency/plugins/PushControllerStateModel.java	2009-07-22 21:18:58 UTC (rev 91582)
@@ -0,0 +1,52 @@
+/*
+* JBoss, Home of Professional Open Source.
+* Copyright 2006, Red Hat Middleware LLC, and individual contributors
+* as indicated by the @author tags. See the copyright.txt file in the
+* distribution for a full listing of individual contributors. 
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/ 
+package org.jboss.dependency.plugins;
+
+import org.jboss.dependency.spi.ControllerStateModel;
+
+/**
+ * 
+ * @author <a href="kabir.khan at jboss.com">Kabir Khan</a>
+ * @version $Revision: 1.1 $
+ */
+public class PushControllerStateModel
+{
+   private static ThreadLocal<ControllerStateModel> controllerStateModel = new ThreadLocal<ControllerStateModel>();
+   
+   public static void push(ControllerStateModel model)
+   {
+      controllerStateModel.set(model);
+   }
+   
+   public static ControllerStateModel peek()
+   {
+      return controllerStateModel.get();
+   }
+   
+   public static ControllerStateModel pop()
+   {
+      ControllerStateModel model = peek();
+      controllerStateModel.remove();
+      return model;
+   }
+   
+}

Added: projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/support/UninstallAsynchronousInProgressTestDelegate.java
===================================================================
--- projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/support/UninstallAsynchronousInProgressTestDelegate.java	                        (rev 0)
+++ projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/support/UninstallAsynchronousInProgressTestDelegate.java	2009-07-22 21:18:58 UTC (rev 91582)
@@ -0,0 +1,181 @@
+/*
+* JBoss, Home of Professional Open Source.
+* Copyright 2006, Red Hat Middleware LLC, and individual contributors
+* as indicated by the @author tags. See the copyright.txt file in the
+* distribution for a full listing of individual contributors. 
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/ 
+package org.jboss.test.dependency.controller.support;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.jboss.dependency.spi.ControllerState;
+import org.jboss.logging.Logger;
+
+/**
+ * 
+ * @author <a href="kabir.khan at jboss.com">Kabir Khan</a>
+ * @version $Revision: 1.1 $
+ */
+public class UninstallAsynchronousInProgressTestDelegate extends TestDelegate
+{
+   Logger log = Logger.getLogger(UninstallAsynchronousInProgressTestDelegate.class);
+   CountDownLatch signalInWaitStateLatch; 
+   ControllerState waitInState;
+   CountDownLatch waitForMainLatch;
+   
+   public UninstallAsynchronousInProgressTestDelegate(Object name, CountDownLatch signalInWaitStateLatch, ControllerState waitInState, CountDownLatch waitForMainLatch)
+   {
+      super(name);
+      this.signalInWaitStateLatch = signalInWaitStateLatch;
+      this.waitInState = waitInState;
+      this.waitForMainLatch = waitForMainLatch;
+   }
+   
+   public UninstallAsynchronousInProgressTestDelegate(Object name)
+   {
+      this(name, null, null, null);
+   }
+   
+   @Override
+   public void configureInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.CONFIGURED);
+      super.configureInstall();
+      debugLogThread("Configure");
+   }
+
+   @Override
+   public void createInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.CREATE);
+      super.createInstall();
+      debugLogThread("Create");
+   }
+
+   @Override
+   public void describeInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.DESCRIBED);
+      super.describeInstall();
+      debugLogThread("Describe");
+   }
+
+   @Override
+   public void installInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.INSTALLED);
+      super.installInstall();
+      debugLogThread("Install");
+   }
+
+   @Override
+   public void instantiateInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.INSTANTIATED);
+      super.instantiateInstall();
+      debugLogThread("Instantiate");
+   }
+
+   @Override
+   public void startInstall()
+   {
+      signalInWaitInStateAndWaitForMainLatch(ControllerState.START);
+      super.startInstall();
+      debugLogThread("Start");
+   }
+   
+   @Override
+   public void configureUninstall()
+   {
+      super.configureUninstall();
+      debugLogThread("Configure--");
+   }
+
+   @Override
+   public void createUninstall()
+   {
+      super.createUninstall();
+      debugLogThread("Create--");
+   }
+
+   @Override
+   public void describeUninstall()
+   {
+      super.describeUninstall();
+      debugLogThread("Describe--");
+   }
+
+   @Override
+   public void installUninstall()
+   {
+      super.installUninstall();
+      debugLogThread("Install--");
+   }
+
+   @Override
+   public void instantiateUninstall()
+   {
+      super.instantiateUninstall();
+      debugLogThread("Instantiate--");
+   }
+
+   @Override
+   public void startUninstall()
+   {
+      super.startUninstall();
+      debugLogThread("Start--");
+   }
+
+   private void awaitMainLatch()
+   {
+      if (waitForMainLatch == null)
+         return;
+      try
+      {
+         debugLogThread("Awaiting main latch for " + getName());
+         waitForMainLatch.await();
+         debugLogThread("Got main latch for " + getName());
+      }
+      catch (InterruptedException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+   
+   private void signalInWaitInState(ControllerState state)
+   {
+      if (signalInWaitStateLatch == null)
+         return;
+      signalInWaitStateLatch.countDown();
+      debugLogThread("I am in " + state +  " " + getName());
+   }
+   
+   
+   private void signalInWaitInStateAndWaitForMainLatch(ControllerState state)
+   {
+      if (!state.equals(waitInState))
+         return;
+      signalInWaitInState(state);
+      awaitMainLatch();
+   }
+   
+   private void debugLogThread(String msg)
+   {
+      log.debug(Thread.currentThread().getName() + " " + getName() + " " + msg);
+   }
+}

Modified: projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/test/AsynchronousTestCase.java
===================================================================
--- projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/test/AsynchronousTestCase.java	2009-07-22 21:06:01 UTC (rev 91581)
+++ projects/kernel/trunk/dependency/src/test/java/org/jboss/test/dependency/controller/test/AsynchronousTestCase.java	2009-07-22 21:18:58 UTC (rev 91582)
@@ -22,8 +22,12 @@
 package org.jboss.test.dependency.controller.test;
 
 import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RejectedExecutionHandler;
@@ -40,6 +44,8 @@
 import org.jboss.test.dependency.controller.support.AsynchronousTestDelegate;
 import org.jboss.test.dependency.controller.support.Synchronizer;
 import org.jboss.test.dependency.controller.support.TestControllerContext;
+import org.jboss.test.dependency.controller.support.TestDelegate;
+import org.jboss.test.dependency.controller.support.UninstallAsynchronousInProgressTestDelegate;
 
 /**
  * 
@@ -452,7 +458,6 @@
          
          ctx2 = createAndInstallContext(delegate2, ControllerState.INSTALLED);
          synchronizer2.waitForWorkersAndEmitStartSignal();
-         assertNotSame(ControllerState.INSTALLED, ctx2.getState());
          
          synchronizer2.waitForWorkersToEnd();
          synchronizer1.waitForWorkersToEnd();
@@ -782,6 +787,257 @@
       }      
    }
    
+   public void testUninstallAsynchronousContextInProgress() throws Throwable
+   {
+      setExecutorInController(2);
+      
+      final CountDownLatch waitForMainLatch = new CountDownLatch(1);
+      final CountDownLatch signalInConfigureLatch = new CountDownLatch(1);
+      final CountDownLatch callUninstall = new CountDownLatch(1);
+      UninstallAsynchronousInProgressTestDelegate delegate = new UninstallAsynchronousInProgressTestDelegate("Bean", signalInConfigureLatch, ControllerState.CONFIGURED, waitForMainLatch);
+      delegate.setMode(ControllerMode.ASYNCHRONOUS);
+      ControllerContext ctx = null;
+      try
+      {
+         ctx = createAndInstallContext(delegate, ControllerState.INSTALLED);
+         signalInConfigureLatch.await();
+         if (ctx.getState() == ControllerState.INSTALLED)
+         {
+            fail("Should have been installed asynchronously");
+         }
+         ExecutorService executorService = Executors.newFixedThreadPool(1);
+         Future<Void> wait = executorService.submit(new Callable<Void>() {
+            public Void call() throws Exception 
+            {
+               callUninstall.await();
+               //Make sure uninstall is in progress 
+               TimeUnit.MILLISECONDS.sleep(750);
+               waitForMainLatch.countDown();
+               return null;
+            }
+         });
+         callUninstall.countDown();
+         uninstallContext(ctx);
+         wait.get();
+         
+         ctx = null;
+         assertEquals(1, delegate.describeInstallOrder);
+         assertEquals(2, delegate.instantiateInstallOrder);
+         assertEquals(3, delegate.configureInstallOrder);
+         assertEquals(4, delegate.configureUninstallOrder);
+         assertEquals(5, delegate.instantiateUninstallOrder);
+         assertEquals(6, delegate.describeUninstallOrder);
+      }
+      finally
+      {
+         try
+         {
+            uninstallContext(ctx);
+         }
+         finally
+         {
+            clearExecutorInController();
+         }
+      }
+   }
+   
+   public void testUninstallContextWithAsynchronousDependencyInProgress_Configured_Configured() throws Throwable
+   {
+      TestDelegate expectedDependency = new TestDelegate("D");
+      TestDelegate expectedBean = new TestDelegate("DB");
+      expectedDependency.describeInstallOrder = 1;
+      expectedDependency.instantiateInstallOrder = 2;
+      expectedDependency.configureInstallOrder = 3;
+      expectedDependency.createInstallOrder = 4;
+      expectedDependency.startInstallOrder = 5;
+      expectedDependency.installInstallOrder = 6;
+
+      expectedBean.describeInstallOrder = 7;
+      expectedBean.instantiateInstallOrder = 8;
+      expectedBean.configureInstallOrder = 9;
+      expectedBean.configureUninstallOrder = 10;
+
+      expectedDependency.installUninstallOrder = 11;
+      expectedDependency.startUninstallOrder = 12;
+      expectedDependency.createUninstallOrder = 13;
+      expectedDependency.configureUninstallOrder = 14;
+      expectedDependency.instantiateUninstallOrder = 15;
+      expectedDependency.describeUninstallOrder = 16;
+      
+      runUninstallContextWithAsynchronousDependencyInProgress(ControllerState.CONFIGURED, ControllerState.CONFIGURED, expectedDependency, expectedBean);
+   }
+
+   public void testUninstallContextWithAsynchronousDependencyInProgress_Configured_Start() throws Throwable
+   {
+      TestDelegate expectedDependency = new TestDelegate("D");
+      TestDelegate expectedBean = new TestDelegate("DB");
+      expectedDependency.describeInstallOrder = 1;
+      expectedDependency.instantiateInstallOrder = 2;
+      expectedDependency.configureInstallOrder = 3;
+      expectedDependency.createInstallOrder = 4;
+      expectedDependency.startInstallOrder = 5;
+      expectedDependency.installInstallOrder = 6;
+
+      expectedBean.describeInstallOrder = 7;
+      expectedBean.instantiateInstallOrder = 8;
+      expectedBean.configureInstallOrder = 9;
+      expectedBean.createInstallOrder = 10;
+      expectedBean.startInstallOrder = 11;
+      expectedBean.startUninstallOrder = 12;
+      expectedBean.createUninstallOrder = 13;
+      expectedBean.configureUninstallOrder = 14;
+
+      expectedDependency.installUninstallOrder = 15;
+      expectedDependency.startUninstallOrder = 16;
+      expectedDependency.createUninstallOrder = 17;
+      expectedDependency.configureUninstallOrder = 18;
+      expectedDependency.instantiateUninstallOrder = 19;
+      expectedDependency.describeUninstallOrder = 20;
+      
+      runUninstallContextWithAsynchronousDependencyInProgress(ControllerState.CONFIGURED, ControllerState.START, expectedDependency, expectedBean);
+   }
+
+   public void testUninstallContextWithAsynchronousDependencyInProgress_Create_Start() throws Throwable
+   {
+      TestDelegate expectedDependency = new TestDelegate("D");
+      TestDelegate expectedBean = new TestDelegate("DB");
+      expectedDependency.describeInstallOrder = 1;
+      expectedDependency.instantiateInstallOrder = 2;
+      expectedDependency.configureInstallOrder = 3;
+      expectedDependency.createInstallOrder = 4;
+      expectedDependency.startInstallOrder = 5;
+      expectedDependency.installInstallOrder = 6;
+
+      expectedBean.describeInstallOrder = 7;
+      expectedBean.instantiateInstallOrder = 8;
+      expectedBean.configureInstallOrder = 9;
+      expectedBean.createInstallOrder = 10;
+      expectedBean.startInstallOrder = 11;
+      expectedBean.startUninstallOrder = 12;
+      expectedBean.createUninstallOrder = 13;
+
+      expectedDependency.installUninstallOrder = 14;
+      expectedDependency.startUninstallOrder = 15;
+      expectedDependency.createUninstallOrder = 16;
+      expectedDependency.configureUninstallOrder = 17;
+      expectedDependency.instantiateUninstallOrder = 18;
+      expectedDependency.describeUninstallOrder = 19;
+      
+      runUninstallContextWithAsynchronousDependencyInProgress(ControllerState.CREATE, ControllerState.START, expectedDependency, expectedBean);
+   }
+
+   public void testUninstallContextWithAsynchronousDependencyInProgress_Configured_Described() throws Throwable
+   {
+      TestDelegate expectedDependency = new TestDelegate("D");
+      TestDelegate expectedBean = new TestDelegate("DB");
+      expectedDependency.describeInstallOrder = 1;
+      expectedDependency.instantiateInstallOrder = 2;
+      expectedDependency.configureInstallOrder = 3;
+      expectedDependency.createInstallOrder = 4;
+      expectedDependency.startInstallOrder = 5;
+      expectedDependency.installInstallOrder = 6;
+
+      expectedDependency.installUninstallOrder = 7;
+      expectedDependency.startUninstallOrder = 8;
+      expectedDependency.createUninstallOrder = 9;
+      expectedDependency.configureUninstallOrder = 10;
+      expectedDependency.instantiateUninstallOrder = 11;
+      expectedDependency.describeUninstallOrder = 12;
+
+      expectedBean.describeInstallOrder = 13;
+      expectedBean.instantiateInstallOrder = 14;
+
+      runUninstallContextWithAsynchronousDependencyInProgress(ControllerState.CONFIGURED, ControllerState.DESCRIBED, expectedDependency, expectedBean);
+   }
+
+   private void runUninstallContextWithAsynchronousDependencyInProgress(
+         ControllerState whenRequiredState, 
+         ControllerState waitInState,
+         TestDelegate expectedDependency,
+         TestDelegate expectedBean) throws Throwable
+   {
+      setExecutorInController(1);
+      
+      final CountDownLatch waitForMainLatch = new CountDownLatch(1);
+      final CountDownLatch signalInWaitStateLatch = new CountDownLatch(1);
+      final CountDownLatch callUninstall = new CountDownLatch(1);
+      
+      UninstallAsynchronousInProgressTestDelegate dependencyDelegate = new UninstallAsynchronousInProgressTestDelegate("Dependency");
+      
+      UninstallAsynchronousInProgressTestDelegate beanDelegate = new UninstallAsynchronousInProgressTestDelegate("Bean", signalInWaitStateLatch, waitInState, waitForMainLatch);
+      beanDelegate.addDependency(new AbstractDependencyItem("Bean", "Dependency", whenRequiredState, ControllerState.INSTALLED));
+      beanDelegate.setMode(ControllerMode.ASYNCHRONOUS);
+
+      ControllerContext dependencyContext = null;
+      ControllerContext beanContext = null;
+      try
+      {
+         dependencyContext = createAndInstallContext(dependencyDelegate, ControllerState.INSTALLED);
+         beanContext = createAndInstallContext(beanDelegate, ControllerState.INSTALLED);
+         signalInWaitStateLatch.await();
+         if (beanContext.getState() == ControllerState.INSTALLED)
+         {
+            fail("Should have been installed asynchronously");
+         }
+         ExecutorService executorService = Executors.newFixedThreadPool(1);
+         Future<Void> wait = executorService.submit(new Callable<Void>() {
+            public Void call() throws Exception 
+            {
+               callUninstall.await();
+               //Make sure uninstall is in progress 
+               TimeUnit.MILLISECONDS.sleep(200);
+               waitForMainLatch.countDown();
+               //Give tasks time to finish
+               TimeUnit.MILLISECONDS.sleep(200);
+               return null;
+            }
+         });
+         callUninstall.countDown();
+
+         uninstallContext(dependencyContext);
+         
+         wait.get();
+         
+         assertEquals(expectedDependency.describeInstallOrder, dependencyDelegate.describeInstallOrder);
+         assertEquals(expectedDependency.instantiateInstallOrder, dependencyDelegate.instantiateInstallOrder);
+         assertEquals(expectedDependency.configureInstallOrder, dependencyDelegate.configureInstallOrder);
+         assertEquals(expectedDependency.createInstallOrder, dependencyDelegate.createInstallOrder);
+         assertEquals(expectedDependency.startInstallOrder, dependencyDelegate.startInstallOrder);
+         assertEquals(expectedDependency.installInstallOrder, dependencyDelegate.installInstallOrder);
+         assertEquals(expectedDependency.installUninstallOrder, dependencyDelegate.installUninstallOrder);
+         assertEquals(expectedDependency.startUninstallOrder, dependencyDelegate.startUninstallOrder);
+         assertEquals(expectedDependency.createUninstallOrder, dependencyDelegate.createUninstallOrder);
+         assertEquals(expectedDependency.configureUninstallOrder, dependencyDelegate.configureUninstallOrder);
+         assertEquals(expectedDependency.instantiateUninstallOrder, dependencyDelegate.instantiateUninstallOrder);
+         assertEquals(expectedDependency.describeUninstallOrder, dependencyDelegate.describeUninstallOrder);
+
+         
+         assertEquals(expectedBean.describeInstallOrder, beanDelegate.describeInstallOrder);
+         assertEquals(expectedBean.instantiateInstallOrder, beanDelegate.instantiateInstallOrder);
+         assertEquals(expectedBean.configureInstallOrder, beanDelegate.configureInstallOrder);
+         assertEquals(expectedBean.createInstallOrder, beanDelegate.createInstallOrder);
+         assertEquals(expectedBean.startInstallOrder, beanDelegate.startInstallOrder);
+         assertEquals(expectedBean.installInstallOrder, beanDelegate.installInstallOrder);
+         assertEquals(expectedBean.installUninstallOrder, beanDelegate.installUninstallOrder);
+         assertEquals(expectedBean.startUninstallOrder, beanDelegate.startUninstallOrder);
+         assertEquals(expectedBean.createUninstallOrder, beanDelegate.createUninstallOrder);
+         assertEquals(expectedBean.configureUninstallOrder, beanDelegate.configureUninstallOrder);
+         assertEquals(expectedBean.instantiateUninstallOrder, beanDelegate.instantiateUninstallOrder);
+         assertEquals(expectedBean.describeUninstallOrder, beanDelegate.describeUninstallOrder);
+      }
+      finally
+      {
+         try
+         {
+            uninstallContext(beanContext);
+         }
+         finally
+         {
+            clearExecutorInController();
+         }
+      }
+   }
+   
    private void setExecutorInController(int threads)
    {
       Executor executor = null;
@@ -829,7 +1085,7 @@
       return result;
    }
    
-   private ControllerContext createAndInstallContext(AsynchronousTestDelegate delegate, ControllerState expected) throws Throwable
+   private ControllerContext createAndInstallContext(TestDelegate delegate, ControllerState expected) throws Throwable
    {
       TestControllerContext context = new TestControllerContext(delegate);
       controller.install(context);
@@ -901,5 +1157,5 @@
          count.incrementAndGet();
          throw new RejectedExecutionException();
       }
-   }
+   } 
 }




More information about the jboss-cvs-commits mailing list