Author: gtrasuk
Date: Mon May 27 01:21:24 2013
New Revision: 1486497

URL: http://svn.apache.org/r1486497
Log:
Can successfully start, stop and restart reggie as a hosted application through 
the JMX interface.  Can also shut down the container through the JMX interface. 
 Currently, the container shutdown is not particularly clean - it notifies all 
components that shutdown is happening, but doesn't take into account any order 
of shutdown, or waiting for apps to close.  Currently ends with a 
System.exit(0).  However, it's probably good enough for many purposes.

Removed:
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/TaskClass.java
Modified:
    river/jtsk/skunk/surrogate/nbproject/genfiles.properties
    river/jtsk/skunk/surrogate/src/org/apache/river/container/MessageNames.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/ShutdownListener.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/Strings.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/codebase/ClassServer.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/ApplicationEnvironment.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceDeployer.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StatusEvents.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/BasicWorkManager.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/ContextualWorkManager.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkManager.java
    
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkingContext.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedMachineTest.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSM.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSMInterface.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java
    river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSMInterface.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/BasicWorkManagerTest.java
    
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/ContextualWorkManagerTest.java
    river/jtsk/skunk/surrogate/testfiles/testroot/profile/default/config.xml

Modified: river/jtsk/skunk/surrogate/nbproject/genfiles.properties
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/nbproject/genfiles.properties?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/nbproject/genfiles.properties (original)
+++ river/jtsk/skunk/surrogate/nbproject/genfiles.properties Mon May 27 
01:21:24 2013
@@ -5,7 +5,7 @@ [email protected]
 # Do not edit this file. You may delete it but then the IDE will never 
regenerate such files for you.
 nbproject/build-impl.xml.data.CRC32=8f1c53be
 nbproject/build-impl.xml.script.CRC32=4f16d38a
-nbproject/[email protected]
+nbproject/[email protected]
 nbproject/management-build-impl.xml.data.CRC32=318d2fde
 nbproject/management-build-impl.xml.script.CRC32=630dcf8f
 nbproject/[email protected]

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/MessageNames.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/MessageNames.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/MessageNames.java 
(original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/MessageNames.java 
Mon May 27 01:21:24 2013
@@ -74,6 +74,8 @@ public class MessageNames {
             CREATED_THREAD="createdThread",
             DUPLICATE_CLASSPATH="duplicateClasspath",
             EXCEPTION_THROWN="exceptionThrown",
+            EXCEPTION_WHILE_STOPPING="exceptionWhileStopping",
+            FAILED_CLEAN_SHUTDOWN="failedCleanShutdown",
             FAILED_DEPLOY_SERVICE="failedDeployService",
             FAILED_READ_PROPERTIES="failedReadProperties",
             FOUND_NO_SERVICE_ARCHIVES="foundNoServiceArchives",
@@ -89,6 +91,7 @@ public class MessageNames {
             INJECT="inject",
             MISSING_PROPERTY_ENTRY="missingPropertyEntry",
             MISSING_SPECIAL_VALUE="missingSpecialValue",
+            N_THREADS_LEFT="nThreadsLeft",
             NO_DEPLOYMENT_DIRECTORY="noDeploymentDirectory",
             PARENT_CLASS_LOADER_IS="parentClassLoaderIs",
             POLICY_DECLINED="policyDeclined",
@@ -106,6 +109,7 @@ public class MessageNames {
             SECURITY_INIT_WRONG_POLICY="securityInitializationWrongPolicy",
             SERVICE_PARENT_CLASSLOADER_IS="serviceParentClassloaderIs",
             SHOW_COMMAND_LINE_ARGUMENTS="showCommandLineArguments",
+            SHUTDOWN_FAILED="shutdownFailed",
             SHUTDOWN_METHOD_HAS_PARAMETERS="shutdownMethodHasParameters",
             SHUTDOWN_METHOD_NOT_VOID="shutdownMethodIsntVoid",
             
STARTER_SERVICE_DEPLOYER_FAILED_INIT="starterServiceDeployerFailedInit",

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties 
(original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties 
Mon May 27 01:21:24 2013
@@ -53,6 +53,8 @@ contextItem=Context key {0} refers to ''
 createdThread=Created thread named ''{0}'' in thread group ''{1}''.
 duplicateClasspath=Duplicate class path entry for id ''{0}''.
 exceptionThrown=Exception thrown:\n{0}
+exceptionWhileStopping=Exception thrown during stop operation:\n{0}
+failedCleanShutdown=Application ''{0}'' failed to shutdown cleanly, so we're 
interrupting it.
 failedDeployService=Deployment of service archive at ''{0}'' failed.
 failedReadProperties=Failed to read one or more properties files.
 foundNoServiceArchives=Found no service archives for deployment dir ''{0}''.
@@ -73,6 +75,7 @@ missingSpecialValue=Deployer configurati
 ''{1}'' to be created with value expression ''{2}'', but the value resolves to 
null.\n\
 This is unlikely to be the desired behavior, so check to see if you''re 
missing\n\
 value ''{2}'' in other configurations or command line parameters.
+nThreadsLeft=Application ''{0}'' has {1} threads currently running.
 noDeploymentDirectory=No deployment directory called {0} found in {1}; \
 skipping deployment.
 parentClassLoaderIs=Parent class loader is {0}.
@@ -91,6 +94,7 @@ securityInitializationSucceeded=Security
 securityInitializationWrongPolicy=After security manager setup, the wrong 
policy is installed: {0}.
 serviceParentClassloaderIs=Parent of service classloader is {0}.
 showCommandLineArguments=Command line arguments were: {0}.
+shutdownFailed=Application ''{0}'' has failed to shut down - there are threads 
still running.
 shutdownMethodHasParameters=A method flagged as @Shutdown must take no 
parameters.  \
 Method ''{1}'' on class ''{0}'' has parameters.
 shutdownMethodIsntVoid=A method flagged as @Shutdown must be void return type. 
 \

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/ShutdownListener.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/ShutdownListener.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/ShutdownListener.java 
(original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/ShutdownListener.java 
Mon May 27 01:21:24 2013
@@ -34,6 +34,7 @@ public class ShutdownListener implements
             try {
                 wait();
                 context.shutDown();
+                System.exit(0);
             } catch (InterruptedException ex) {
                 
Logger.getLogger(ShutdownListener.class.getName()).log(Level.SEVERE, null, ex);
             }

Modified: river/jtsk/skunk/surrogate/src/org/apache/river/container/Strings.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/Strings.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/Strings.java 
(original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/Strings.java Mon 
May 27 01:21:24 2013
@@ -44,6 +44,7 @@ public class Strings {
             DOT_PROPERTIES=".properties",
             DOT_SSAR=".ssar",
             EMPTY = "",
+            GET_ADMIN="getAdmin",
             FILE_UTILITY="fileUtility",
             INIT_COMPLETE="initComplete",
             JAR="jar",

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/codebase/ClassServer.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/codebase/ClassServer.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/codebase/ClassServer.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/codebase/ClassServer.java
 Mon May 27 01:21:24 2013
@@ -42,7 +42,6 @@ import org.apache.river.container.Inject
 import org.apache.river.container.LocalizedRuntimeException;
 import org.apache.river.container.MessageNames;
 import org.apache.river.container.Shutdown;
-import org.apache.river.container.work.TaskClass;
 import org.apache.river.container.work.WorkManager;
 
 /**
@@ -91,7 +90,7 @@ public class ClassServer implements Code
     public void init() {
         try {
             establishServerSocket();
-            workManager.queueTask(TaskClass.SYSTEM_TASK, 
Thread.currentThread().getContextClassLoader(),
+            
workManager.queueTask(Thread.currentThread().getContextClassLoader(),
                     new Runnable() {
 
                         @Override
@@ -144,7 +143,7 @@ public class ClassServer implements Code
                 /*
                  * Boy, would this be a nice spot to have closures!
                  */
-                workManager.queueTask(TaskClass.SYSTEM_TASK,
+                workManager.queueTask(
                         Thread.currentThread().getContextClassLoader(),
                         new Runnable() {
 

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/ApplicationEnvironment.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/ApplicationEnvironment.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/ApplicationEnvironment.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/ApplicationEnvironment.java
 Mon May 27 01:21:24 2013
@@ -21,6 +21,7 @@ package org.apache.river.container.deplo
 import org.apache.commons.vfs.FileObject;
 import org.apache.river.container.classloading.VirtualFileSystemClassLoader;
 import org.apache.river.container.codebase.CodebaseContext;
+import org.apache.river.container.work.WorkingContext;
 
 /**
  * Everything the host needs to know about the surrogate.
@@ -84,4 +85,14 @@ public class ApplicationEnvironment {
         this.serviceInstance = serviceInstance;
     }
     
+    WorkingContext workingContext=null;
+
+    public WorkingContext getWorkingContext() {
+        return workingContext;
+    }
+
+    public void setWorkingContext(WorkingContext workingContext) {
+        this.workingContext = workingContext;
+    }
+    
 }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceDeployer.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceDeployer.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceDeployer.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceDeployer.java
 Mon May 27 01:21:24 2013
@@ -39,7 +39,6 @@ import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import net.jini.security.policy.DynamicPolicyProvider;
-import net.jini.security.Security;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileType;
@@ -61,7 +60,7 @@ import org.apache.river.container.codeba
 import org.apache.river.container.codebase.CodebaseHandler;
 import org.apache.river.container.el.ArgsParser;
 import org.apache.river.container.liaison.VirtualFileSystemConfiguration;
-import org.apache.river.container.work.TaskClass;
+import org.apache.river.container.work.ContextualWorkManager;
 import org.apache.river.container.work.WorkManager;
 
 /**
@@ -100,6 +99,9 @@ public class StarterServiceDeployer impl
     @Injected(style = InjectionStyle.BY_TYPE)
     WorkManager workManager = null;
     
+    @Injected(style=InjectionStyle.BY_TYPE)
+    ContextualWorkManager contextualWorkManager=null;
+    
     @Injected(style = InjectionStyle.BY_TYPE)
     private DynamicPolicyProvider securityPolicy = null;
 
@@ -220,19 +222,9 @@ public class StarterServiceDeployer impl
                 }
             }
         };
-        workManager.queueTask(TaskClass.APPLICATION_TASK, 
env.getClassLoader(), task);
+        
env.getWorkingContext().getWorkManager().queueTask(env.getClassLoader(), task);
     }
 
-    public void stopService(ApplicationEnvironment env) {
-        /*
-         * TODO: Write the implementation of this method.
-         * Should do something like:
-         *  - Call destroy() on the service proxy if possible.
-         *  - Check to make sure all the threads are gone.
-         *  - Interrupt the threads if necessary.
-         */
-    }
-    
     public Properties readStartProperties(FileObject serviceRoot) throws 
FileSystemException, LocalizedRuntimeException, IOException {
         /*
          Read the start.properties file.
@@ -364,6 +356,10 @@ public class StarterServiceDeployer impl
         grantPermissions(cl, perms);
         setupLiaisonConfiguration(env.getServiceArchive(), 
env.getServiceRoot(), cl);
         
+        /*
+         * Create a working context (work manager).
+         */
+        
env.setWorkingContext(contextualWorkManager.createContext(env.getServiceName()));
     }
     
     void launchService(ApplicationEnvironment env) throws FileSystemException, 
IOException {
@@ -451,4 +447,27 @@ public class StarterServiceDeployer impl
         constructor.setAccessible(true);
         return constructor.newInstance(parms, null);
     }
+    
+    /**
+     * Attempt to stop the service in an orderly fashion.
+     * Go to the service, see if it implements Administrable, then get the 
+     * admin proxy and see if it implements DestroyAdmin.  If so, call it.
+     * @param env 
+     */
+    public void stopService(ApplicationEnvironment env)  {
+        /* Option 1 - Service has a getAdmin() method - it probably implements
+         * Administrable.
+         */
+        Object serviceInstance=env.getServiceInstance();
+        Method getAdmin=null;
+        try {
+            getAdmin=serviceInstance.getClass().getMethod(Strings.GET_ADMIN, 
new Class[0]);
+        } catch (Exception ex) {
+            // Silent catch - leave it null;
+        }
+        if (getAdmin != null) {
+            
+        }
+        
+    }
 }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java
 Mon May 27 01:21:24 2013
@@ -19,17 +19,22 @@ package org.apache.river.container.deplo
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.river.container.MessageNames;
+import org.apache.river.container.Utils;
 import org.apache.river.container.hsm.Controller;
+import org.apache.river.container.hsm.Guard;
 import org.apache.river.container.hsm.Initial;
+import org.apache.river.container.hsm.OnEntry;
+import org.apache.river.container.hsm.OnExit;
 import org.apache.river.container.hsm.PlainStateMachineExecutor;
 import org.apache.river.container.hsm.RootState;
 import org.apache.river.container.hsm.State;
 import org.apache.river.container.hsm.StateMachineInfo;
 import org.apache.river.container.hsm.Transition;
-import org.apache.river.container.work.TaskClass;
 
 /**
  * Life cycle controller for "service-starter" services. Idle --> Starting -->
@@ -38,6 +43,8 @@ import org.apache.river.container.work.T
 @RootState({ServiceLifeCycle.class, StatusEvents.class})
 public class StarterServiceLifeCycleSM {
 
+    public static final int MAX_RETRY_COUNT=10;
+    
     private static final Logger logger = 
Logger.getLogger(StarterServiceLifeCycleSM.class.getName(),
             MessageNames.BUNDLE_NAME);
     private ApplicationEnvironment appEnv = null;
@@ -54,7 +61,9 @@ public class StarterServiceLifeCycleSM {
         machine.lifeCycleProxy = (ServiceLifeCycle) machine.eventProxy;
         return machine.lifeCycleProxy;
     }
-    @State({Idle.class, Preparing.class, Prepared.class, Starting.class, 
Failed.class, Running.class, Stopping.class, Idle.class})
+    @State({Idle.class, Preparing.class, Prepared.class, Starting.class, 
+        Failed.class, Running.class, Stopping.class, DirtyShutdown.class,
+        Idle.class})
     @Initial(Idle.class)
     private Object state;
     @Controller
@@ -88,13 +97,12 @@ public class StarterServiceLifeCycleSM {
                         deployer.prepareService(appEnv);
                         eventProxy.prepareSucceeded();
                         lifeCycleProxy.start();
-                        eventProxy.startSucceeded();
                     } catch (Exception ex) {
                         eventProxy.exception(ex);
                     }
                 }
             };
-            deployer.workManager.queueTask(TaskClass.SYSTEM_TASK, null, 
command);
+            deployer.workManager.queueTask(null, command);
         }
 
         @Transition(Preparing.class)
@@ -111,7 +119,7 @@ public class StarterServiceLifeCycleSM {
                     }
                 }
             };
-            deployer.workManager.queueTask(TaskClass.SYSTEM_TASK, null, 
command);
+            deployer.workManager.queueTask(null, command);
         }
 
     }
@@ -127,14 +135,6 @@ public class StarterServiceLifeCycleSM {
             exceptions.add(ex);
         }
 
-        public void start() {
-            throw new IllegalStateException(getStatus());
-        }
-
-        public void stop() {
-            throw new IllegalStateException(getStatus());
-        }
-
     }
 
     public class Prepared {
@@ -152,7 +152,7 @@ public class StarterServiceLifeCycleSM {
                     }
                 }
             };
-            deployer.workManager.queueTask(TaskClass.SYSTEM_TASK, null, 
command);
+            deployer.workManager.queueTask( null, command);
 
         }
         
@@ -169,26 +169,98 @@ public class StarterServiceLifeCycleSM {
                     /* Prepare the application environment. */
                     try {
                         deployer.stopService(appEnv);
-                        eventProxy.stopSucceeded();
+                        
if(appEnv.getWorkingContext().getActiveThreadCount()==0) {
+                            eventProxy.stopSucceeded();
+                        } else {
+                            eventProxy.stopFailed();
+                        }
                     } catch (Exception ex) {
                         eventProxy.exception(ex);
                     }
                 }
             };
-            deployer.workManager.queueTask(TaskClass.SYSTEM_TASK, null, 
command);
-           
+            deployer.workManager.queueTask(null, command);
         }
     }
 
-    public class Failed {
+    /**
+     * We want the state to show as "Failed" but in reality, you can do all
+     * the same commands as if you were in "Idle".  So we just extend "Idle".
+     */
+    public class Failed extends Idle {
     }
 
     public class Stopping {
         /* TODO: Implement the state machine from here to check for proper
          * shutdown.
          */
+        
+        @Transition(Idle.class) 
+        public void stopSucceeded() {}
+        
+        @Transition(DirtyShutdown.class)
+        public void stopFailed() {}
+        
+        @Guard(Idle.class) 
+        public boolean areThreadsGone() {
+            return appEnv.getWorkingContext().getActiveThreadCount()==0;
+        }
+        
+        public void exception(Exception ex) {
+            logger.log(Level.WARNING, MessageNames.EXCEPTION_WHILE_STOPPING,
+                    new Object[] { Utils.stackTrace(ex) });
+        }
     }
 
+    public class DirtyShutdown {
+        int retryCount=0;
+        
+        @OnEntry
+        public void enter() {
+            try {
+            logger.log(Level.INFO, MessageNames.FAILED_CLEAN_SHUTDOWN, 
+                    new Object[] { appEnv.getServiceName() });
+            retryCount=0;
+            /* Interrupt threads,  then start interval timer to repeat. */
+            appEnv.getWorkingContext().shutdown();
+            setTimer();
+            } catch(Throwable t) {
+                System.out.println("Got exception while entering 
DirtyShutdown");
+                t.printStackTrace();
+            }
+        }
+        
+        @Transition(Idle.class)
+        public void stopped() {}
+        
+        @OnExit
+        public void exit() {
+            clearTimer();
+        }
+        
+        public void timeout() {
+            appEnv.getWorkingContext().shutdown();
+            appEnv.getWorkingContext().interrupt();
+            retryCount++;
+        }
+        
+        @Guard(Failed.class)
+        public boolean isRetryCountExceeded() {
+            if (retryCount > MAX_RETRY_COUNT) {
+                logger.log(Level.INFO, MessageNames.SHUTDOWN_FAILED,
+                        new Object[] { appEnv.getServiceName()});
+            }
+            return retryCount > MAX_RETRY_COUNT;
+        }
+        
+        @Guard(Idle.class) 
+        public boolean areThreadsGone() {
+            int nThreads=appEnv.getWorkingContext().getActiveThreadCount();
+            logger.log(Level.FINE, MessageNames.N_THREADS_LEFT,
+                    new Object[]{ appEnv.getServiceName(), nThreads });
+            return nThreads==0;
+        }
+    }
     public class Starting {
 
         @Transition(Running.class)
@@ -200,4 +272,26 @@ public class StarterServiceLifeCycleSM {
             exceptions.add(ex);
         }
     }
+    
+    ScheduledFuture timer=null;
+    
+    public synchronized void setTimer() {
+        Runnable command=new Runnable() {
+            public void run() {
+                eventProxy.timeout();
+                setTimer();
+            }
+        };
+        clearTimer();
+        // We're shutting down the appEnv's working context, so we need the 
+        // deployer's work manager.
+        timer=deployer.workManager.schedule(null, command, 2, 
TimeUnit.SECONDS);
+    }
+    
+    public synchronized void clearTimer() {
+        if (timer != null) {
+            timer.cancel(true);
+            timer=null;
+        }
+    }
 }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StatusEvents.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StatusEvents.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StatusEvents.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/deployer/StatusEvents.java
 Mon May 27 01:21:24 2013
@@ -28,7 +28,9 @@ interface StatusEvents {
     
     public void stopSucceeded();
     
+    public void stopFailed();
+    
     public void exception(Exception ex);
     
-    public void tick();
+    public void timeout();
 }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java
 Mon May 27 01:21:24 2013
@@ -263,12 +263,17 @@ public class PlainStateMachineExecutor i
     }
 
     private void runEventActions(List<MetaState> activeStates, Method 
eventInterfaceMethod, Object[] args) {
+        boolean thereWasAnEventMethod=false;
         for (MetaState ms : activeStates) {
             Operation op = ms.eventMethods.get(eventInterfaceMethod);
             if (op != null) {
+                thereWasAnEventMethod=true;
                 op.eval(this, args);
             }
         }
+        if(!thereWasAnEventMethod) {
+            exceptions.add(new IllegalStateException());
+        }
     }
 
     private void runGuardMethods(List<MetaState> activeStates) {

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/BasicWorkManager.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/work/BasicWorkManager.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/BasicWorkManager.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/BasicWorkManager.java
 Mon May 27 01:21:24 2013
@@ -20,7 +20,10 @@ package org.apache.river.container.work;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.river.container.Init;
@@ -39,18 +42,24 @@ public class BasicWorkManager implements
 
     private static final Logger log = 
Logger.getLogger(BasicWorkManager.class.getName(), MessageNames.BUNDLE_NAME);
     ExecutorService executor = null;
+    ScheduledExecutorService scheduledExecutor=null;
     private MyThreadFactory threadFactory = null;
     private String name = Strings.UNNAMED;
 
     public BasicWorkManager() {
         threadFactory = new MyThreadFactory();
         executor = Executors.newCachedThreadPool(threadFactory);
+        scheduledExecutor=
+                Executors.newSingleThreadScheduledExecutor(threadFactory);
+        
     }
 
     public BasicWorkManager(String name) {
         this.name = name;
         threadFactory = new MyThreadFactory();
         executor = Executors.newCachedThreadPool(threadFactory);
+        scheduledExecutor=
+                Executors.newSingleThreadScheduledExecutor(threadFactory);
     }
 
     synchronized int getActiveCount() {
@@ -58,12 +67,19 @@ public class BasicWorkManager implements
     }
 
     @Override
-    public void queueTask(TaskClass taskClass, ClassLoader contextClassLoader, 
Runnable task) {
+    public void queueTask(ClassLoader contextClassLoader, Runnable task) {
         ClassLoader classLoaderToUse =
                 contextClassLoader != null ? contextClassLoader : 
Thread.currentThread().getContextClassLoader();
         executor.execute(new TaskHolder(task, classLoaderToUse));
     }
 
+    @Override
+    public ScheduledFuture<?> schedule(ClassLoader contextClassLoader, 
Runnable command, long delay, TimeUnit unit) {
+        ClassLoader classLoaderToUse =
+                contextClassLoader != null ? contextClassLoader : 
Thread.currentThread().getContextClassLoader();
+        return scheduledExecutor.schedule(new TaskHolder(command, 
classLoaderToUse), delay, unit);
+    }
+
     private class TaskHolder implements Runnable {
 
         Runnable task = null;
@@ -95,8 +111,13 @@ public class BasicWorkManager implements
     @Shutdown
     public void shutdown() {
         executor.shutdownNow();
+        scheduledExecutor.shutdownNow();
     }
 
+    public void interrupt() {
+        threadFactory.threadGroup.interrupt();
+    }
+    
     private class MyThreadFactory implements ThreadFactory {
 
         private ThreadGroup threadGroup = new ThreadGroup(name);

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/ContextualWorkManager.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/work/ContextualWorkManager.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/ContextualWorkManager.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/ContextualWorkManager.java
 Mon May 27 01:21:24 2013
@@ -29,7 +29,7 @@ public class ContextualWorkManager {
 
     List<Context> contexts=new ArrayList<Context>();
     
-    WorkingContext createContext(String name) {
+    public WorkingContext createContext(String name) {
         Context context=new Context(name);
         contexts.add(context);
         return context;
@@ -60,8 +60,13 @@ public class ContextualWorkManager {
         }
 
         @Override
+        public void shutdown() {
+            workManager.shutdown();
+        }
+
+        @Override
         public void interrupt() {
-            throw new UnsupportedOperationException("Not supported yet.");
+            workManager.interrupt();
         }
         
     }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkManager.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkManager.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkManager.java 
(original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkManager.java 
Mon May 27 01:21:24 2013
@@ -15,35 +15,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.river.container.work;
 
-/**
-
-    Interface for the container's workload manager.  System objects use an
-instance of this interface to request the container to perform work on their
-behalf.  Using a centralized workload manager allows the container to both
-control the scheduling of the workload and provide instrumentation on the 
-workload that might be useful for debugging or performance management.
-
-TODO: Need to have some way of grouping tasks, then killing off a task
-and all its subtasks (thread groups etc) for shutdown purposes.
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
+/**
+ *
+ * Interface for the container's workload manager. System objects use an
+ * instance of this interface to request the container to perform work on their
+ * behalf. Using a centralized workload manager allows the container to both
+ * control the scheduling of the workload and provide instrumentation on the
+ * workload that might be useful for debugging or performance management.
+ *
+ * TODO: Need to have some way of grouping tasks, then killing off a task and
+ * all its subtasks (thread groups etc) for shutdown purposes.
+ *
  * @author trasukg
  */
 public interface WorkManager {
-    
+
     /**
-    Queue a task for execution.
-    @param taskClass Indicates what type of task this is.  The implementation
-    may use this information to assign the task to one of several execution
-    queues.
-    
-    @param contextClassLoader The context classloader that should be used when
-    running the task.
-    
-    @param task The task to be run.
-    */
-    public void queueTask(TaskClass taskClass, ClassLoader contextClassLoader,
+     * Queue a task for execution.
+     *
+     * @param taskClass Indicates what type of task this is. The implementation
+     * may use this information to assign the task to one of several execution
+     * queues.
+     *
+     * @param contextClassLoader The context classloader that should be used
+     * when running the task.
+     *
+     * @param task The task to be run.
+     */
+    public void queueTask(ClassLoader contextClassLoader,
             Runnable task);
+
+    /**
+     * Schedule a task for future execution
+     *
+     * @param command
+     * @param delay
+     * @param unit
+     * @return
+     */
+    ScheduledFuture<?> schedule(ClassLoader contextClassLoader,
+            Runnable command,
+            long delay,
+            TimeUnit unit);
 }

Modified: 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkingContext.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkingContext.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkingContext.java
 (original)
+++ 
river/jtsk/skunk/surrogate/src/org/apache/river/container/work/WorkingContext.java
 Mon May 27 01:21:24 2013
@@ -38,5 +38,10 @@ public interface WorkingContext {
     /**
     Attempt to stop all threads in the context by interrupting them.
     */
+    void shutdown();
+    
+    /** 
+     * Interrupt all threads associated with this working context.
+     */
     void interrupt();
 }

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedMachineTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedMachineTest.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedMachineTest.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedMachineTest.java
 Mon May 27 01:21:24 2013
@@ -74,6 +74,21 @@ public class InitializedMachineTest {
     }
     
     /**
+     * The "Armed" state subclasses Locked, so the unlocking should continue to
+     * work.
+     */
+    @Test
+    public void testArming() {
+        UUTI.arm();
+        assertTrue("lockedState is not instance of Armed", UUT.lockedState 
instanceof InitializedTestSM.Armed);
+        
+        UUTI.unlock();
+        assertTrue("lockedState is not instance of Unlocked", UUT.lockedState 
instanceof InitializedTestSM.Unlocked);
+        UUTI.setValue(20);
+        
+    }
+    
+    /**
      * Test that the methods are executing against the same instance that we
      * created.
      */

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSM.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSM.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSM.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSM.java
 Mon May 27 01:21:24 2013
@@ -23,37 +23,45 @@ package org.apache.river.container.hsm;
  */
 @RootState(InitializedTestSMInterface.class)
 public class InitializedTestSM {
-        
-    private int value=0;
-    
-    @State({Locked.class, Unlocked.class})
+
+    private int value = 0;
+
+    @State({Locked.class, Unlocked.class, Armed.class})
     @Initial(Locked.class)
     Object lockedState;
 
     public int getValue() {
         return value;
     }
-    
+
     public class Locked {
-        
+
         @Transition(Unlocked.class)
         public void unlock() {
             System.out.println("Locked.unlock()");
         }
-        
+
         public void setValue(int v) {
             throw new IllegalStateException("Locked!");
         }
+
+        @Transition(Armed.class)
+        public void arm() {
+        }
+    }
+
+    public class Armed extends Locked {
     }
-    
+
     public class Unlocked {
+
         @Transition(Locked.class)
         public void lock() {
             System.out.println("Unlocked.lock()");
         }
-        
+
         public void setValue(int v) {
-            value=v;
+            value = v;
         }
     }
 }

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSMInterface.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSMInterface.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSMInterface.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/InitializedTestSMInterface.java
 Mon May 27 01:21:24 2013
@@ -26,6 +26,8 @@ public interface InitializedTestSMInterf
     
     public void unlock();
     
+    public void arm();
+    
     public void setValue(int x);
     
     public int getValue();

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java
 Mon May 27 01:21:24 2013
@@ -225,4 +225,14 @@ public class PlainMachineExecutorTest {
                 activeStates.contains(TestSM.A.class));
         assertEquals("HelloFromC", UUT.sayHello());
     }
+    
+    
+    /**
+     * Calling an event method that isn't implemented in the current state
+     * should throw an IllegalStateException.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testUnimplementedMethod() {
+        UUT.unimplementedMethod();
+    }
 }
\ No newline at end of file

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java 
(original)
+++ river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java 
Mon May 27 01:21:24 2013
@@ -39,6 +39,10 @@ public class TestSM {
     int nullTransitionEntryCount = 0;
     int aEntryCount = 0, aExitCount = 0;
 
+    public Object returnNull() {
+        return null;
+    }
+    
     public List<Class> getActiveStates() {
         try {
             return controller.getActiveStates();

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSMInterface.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSMInterface.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSMInterface.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSMInterface.java
 Mon May 27 01:21:24 2013
@@ -45,4 +45,6 @@ public interface TestSMInterface {
     public void gotoB();
 
     List<Class> getActiveStates();
+    
+    public void unimplementedMethod();
 }

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/BasicWorkManagerTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/work/BasicWorkManagerTest.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/BasicWorkManagerTest.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/BasicWorkManagerTest.java
 Mon May 27 01:21:24 2013
@@ -18,6 +18,7 @@
 
 package org.apache.river.container.work;
 
+import java.util.concurrent.TimeUnit;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -39,12 +40,24 @@ public class BasicWorkManagerTest {
     @Test
     public void testNullContextClassLoader() {
         Harness h=new Harness();
-        UUT.queueTask(TaskClass.SYSTEM_TASK, null, h);
+        UUT.queueTask(null, h);
         waitHarness(4000,h);
         assertEquals("Didn't use current context classloader.", 
                 Thread.currentThread().getContextClassLoader(), h.cl);
     }
     
+    @Test
+    public void testScheduledExecution() {
+        Harness h=new Harness();
+        long startTime=System.currentTimeMillis();
+        UUT.schedule(null, h, 2, TimeUnit.SECONDS);
+        waitHarness(4000,h);
+        assertEquals("Didn't use current context classloader.", 
+                Thread.currentThread().getContextClassLoader(), h.cl);
+        assertTrue("Delay was only " + (h.runTime - startTime),
+                h.runTime >= startTime+2000);
+    }
+    
     private void waitHarness(long time, Harness h) {
         long start=System.currentTimeMillis();
         while ( !h.done && System.currentTimeMillis() - start < time) {
@@ -57,14 +70,16 @@ public class BasicWorkManagerTest {
     
     private class Harness implements Runnable {
         ClassLoader cl=null;
-        boolean done=false;
+        volatile boolean done=false;
         String threadName=null;
+        volatile long runTime=0;
         
         @Override
         public void run() {
             cl=Thread.currentThread().getContextClassLoader();
             threadName=Thread.currentThread().getName();
             done=true;
+            runTime=System.currentTimeMillis();
         }
     }
             

Modified: 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/ContextualWorkManagerTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/work/ContextualWorkManagerTest.java?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/ContextualWorkManagerTest.java
 (original)
+++ 
river/jtsk/skunk/surrogate/test/org/apache/river/container/work/ContextualWorkManagerTest.java
 Mon May 27 01:21:24 2013
@@ -43,7 +43,7 @@ public class ContextualWorkManagerTest {
     @Test
     public void testThreadCount() {
         WorkerRunnable wt = new WorkerRunnable();
-        context.getWorkManager().queueTask(TaskClass.SYSTEM_TASK, null, wt);
+        context.getWorkManager().queueTask(null, wt);
         long start = System.currentTimeMillis();
         while (System.currentTimeMillis() - start < 2000 & 
context.getActiveThreadCount() < 1) {
             Thread.yield();
@@ -55,7 +55,7 @@ public class ContextualWorkManagerTest {
     @Test
     public void testChildThreadGroup() throws Exception {
         WorkerRunnable wt = new WorkerRunnable();
-        context.getWorkManager().queueTask(TaskClass.SYSTEM_TASK, null, wt);
+        context.getWorkManager().queueTask(null, wt);
         long start = System.currentTimeMillis();
         while (System.currentTimeMillis() - start < 2000 & 
context.getActiveThreadCount() < 1) {
             Thread.yield();
@@ -71,7 +71,7 @@ public class ContextualWorkManagerTest {
     @Test
     public void testThreadCountWithChildren() throws Exception {
         WorkerRunnable wt = new WorkerRunnable(2);
-        context.getWorkManager().queueTask(TaskClass.SYSTEM_TASK, null, wt);
+        context.getWorkManager().queueTask(null, wt);
         long start = System.currentTimeMillis();
         while (System.currentTimeMillis() - start < 2000 & 
context.getActiveThreadCount() < 1) {
             Thread.yield();

Modified: 
river/jtsk/skunk/surrogate/testfiles/testroot/profile/default/config.xml
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/testfiles/testroot/profile/default/config.xml?rev=1486497&r1=1486496&r2=1486497&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/testfiles/testroot/profile/default/config.xml 
(original)
+++ river/jtsk/skunk/surrogate/testfiles/testroot/profile/default/config.xml 
Mon May 27 01:21:24 2013
@@ -33,6 +33,7 @@
     <cfg:property name="deploymentDirectory" value="deploy"/>
 
     <cfg:property name="defaultDiscoveryGroup" value="RiverContainerDefault"/>
+    <cfg:component 
class="org.apache.river.container.work.ContextualWorkManager"/>
     <cfg:component class="org.apache.river.container.work.BasicWorkManager"/>
     <cfg:component class="org.apache.river.container.codebase.ClassServer"/>
 


Reply via email to