You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by gt...@apache.org on 2013/01/11 23:06:32 UTC

svn commit: r1432308 - in /river/jtsk/skunk/surrogate: docs/ src/org/apache/river/container/hsm/ test/org/apache/river/container/hsm/

Author: gtrasuk
Date: Fri Jan 11 22:06:32 2013
New Revision: 1432308

URL: http://svn.apache.org/viewvc?rev=1432308&view=rev
Log:
Incomplete work on application deployment state machine.  Need to move the code to a different machine for testing.

Added:
    river/jtsk/skunk/surrogate/docs/StateMachine.txt
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Guard.java
      - copied, changed from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Transition.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeAndTransitionOperation.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeGuardOperation.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeVoidAndTransitionOperation.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MessageNames.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Messages.properties
      - copied, changed from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaState.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaStateVisitor.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Operation.java
      - copied, changed from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineCompiler.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineException.java
      - copied, changed from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/LocalizedRuntimeException.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineExecutor.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/TransitionOnSubstate.java
    river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/LoggingTest.java
    river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java
      - copied, changed from r1427243, river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/SimpleMachineTest.java
    river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/StateMachineCompilerTest.java
Modified:
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/RootState.java
    river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/SubstateInfo.java
    river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/TestSM.java

Added: river/jtsk/skunk/surrogate/docs/StateMachine.txt
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/docs/StateMachine.txt?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/docs/StateMachine.txt (added)
+++ river/jtsk/skunk/surrogate/docs/StateMachine.txt Fri Jan 11 22:06:32 2013
@@ -0,0 +1,52 @@
+Some thinking on the Hierarchical State Machine
+===============================================
+
+The application deployment system includes a hierarchical state machine
+implementation that is intended to be used to help control the deployment of 
+applications.
+
+Use cases:
+
+For instance, an application exists in one of several states: Created, 
+Resolved, Starting, Started, Stopping Gracefully, Stopped, Stopping Forcefully,
+Failed to Stop.  If we have an application that started successfully, then
+we attempt to stop it, it may shut all its threads down properly, which puts it
+into the Stopped state.  Or it may fail to stop all its threads, in which case, 
+we would attempt to interrupt the thread group (Stopping Forcefully).  After a 
+while, if the thread group's active thread count didn't go to zero, it would
+enter the "Failed to Stop" state.  In this state, we would interrupt the thread
+group at regular intervals.  If the thread count happened to go to zero, it
+would go to the Stopped state, otherwise it would just sit there in the "Failed 
+to Stop" state.
+
+The model for the HSM is as follows:
+
+- events on the state machine are expressed as methods defined in the interface.
+- User sends an event by calling a method on a proxy that implements the machine.
+- An event is "run" on the current state hierarchy (top-level state and all
+active substates).
+- Each event can trigger a transition (actually one or more) by calling the 
+'transition' method on the machine implementation.  Perhaps this is a mistake - 
+it might be better if the transition was executed by guard conditions and not 
+by a method call.  Event can also imply a transition.
+- The state can have sub-states, so when we transition to a state, a sub-state
+may also become active, and will need to have its entry code run.  The sub-state
+might be a fresh state on entry, or might have been retained from the last time
+the state was active.  
+- Each state may have an "on-entry" and "on-exit" function, which will be executed
+by the transition code.  These functions could conceivably queue up transitions 
+that need to be executed (maybe this is a problem?). Entry and exit code 
+typically sets up timers or toggles outputs.
+- Each state can have zero or more "guard" methods, each specifying a transition.
+A guard method is run after every event is executed, and determines whether the
+transition is taken.  If the
+guard method returns true, the corresponding transition is executed.  These 
+could also be seen as "anonymous" transitions, as they are not attached to any
+particular event.
+
+Thoughts:
+The idea of the machine's actions queuing their own events and transitions
+leads to a problem of re-entrancy, where we start to need complicated
+mechanisms (e.g. different proxies for external vs internal access).  It's
+probably a bad idea.
+

Modified: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java?rev=1432308&r1=1432307&r2=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java Fri Jan 11 22:06:32 2013
@@ -25,7 +25,7 @@ package org.apache.river.container.hsm;
 interface Evaluator {
 
     /**
-     Run the evaluator agains a set of arguments.
+     Run the evaluator against a set of arguments.
      @param args
      @return
      */

Copied: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Guard.java (from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Transition.java)
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Guard.java?p2=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Guard.java&p1=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Transition.java&r1=1427243&r2=1432308&rev=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Transition.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Guard.java Fri Jan 11 22:06:32 2013
@@ -24,13 +24,14 @@ import java.lang.annotation.RetentionPol
 import java.lang.annotation.Target;
 
 /**
- * Annotation type to indicate an unconditional  transition that is applied after an
- * event method is executed.  
+ * Annotation type to indicate a guard method.  The annotated method must return
+ * a boolean value.  The method is run after the action events are run.  If the
+ * method returns true, the indicated transition is taken.
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
-public @interface Transition {
+public @interface Guard {
 
     /**
      * An array of state classes that reflect the target states for this

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeAndTransitionOperation.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeAndTransitionOperation.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeAndTransitionOperation.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeAndTransitionOperation.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * State machine operation that invokes a method on the target instance
+ * and then queues a transition (or set of transitions).
+ */
+public class InvokeAndTransitionOperation implements Operation {
+
+    private static final Logger log=
+            Logger.getLogger(InvokeAndTransitionOperation.class.getName(),MessageNames.BUNDLE_NAME);
+    
+    
+    Method targetMethod;
+
+    /**
+     * Create an instance that executes an event method that returns a value, but
+     * does not include any transitions.
+     * @param targetMetaState
+     * @param targetMethod 
+     */
+    public InvokeAndTransitionOperation(MetaState targetMetaState, Method targetMethod) {
+        this(targetMetaState, targetMethod, new TransitionOnSubstate[0]);
+    }
+    
+    public InvokeAndTransitionOperation(MetaState targetMetaState, Method targetMethod,  TransitionOnSubstate[] transitions) {
+        this.targetMethod = targetMethod;
+        this.targetMetaState = targetMetaState;
+        this.transitions = transitions;
+    }
+    MetaState targetMetaState;
+    TransitionOnSubstate[] transitions;
+    
+    @Override
+    public void eval(StateMachineExecutor exec, Object[] args) {
+        try {
+            if (log.isLoggable(Level.FINER)) {
+                log.log(Level.FINER, MessageNames.APPLYING_EVENT_TO_STATE,
+                        new Object[]{targetMethod.getName(),
+                            targetMetaState.stateClass.getSimpleName(),
+                            targetMetaState.stateInstance});
+            }
+            exec.output(targetMethod.invoke(targetMetaState.stateInstance, args));
+        } catch(InvocationTargetException e) {
+            exec.exception(e.getCause());
+        } catch (Exception ex) {
+            log.log(Level.SEVERE, MessageNames.APPLYING_EVENT_TO_STATE,
+                    new Object[]{targetMethod.getName(),
+                            targetMetaState.stateClass.getSimpleName(),
+                            targetMetaState.stateInstance});
+            log.log(Level.SEVERE, MessageNames.ERROR_INVOKING_TARGET_METHOD, ex);
+        }
+        // Execute the transitions
+        for (TransitionOnSubstate t:transitions) {
+            exec.queueTransition(t);
+        }
+    }
+    
+}

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeGuardOperation.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeGuardOperation.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeGuardOperation.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeGuardOperation.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * State machine operation that invokes a method on the target instance and then
+ * queues a transition (or set of transitions).
+ */
+public class InvokeGuardOperation implements Operation {
+
+    private static final Logger log =
+            Logger.getLogger(InvokeGuardOperation.class.getName(), MessageNames.BUNDLE_NAME);
+    Method targetMethod;
+
+    /**
+     * Create an instance that executes an event method that returns a value,
+     * but does not include any transitions.
+     *
+     * @param targetMetaState
+     * @param targetMethod
+     */
+    public InvokeGuardOperation(MetaState targetMetaState, Method targetMethod) {
+        this(targetMetaState, targetMethod, new TransitionOnSubstate[0]);
+    }
+
+    public InvokeGuardOperation(MetaState targetMetaState, Method targetMethod, TransitionOnSubstate[] transitions) {
+        this.targetMethod = targetMethod;
+        this.targetMetaState = targetMetaState;
+        this.transitions = transitions;
+    }
+    MetaState targetMetaState;
+    TransitionOnSubstate[] transitions;
+
+    @Override
+    public void eval(StateMachineExecutor exec, Object[] args) {
+        try {
+            if (log.isLoggable(Level.FINER)) {
+                log.log(Level.FINER, MessageNames.RUNNING_GUARD_ON_STATE,
+                        new Object[]{targetMethod.getName(),
+                            targetMetaState.stateClass.getSimpleName(),
+                            targetMetaState.stateInstance});
+            }
+            Boolean b = (Boolean) targetMethod.invoke(targetMetaState.stateInstance, args);
+            if (b.booleanValue()) {
+                // Execute the transitions
+                for (TransitionOnSubstate t : transitions) {
+                    exec.queueTransition(t);
+                }
+            }
+        } catch (InvocationTargetException e) {
+            exec.exception(e.getCause());
+        } catch (Exception ex) {
+            log.log(Level.SEVERE, MessageNames.RUNNING_GUARD_ON_STATE,
+                    new Object[]{targetMethod.getName(),
+                        targetMetaState.stateClass.getSimpleName(),
+                        targetMetaState.stateInstance});
+            log.log(Level.SEVERE, MessageNames.ERROR_INVOKING_TARGET_METHOD, ex);
+        }
+        // Execute the transitions
+        for (TransitionOnSubstate t : transitions) {
+            exec.queueTransition(t);
+        }
+    }
+}

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeVoidAndTransitionOperation.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeVoidAndTransitionOperation.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeVoidAndTransitionOperation.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/InvokeVoidAndTransitionOperation.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * State machine operation that invokes a method on the target instance
+ * and then queues a transition (or set of transitions).
+ */
+public class InvokeVoidAndTransitionOperation implements Operation {
+
+    private static final Logger log=
+            Logger.getLogger(InvokeVoidAndTransitionOperation.class.getName(),MessageNames.BUNDLE_NAME);
+    
+    
+    Method targetMethod;
+
+    /**
+     * Create an instance that executes an event method that returns a value, but
+     * does not include any transitions.
+     * @param targetMetaState
+     * @param targetMethod 
+     */
+    public InvokeVoidAndTransitionOperation(MetaState targetMetaState, Method targetMethod) {
+        this(targetMetaState, targetMethod, new TransitionOnSubstate[0]);
+    }
+    
+    public InvokeVoidAndTransitionOperation(MetaState targetMetaState, Method targetMethod,  TransitionOnSubstate[] transitions) {
+        this.targetMethod = targetMethod;
+        this.targetMetaState = targetMetaState;
+        this.transitions = transitions;
+    }
+    MetaState targetMetaState;
+    TransitionOnSubstate[] transitions;
+    
+    @Override
+    public void eval(StateMachineExecutor exec, Object[] args) {
+        try {
+            if (log.isLoggable(Level.FINER)) {
+                log.log(Level.FINER, MessageNames.APPLYING_EVENT_TO_STATE,
+                        new Object[]{targetMethod.getName(),
+                            targetMetaState.stateClass.getSimpleName(),
+                            targetMetaState.stateInstance});
+            }
+            targetMethod.invoke(targetMetaState.stateInstance, args);
+        } catch(InvocationTargetException e) {
+            exec.exception(e.getCause());
+        } catch (Exception ex) {
+            log.log(Level.SEVERE, MessageNames.APPLYING_EVENT_TO_STATE,
+                    new Object[]{targetMethod.getName(),
+                            targetMetaState.stateClass.getSimpleName(),
+                            targetMetaState.stateInstance});
+            log.log(Level.SEVERE, MessageNames.ERROR_INVOKING_TARGET_METHOD, ex);
+        }
+        // Execute the transitions
+        for (TransitionOnSubstate t:transitions) {
+            exec.queueTransition(t);
+        }
+    }
+    
+}

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MessageNames.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MessageNames.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MessageNames.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MessageNames.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+/**
+ * Message names for the state machine implementation's logging.
+ */
+public class MessageNames {
+
+    public static final String BUNDLE_NAME = "org.apache.river.container.hsm.Messages";
+    public static final String APPLYING_EVENT_TO_STATE = "applyingEventToState",
+            CANT_RESOLVE_TRANSITIONS_FOR_CLASS = "cantResolveTransitionsForClass",
+            ENTRY_METHOD_ISNT_VOID = "entryMethodIsntVoid",
+            ERROR_APPLYING_TRANSITION="errorApplyingTransition",
+            ERROR_CREATING_PROXY="errorCreatingProxy",
+            ERROR_INSTANTIATING="errorInstantiating",
+            ERROR_INVOKING_TARGET_METHOD = "errorInvokingTargetMethod",
+            EXIT_METHOD_ISNT_VOID = "exitMethodIsntVoid",
+            GUARD_METHOD_DOESNT_RETURN_BOOLEAN = "guardMethodDoesntReturnBoolean",
+            MULTIPLE_EXCEPTIONS_THROWN="multipleExceptionsThrown",
+            NO_PARENT_INSTANCE="noParentInstance",
+            RUNNING_GUARD_ON_STATE = "runningGuardOnState",
+            SETTING_INITIAL_SUBSTATE="settingInitialSubstate";
+}

Copied: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Messages.properties (from r1427243, 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/hsm/Messages.properties?p2=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Messages.properties&p1=river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties&r1=1427243&r2=1432308&rev=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/Messages.properties (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Messages.properties Fri Jan 11 22:06:32 2013
@@ -16,90 +16,16 @@
  * limitations under the License.
  */
 
-addedPlatformCodebaseJar=Added platform codebase jar ''{0}''.
-addingClasspathEntry=Adding classpath entry ''{0}''.
-annotatedObjectDeployerHasUnresolvedDependencies=Initialization failed because \
-one or more dependencies flagged with @Injected or @Name annotations \
-are unresolved.
-badClasspathExpression=Bad Classpath Expression {0} {1}.
-badMemberForInjectedAnnotation=@Injected annotation must be applied to a field or\
- setter method only: Member {1} on class {0} doesn't qualify.
-badMemberForNameAnnotation=@Name annotation must be applied to a String field or\
- setter method only: Member {1} on class {0} doesn't qualify.
-basicWorkManagerInitialized=Basic Work Manager was initialized.
-callingMain=Calling main method on class ''{0}'' with arguments {1}.
-cantConvertException=Can''t convert type ''{0}'' to ''{1}''.
-cantReadStartProperties=Can''t read starter configuration file ''{0}'' in ''{1}''.
-circularClasspath=Definition of classpath ''{0}'' contains a circular reference.
-classLoaderIs=ClassLoader for class {0} is {1}.
-classpathUndefined=No definition found for classpath id ''{0}''.
-classServerBadRequest=bad request \"{0}\" from {1}:{2}
-classServerErrorAcceptingConnections=Class Server was terminated due to IOException while accepting connections.
-classServerEstablished=Class Server established on host {0} port {1}.
-classServerExceptionDuringShutdown=Class Server caught an exception during shutdown, which was ignored.
-classServerExceptionGettingBytes=Class Server caught an exception while getting bytes to serve the request.
-classServerExceptionWritingResponse=Class Server caught an exception writing the response, so terminated the response.
-classServerInitFailed=Class Server initialization failed.
-classServerNoContentFound=Class Server has no content for path ''{0}''.
-classServerRejectedPath=Rejected request for path ''{0}'' (returning 404).
-classServerReceivedProbe={0} probed from {1}:{2}
-classServerReceivedRequest={0} requested from {1}:{2}
-classServerTerminated=Class Server terminated as part of normal shutdown on host {0} port {1}.
-codeSourceIs=CodeSource for service in ''{0}'' is ''{1}''.
-completedServiceDeployment=Completed deployment of service in {0}.
-configFile=Configuration file is ''{0}''.
-configuredClasspath=The configured classpath with id ''{0}'' is {1}.
-contextItem=Context key {0} refers to ''{1}''.
-createdThread=Created thread named ''{0}'' in thread group ''{1}''.
-duplicateClasspath=Duplicate class path entry for id ''{0}''.
-exceptionThrown=Exception thrown:
-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}''.
-foundServiceArchives=Found {0} service archives in deployment dir ''{1}''.
-illegalArgumentException=An operation threw an IllegalArgumentException.
-illegalAccessException=An operation threw an IllegalAccessException.
-invalidClasspathEntry=Invalid classpath entry: {0}
-invocationTargetException=An operation threw an InvocationTargetException.
-initializingEventTable=Initializing event table for class ''{0}''.
-initializationException=An exception has occured while initializing the container.
-initMethodHasParameters=A method flagged as @Init must take no parameters.  \
-Method ''{1}'' on class ''{0}'' has parameters.
-initMethodIsntVoid=A method flagged as @Init must be void return type.  \
-Method ''{1}'' on class ''{0}'' returns ''{2}''.
-inject=Injecting {2} into member ''{1}'' of deployed object {0}.
-missingPropertyEntry="Properties file ''{0}'' is missing entry for ''{1}''.
-missingSpecialValue=Deployer configuration file ''{0}'' calls for a special entry called\n\
-''{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.
-noDeploymentDirectory=No deployment directory called {0} found in {1}; \
-skipping deployment.
-parentClassLoaderIs=Parent class loader is {0}.
-policyDeclined=No permissions granted to protection domain with classloader {0}.
-profileConfigurationException=Failed to read the configuration for profile {0}.
-profileConfigLoading=Loading the profile configuration with classloader {0}.
-readProperties=...properties read were {0}.
-readPropertiesFile=Read properties file ''{0}''.
-readingObject=Reading instance of {1} named ''{0}'' for unresolved dependencies.
-readingObject.memberCount={0} members found.
-readingObject.annotatedMemberFound=Member ''{0}'' is annotated @Injected.
-readingObject.nonAnnotatedMemberFound=Member ''{0}'' is not annotated @Injected.
-securityInitializationFailed=Failed to initialize security subsystem.
-securityInitializationSucceeded=Security Manager and Dynamic Policy successfully installed.            
-securityInitializationWrongPolicy=After security manager setup, the wrong policy is installed: {0}.
-serviceParentClassloaderIs=Parent of service classloader is {0}.
-showCommandLineArguments=Command line arguments were: {0}.
-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.  \
-Method ''{1}'' on class ''{0}'' returns ''{2}''.
-starterServiceDeployerFailedInit=Starter-Service deployer has failed to initialize.
-starterServiceDeployerInitialized=Starter-Service deployer named ''{0}'' completed \
-initialization.
-starterServiceDeployerStarting=Starter-Service deployer named ''{0}'' is being \
-initialized.
-systemClassLoaderIs=System classloader is ''{0}'' with classpath {1}.
-unresolvedDependency=Object {0} has an unresolved dependent member ''{1}'' \
-(name=''{2}'').
-unsupportedElement=Element type {0} is currently unsupported.
+applyingEventToState=Applying event {0} to state {1} instance {2}.
+cantResolveTransitionsForClass=Can''t find any valid targets for transition ''{1}'' from the context of state class ''{0}''.
+entryMethodIsntVoid=Methods annotated with @OnEntry need to return void.  Method {0} returns {1}.
+errorApplyingTransition=Got an error while applying a transition: {0}.
+errorCreatingProxy=Error Creating Proxy.
+errorInvokingTargetMethod=Error invoking target method on state instance.
+errorInstantiating=Error while instantiating the state machine.
+exitMethodIsntVoid=Methods annotated with @OnExit need to return void.  Method {0} returns {1}.
+guardMethodDoesntReturnBoolean=Methods annotated with @Guard need to return a boolean value.  Method {0} returns {1}.
+multipleExceptionsThrown=Execution of the event threw multiple exceptions.
+noParentInstance=Couldn''t find a parent instance for {0} starting at state class {1}.
+runningGuardOnState=Running guard method {0} on state {1} instance {2}.
+settingInitialSubstate=Setting initial substate field {0} on state {1}.

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaState.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaState.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaState.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaState.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An object that corresponds to a state instance.
+ */
+public class MetaState implements StateMachine {
+    
+    Object stateInstance=null;
+    Class stateClass=null;
+    MetaState parent=null;
+    
+    /** This definition will disappear when we banish the transition() method. */
+    @Override
+    public void transition(Class targetState) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public List<Class> getActiveStates() throws IllegalArgumentException, IllegalAccessException {
+        List<Class> activeStates=new ArrayList<Class>();
+        getActiveStates(activeStates);
+        return activeStates;
+    }
+    
+    public void getActiveStates(List<Class> activeStates) {
+        /* Add my stateInstance's class. */
+        activeStates.add(stateClass);
+        /* Iterate down for each substate. */
+        for(SubstateInfo substate: substates) {
+            substate.getActiveMetaState().getActiveStates(activeStates);
+        }
+    }
+    
+    List<SubstateInfo> substates=new ArrayList<SubstateInfo>();
+    
+    Map<Method, Operation> eventMethods=new HashMap<Method, Operation>();
+    
+    public void visitAll(MetaStateVisitor visitor) {
+        visitor.visit(this);
+        for (SubstateInfo substateInfo: substates) {
+            for (MetaState ms: substateInfo.getPossibleMetaStates()) {
+                visitor.visit(ms);
+            }
+        }
+    }
+    
+    List<Operation> guardMethods=new ArrayList<Operation>();
+
+    List<Operation> entryMethods=new ArrayList<Operation>();
+    
+    List<Operation> exitMethods=new ArrayList<Operation>();
+    
+    List<TransitionOnSubstate> entryTransitions=new ArrayList<TransitionOnSubstate>();
+}

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaStateVisitor.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaStateVisitor.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaStateVisitor.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/MetaStateVisitor.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,13 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.apache.river.container.hsm;
+
+/**
+ *
+ * @author trasukg
+ */
+public interface MetaStateVisitor {
+    public void visit(MetaState metaState);
+}

Copied: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Operation.java (from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java)
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Operation.java?p2=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Operation.java&p1=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java&r1=1427243&r2=1432308&rev=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Evaluator.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/Operation.java Fri Jan 11 22:06:32 2013
@@ -22,13 +22,13 @@ package org.apache.river.container.hsm;
  Interface for an object that evaluates "something" against a set of arguments.
  * @author trasukg
  */
-interface Evaluator {
+interface Operation {
 
     /**
-     Run the evaluator agains a set of arguments.
+     Run the operation against a set of arguments.
      @param args
      @return
      */
-    void eval(Object[] args);
+    void eval(StateMachineExecutor exec, Object[] args);
 
 }

Added: 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=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/PlainStateMachineExecutor.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+public class PlainStateMachineExecutor implements StateMachineExecutor {
+    
+    private static final Logger log =
+            Logger.getLogger(PlainStateMachineExecutor.class.getName(),
+            MessageNames.BUNDLE_NAME);
+
+    /**
+     * Convenience method to compile a state machine and instantiate it.
+     *
+     * @param rootStateClass
+     * @return
+     */
+    public static Object createProxy(Class rootStateClass) {
+        StateMachineCompiler compiler = new StateMachineCompiler();
+        try {
+            MetaState rootState = compiler.compile(rootStateClass);
+            instantiate(rootState);
+            return createProxy(rootState);
+        } catch (StateMachineException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new StateMachineException(ex, MessageNames.BUNDLE_NAME, MessageNames.ERROR_CREATING_PROXY);
+        }
+    }
+
+    /**
+     * Create a fully-instantiated metastate from a compiled metastate.
+     *
+     * @param rootState
+     */
+    public static void instantiate(MetaState rootState) {
+        /* Create a user class instance to go with every metastate. */
+        rootState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState metaState) {
+                try {
+                    /*
+                     * Goal here is to create a stateInstance for each metastate.
+                     * In the simple case, the stateClass is a root class, and we can
+                     * just instantiate it.
+                     */
+                    if (metaState.stateClass.getEnclosingClass() == null) {
+                        metaState.stateInstance = metaState.stateClass.newInstance();
+                    } else {
+                        /* OK, we need to get an instance of the enclosing class.
+                         * That should be the stateInstance of a parent state.
+                         */
+                        Object parentInstance = findAParentInstance(metaState, metaState.stateClass.getEnclosingClass());
+                        
+                        Constructor con = metaState.stateClass.getConstructor(parentInstance.getClass());
+                        metaState.stateInstance = con.newInstance(parentInstance);
+                    }
+                } catch (Exception ex) {
+                    throw new StateMachineException(ex, MessageNames.BUNDLE_NAME, MessageNames.ERROR_INSTANTIATING);
+                }
+            }
+        });
+        /* Now set all the initial state instance values. */
+        rootState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState metaState) {
+                try {
+                    for (SubstateInfo ssi : metaState.substates) {
+                        System.out.println("Setting field " + ssi.getField().getName() + " on "
+                                + metaState.stateClass);
+                        System.out.println("metastate's instance is " + metaState.stateInstance + ", substate value is " + ssi.getInitialMetaState().stateInstance);
+                        ssi.getField().set(metaState.stateInstance, ssi.getInitialMetaState().stateInstance);
+                    }
+                } catch (Exception ex) {
+                    throw new StateMachineException(ex, MessageNames.BUNDLE_NAME, MessageNames.ERROR_INSTANTIATING);
+                }
+            }
+        });
+    }
+    
+    private static Object findAParentInstance(MetaState metaState, Class enclosingClass) {
+        for (MetaState currentState = metaState.parent; currentState != null; currentState = currentState.parent) {
+            if (currentState.stateClass == enclosingClass) {
+                return currentState.stateInstance;
+            }
+        }
+        throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.NO_PARENT_INSTANCE,
+                new Object[]{enclosingClass, metaState.stateInstance});
+    }
+    
+    public static Object createProxy(MetaState instantiatedMetaState) {
+        RootState rootStateAnnotation = (RootState) instantiatedMetaState.stateClass.getAnnotation(RootState.class);
+        Class eventInterface = rootStateAnnotation.value();
+        PlainStateMachineExecutor executor = new PlainStateMachineExecutor(instantiatedMetaState);
+        Object proxy =
+                Proxy.newProxyInstance(eventInterface.getClassLoader(),
+                new Class[]{eventInterface},
+                executor.getInvocationHandler());
+        return proxy;
+    }
+    InvocationHandler invocationHandler = null;
+    
+    public InvocationHandler getInvocationHandler() {
+        if (invocationHandler == null) {
+            invocationHandler = new InvocationHandler() {
+                @Override
+                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                    return runEvent(method, args);
+                }
+            };
+        }
+        return invocationHandler;
+    }
+    
+    PlainStateMachineExecutor(MetaState instantiatedMetaState) {
+        this.rootMetaState = instantiatedMetaState;
+        fillInControllerReferences();
+        fillInRootStateReferences();
+    }
+    MetaState rootMetaState = null;
+    
+    private synchronized Object runEvent(Method eventMethod, Object[] args)
+            throws Throwable {
+        clearOutput();
+        clearExceptions();
+        clearTransitions();
+        List<MetaState> initialStates = buildActiveStates();
+        runEventActions(initialStates, eventMethod, args);
+        runGuardMethods(initialStates);
+        applyTransitions(initialStates);
+        List<MetaState> finalStates = buildActiveStates();
+        List<MetaState> exiting = new ArrayList<MetaState>();
+        List<MetaState> entering = new ArrayList<MetaState>();
+        calculateStateDelta(initialStates, finalStates, exiting, entering);
+        runExitMethods(exiting);
+        runEntryMethods(entering);
+        if (hasExceptions()) {
+            throw buildOutputException();
+        }
+        if (eventMethod.getReturnType() != null) {
+            return buildOutputValue(eventMethod.getReturnType());
+        }
+        return null;
+    }
+    
+    private void fillInControllerReferences(MetaState metaState) {
+    }
+    Object output = null;
+    
+    private void clearOutput() {
+        output = null;
+    }
+    
+    private Object buildOutputValue(Class returnType) {
+        return output;
+    }
+    List<Throwable> exceptions = new ArrayList<Throwable>();
+    
+    private void clearExceptions() {
+        exceptions.clear();
+    }
+    
+    private boolean hasExceptions() {
+        return !exceptions.isEmpty();
+    }
+    
+    private Throwable buildOutputException() {
+        if (exceptions.size() == 1) {
+            return exceptions.get(0);
+        } else {
+            return new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.MULTIPLE_EXCEPTIONS_THROWN,
+                    new Object[0]);
+        }
+    }
+    List<TransitionOnSubstate> queuedTransitions = new ArrayList<TransitionOnSubstate>();
+    
+    private void clearTransitions() {
+        queuedTransitions.clear();
+    }
+    
+    @Override
+    public void queueTransition(TransitionOnSubstate t) {
+        queuedTransitions.add(t);
+    }
+    
+    @Override
+    public void output(Object outputObject) {
+        output = outputObject;
+    }
+    
+    @Override
+    public void exception(Throwable cause) {
+        exceptions.add(cause);
+    }
+    
+    private List<MetaState> buildActiveStates() {
+        final List<MetaState> activeStates = new ArrayList<MetaState>();
+        rootMetaState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState metaState) {
+                activeStates.add(metaState);
+            }
+        });
+        return activeStates;
+    }
+    
+    private void runEventActions(List<MetaState> activeStates, Method eventInterfaceMethod, Object[] args) {
+        for (MetaState ms : activeStates) {
+            Operation op = ms.eventMethods.get(eventInterfaceMethod);
+            if (op != null) {
+                op.eval(this, args);
+            }
+        }
+    }
+    
+    private void runGuardMethods(List<MetaState> activeStates) {
+        for (MetaState ms : activeStates) {
+            for (Operation op : ms.guardMethods) {
+                op.eval(this, null);
+            }
+        }
+    }
+    
+    private void runExitMethods(List<MetaState> exitingStates) {
+        for (MetaState ms : exitingStates) {
+            for (Operation op : ms.exitMethods) {
+                op.eval(this, null);
+            }
+        }
+    }
+    
+    private void runEntryMethods(List<MetaState> enteringStates) {
+        for (MetaState ms : enteringStates) {
+            for (Operation op : ms.entryMethods) {
+                op.eval(this, null);
+            }
+        }
+    }
+    
+    private void applyTransitions(List<MetaState> activeStates) {
+        
+        while (!queuedTransitions.isEmpty()) {
+            /* Pull a transition. */
+            TransitionOnSubstate tos = queuedTransitions.remove(queuedTransitions.size() - 1);
+            /* Apply it. */
+            applyTransition(tos);
+            /* Add any implied transitions to the list of transitions, if the new state is not 
+             * currently active. */
+            if (!activeStates.contains(tos.targetMetaState)) {
+                queuedTransitions.addAll(tos.targetMetaState.entryTransitions);
+            }
+        }
+    }
+    
+    private void applyTransition(TransitionOnSubstate tos) {
+        try {
+            tos.substate.setActiveMetaState(tos.targetMetaState);
+            tos.substate.getField().set(tos.targetMetaState.parent.stateInstance, tos.targetMetaState.stateInstance);
+        } catch (Exception ex) {
+            throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.ERROR_APPLYING_TRANSITION,
+                    new Object[]{ex.getMessage()});
+        }
+    }
+    
+    private void calculateStateDelta(List<MetaState> initialStates, List<MetaState> finalStates, List<MetaState> exiting, List<MetaState> entering) {
+        for (MetaState initialState : initialStates) {
+            if (!finalStates.contains(initialState)) {
+                exiting.add(initialState);
+            }
+        }
+        for (MetaState finalState : finalStates) {
+            if (!initialStates.contains(finalState)) {
+                entering.add(finalState);
+            }
+        }
+    }
+    
+    private void fillInControllerReferences() {
+        rootMetaState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState metaState) {
+                fillInReferenceFields(metaState, Controller.class, PlainStateMachineExecutor.this);
+            }
+        });
+    }
+    
+    private void fillInRootStateReferences() {
+        rootMetaState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState metaState) {
+                System.out.println("Visiting " + metaState + " to fill in root state reference.");
+                fillInReferenceFields(metaState, RootState.class, rootMetaState.stateInstance);
+            }
+        });
+    }
+    
+    private void fillInReferenceFields(MetaState metaState, Class<? extends Annotation> aClass, Object value) {
+
+        for (Field f : metaState.stateClass.getFields()) {
+            
+            if (f.getAnnotation(aClass) != null) {
+                try {
+                    System.out.println("Setting field " + metaState.stateInstance + "." + f.getName()
+                            + " to " + value);
+                    boolean accessible = f.isAccessible();
+                    f.setAccessible(true);
+                    f.set(metaState.stateInstance, value);
+                    f.setAccessible(accessible);
+                } catch (Exception ex) {
+                    throw new StateMachineException(MessageNames.BUNDLE_NAME,
+                            MessageNames.ERROR_INSTANTIATING, new Object[]{ex.getMessage()});
+                    
+                }
+            }
+        }
+    }
+}

Modified: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/RootState.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/RootState.java?rev=1432308&r1=1432307&r2=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/RootState.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/RootState.java Fri Jan 11 22:06:32 2013
@@ -19,8 +19,10 @@
 package org.apache.river.container.hsm;
 
 import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * Annotation to indicate the root class of a hierarchical state machine.
@@ -28,6 +30,7 @@ import java.lang.annotation.RetentionPol
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
 public @interface RootState {
     /**
      The event interface that this machine is intended to implement.

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineCompiler.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineCompiler.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineCompiler.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineCompiler.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.river.container.Utils;
+
+/**
+ * Compiler for state machine instances. The "input" to the compiler is actually
+ * a class that is annotated with the
+ *
+ * @RootState annotation.
+ *
+ */
+public class StateMachineCompiler {
+
+    Class eventInterface = null;
+
+    /**
+     * Retrieve the event interface, as set by the
+     *
+     * @RootState annotation.
+     * @return The event interface.
+     */
+    public Class getEventInterface() {
+        return eventInterface;
+    }
+
+    public MetaState compile(Class rootStateClass) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        findEventInterface(rootStateClass);
+        // First pass: create all metastates
+        MetaState rootMetaState = createMetaState(rootStateClass);
+        // Second pass: Fill in event methods
+        rootMetaState.visitAll(new MetaStateVisitor() {
+            @Override
+            public void visit(MetaState ms) {
+                try {
+                    fillInEventMethods(ms);
+                    fillInGuardMethods(ms);
+                    fillInEntryMethods(ms);
+                    fillInExitMethods(ms);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        });
+        return rootMetaState;
+    }
+
+    private void findEventInterface(Class rootStateClass) {
+        RootState rootStateAnnotation = (RootState) rootStateClass.getAnnotation(RootState.class);
+        if (rootStateAnnotation == null || rootStateAnnotation.value() == null) {
+            throw new RuntimeException("Root state class must specify @RootState(interfaceClass).");
+        }
+        eventInterface = rootStateAnnotation.value();
+    }
+
+    MetaState createMetaState(Class stateClass) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        return createMetaState(null, stateClass);
+    }
+
+    MetaState createMetaState(MetaState parentMetaState, Class stateClass) throws IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+
+        MetaState metaState = new MetaState();
+        metaState.stateClass = stateClass;
+        metaState.parent = parentMetaState;
+
+        processSubstates(metaState);
+        return metaState;
+    }
+
+    /**
+     * Look for fields that are annotated with
+     *
+     * @State and fill in the required substate info.
+     * @param metaState
+     */
+    private void processSubstates(MetaState metaState) throws IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+        for (Field f : metaState.stateClass.getDeclaredFields()) {
+            // Look for fields annotated with @State.
+            State ann = (State) f.getAnnotation(State.class);
+            if (ann == null) {
+                continue;
+            }
+            SubstateInfo info = new SubstateInfo();
+            info.setField(f);
+            metaState.substates.add(info);
+            if (ann.value() == null) {
+                throw new RuntimeException("@State needs a list of possible states");
+            }
+            info.setPossibleMetaStates(createPossibleMetaStates(metaState, ann.value()).toArray(new MetaState[0]));
+            Initial initialAnn = f.getAnnotation(Initial.class);
+            if (initialAnn == null) {
+                throw new RuntimeException("Need initial state for "
+                        + f.getName());
+            }
+            MetaState initialMetaState = findMetaState(info.getPossibleMetaStates(), initialAnn.value());
+            if (initialMetaState == null) {
+                throw new RuntimeException("Couldn't find a metastate that corresponds to " + initialAnn.value() + " in " + Utils.format(info.getPossibleMetaStates()));
+            }
+            info.setInitialMetaState(initialMetaState);
+
+            /* While we're at it, set the active metastate. */
+            info.setActiveMetaState(info.getInitialMetaState());
+            Retained retainedAnn = f.getAnnotation(Retained.class);
+            info.setRetained(retainedAnn != null);
+            
+            /* Add the non-retained metastate to the list of transitions-on-entry of the
+             * parent metastat.
+             */
+            if (! info.isRetained()) {
+                metaState.entryTransitions.add(new TransitionOnSubstate(info, info.getInitialMetaState()));
+            }
+        }
+    }
+
+    private List<MetaState> createPossibleMetaStates(MetaState metaState, Class[] substateClasses) throws IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+        List<MetaState> metaStates = new ArrayList<MetaState>();
+        /* For each class, we'll need an instance. */
+        for (Class substateClass : substateClasses) {
+            metaStates.add(createMetaState(metaState, substateClass));
+        }
+        return metaStates;
+
+    }
+
+    /**
+     * Have a feeling this will end up in the StateMachineRunner
+     */
+    private Object findInstance(MetaState parentMetaState, Class enclosingClass) {
+        MetaState currentState = parentMetaState;
+        while (currentState != null) {
+            if (currentState.stateClass == enclosingClass) {
+                return currentState.stateInstance;
+            }
+            currentState = currentState.parent;
+        }
+        return null;
+    }
+
+    private MetaState findMetaState(MetaState[] possibleMetaStates, Class value) {
+        for (MetaState metaState : possibleMetaStates) {
+            if (metaState.stateClass == value) {
+                return metaState;
+            }
+        }
+        return null;
+    }
+
+    private void fillInEventMethods(MetaState metaState) throws NoSuchMethodException {
+        for (Method m : getEventInterface().getMethods()) {
+            /* See if the state class has a method with the same name and 
+             * parameters.
+             */
+            Method eventMethod = null;
+            try {
+                eventMethod = metaState.stateClass.getMethod(m.getName(), m.getParameterTypes());
+            } catch (NoSuchMethodException nsme) {
+                // Silent catch - lets the event method remain null, which we check for.
+            }
+            Operation operation = null;
+            if (eventMethod != null) {
+                if (eventMethod.getReturnType() != null && !m.getReturnType().isAssignableFrom(eventMethod.getReturnType())) {
+                    throw new RuntimeException("If the event method returns a value, its type must match the event interface's method.  Required"
+                            + m.getReturnType() + ", found " + eventMethod.getReturnType());
+                }
+                Transition transition = eventMethod.getAnnotation(Transition.class);
+                // Fill in from here down!
+                // Return value, no transition
+                if (eventMethod.getReturnType() != null && transition == null) {
+                    operation = new InvokeAndTransitionOperation(metaState, eventMethod);
+                } // Return value with transition
+                else if (eventMethod.getReturnType() != null && transition != null) {
+                    TransitionOnSubstate[] transitions = resolveTransitions(metaState, transition.value());
+                    operation = new InvokeAndTransitionOperation(metaState, eventMethod, transitions);
+                } // No return value, no transition
+                else if (eventMethod.getReturnType() == null && transition == null) {
+                    operation = new InvokeVoidAndTransitionOperation(metaState, eventMethod);
+                } // No return value, with transition
+                else if (eventMethod.getReturnType() == null && transition != null) {
+                    TransitionOnSubstate[] transitions = resolveTransitions(metaState, transition.value());
+                    operation = new InvokeVoidAndTransitionOperation(metaState, eventMethod, transitions);
+                }
+            }
+            metaState.eventMethods.put(m, operation);
+        }
+    }
+
+    private void fillInGuardMethods(MetaState metaState) {
+        Class stateClass = metaState.stateClass;
+        for (Method m : stateClass.getMethods()) {
+            Guard guard = m.getAnnotation(Guard.class);
+            if (guard != null) {
+                if (m.getReturnType() != boolean.class) {
+                    throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.GUARD_METHOD_DOESNT_RETURN_BOOLEAN,
+                            new Object[]{m.toGenericString(), m.getReturnType()});
+                }
+                TransitionOnSubstate[] transitions = resolveTransitions(metaState, guard.value());
+                Operation op = new InvokeGuardOperation(metaState, m, transitions);
+                metaState.guardMethods.add(op);
+            }
+
+        }
+    }
+
+    private void fillInEntryMethods(MetaState metaState) {
+        Class stateClass = metaState.stateClass;
+        for (Method m : stateClass.getMethods()) {
+            OnEntry onEntry = m.getAnnotation(OnEntry.class);
+            if (onEntry != null) {
+                if (m.getReturnType() != void.class) {
+                    throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.ENTRY_METHOD_ISNT_VOID,
+                            new Object[]{m.toGenericString(), m.getReturnType()});
+                }
+                Operation op = new InvokeVoidAndTransitionOperation(metaState, m, new TransitionOnSubstate[0]);
+                metaState.entryMethods.add(op);
+            }
+
+        }
+    }
+
+    private void fillInExitMethods(MetaState metaState) {
+        Class stateClass = metaState.stateClass;
+        for (Method m : stateClass.getMethods()) {
+            OnExit onEntry = m.getAnnotation(OnExit.class);
+            if (onEntry != null) {
+                if (m.getReturnType() != void.class) {
+                    throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.EXIT_METHOD_ISNT_VOID,
+                            new Object[]{m.toGenericString(), m.getReturnType()});
+                }
+                Operation op = new InvokeVoidAndTransitionOperation(metaState, m, new TransitionOnSubstate[0]);
+                metaState.exitMethods.add(op);
+            }
+
+        }
+    }
+
+    /**
+     * Convert an array of classes to a set of transitions with reference to a
+     * given metastate. Transitions are specified as an array of classes on an
+     * event method in a state class. In order to be used, they need to be
+     * resolved to a particular substate field related to that metastate. <br>
+     * The class referenced can be one of <ul> <li>a class that is a possible
+     * state of one of the substates defined on the state class that contains
+     * the annotated event method.</li> <li>A class that is a possible peer
+     * state to the state class that contains the annotated method. </li> <li>A
+     * class that is a possible peer state to an ancester of the state class
+     * that contains the annotated method. </li> </ul>
+     *
+     * @param metaState
+     * @param transitionClasses
+     * @return
+     */
+    private TransitionOnSubstate[] resolveTransitions(MetaState metaState, Class[] transitionClasses) {
+        List<TransitionOnSubstate> allTransitions = new ArrayList<TransitionOnSubstate>();
+        for (Class c : transitionClasses) {
+            List<TransitionOnSubstate> transitionsForClass = new ArrayList<TransitionOnSubstate>();
+            resolveSubstateTransitions(transitionsForClass, metaState, c);
+            resolvePeerAndParentTransitions(transitionsForClass, metaState, c);
+            if (transitionsForClass.isEmpty()) {
+                throw new StateMachineException(MessageNames.BUNDLE_NAME, MessageNames.CANT_RESOLVE_TRANSITIONS_FOR_CLASS,
+                        new Object[]{metaState.stateClass.getName(), c.getName()});
+            }
+            allTransitions.addAll(transitionsForClass);
+        }
+        return allTransitions.toArray(new TransitionOnSubstate[0]);
+    }
+
+    /**
+     * Go through each of the substates and find any metastates that correspond
+     * to the given state class. Create a transition for them and add it to the
+     * list of transitions.
+     *
+     * @param transitionsForClass
+     * @param metaState
+     * @param c
+     */
+    private void resolveSubstateTransitions(List<TransitionOnSubstate> transitionsForClass, MetaState metaState, Class c) {
+        for (SubstateInfo substateInfo : metaState.substates) {
+            for (MetaState m : substateInfo.getPossibleMetaStates()) {
+                if (m.stateClass == c) {
+                    transitionsForClass.add(new TransitionOnSubstate(substateInfo, m));
+                }
+            }
+        }
+    }
+
+    private void resolvePeerAndParentTransitions(List<TransitionOnSubstate> transitionsForClass, MetaState metaState, Class c) {
+        for (MetaState currentMetaState = metaState.parent; currentMetaState != null; currentMetaState = currentMetaState.parent) {
+            resolveSubstateTransitions(transitionsForClass, currentMetaState, c);
+        }
+    }
+}

Copied: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineException.java (from r1427243, river/jtsk/skunk/surrogate/src/org/apache/river/container/LocalizedRuntimeException.java)
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineException.java?p2=river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineException.java&p1=river/jtsk/skunk/surrogate/src/org/apache/river/container/LocalizedRuntimeException.java&r1=1427243&r2=1432308&rev=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/LocalizedRuntimeException.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineException.java Fri Jan 11 22:06:32 2013
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-package org.apache.river.container;
+package org.apache.river.container.hsm;
 
 import java.text.MessageFormat;
 import java.util.Locale;
@@ -24,9 +24,8 @@ import java.util.ResourceBundle;
 
 /**
  * This is a runtime exception with localized error messages.
- * @author trasukg
  */
-public class LocalizedRuntimeException extends RuntimeException {
+public class StateMachineException extends RuntimeException {
     private String messageBundleName;
     private String messageKey;
 
@@ -49,7 +48,7 @@ public class LocalizedRuntimeException e
      @param messageKey
      @param messageParameters
      */
-    public LocalizedRuntimeException(String messageBundleName,
+    public StateMachineException(String messageBundleName,
             String messageKey,
             Object[] messageParameters) {
         this.messageBundleName=messageBundleName;
@@ -63,7 +62,7 @@ public class LocalizedRuntimeException e
      @param messageKey
      @param messageParameters
      */
-    public LocalizedRuntimeException(Throwable rootCause,
+    public StateMachineException(Throwable rootCause,
             String messageBundleName,
             String messageKey,
             Object ... messageParameters) {

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineExecutor.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineExecutor.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineExecutor.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/StateMachineExecutor.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.river.container.hsm;
+
+/**
+ *
+ */
+public interface StateMachineExecutor {
+    
+    /**
+     * Queue a transition to a new state, which will be executed after the 
+     * current action.
+     * @param parentState
+     * @param newState 
+     */
+    public void queueTransition(TransitionOnSubstate t);
+
+    void output(Object outputObject);
+
+    void exception(Throwable cause);
+
+}

Modified: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/SubstateInfo.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/SubstateInfo.java?rev=1432308&r1=1432307&r2=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/SubstateInfo.java (original)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/SubstateInfo.java Fri Jan 11 22:06:32 2013
@@ -22,15 +22,49 @@ import java.lang.reflect.Field;
 
 /**
  *
- * @author trasukg
+ * Information on substates within a MetaState.
  */
 class SubstateInfo {
 
     private Field field;
+    
+    /** this field will disappear when the StateMachineCompiler is complete. */
     private Class[] possibleStates;
+    
+    private MetaState[] possibleMetaStates;
+
+    public MetaState[] getPossibleMetaStates() {
+        return possibleMetaStates;
+    }
+
+    public void setPossibleMetaStates(MetaState[] possibleMetaStates) {
+        this.possibleMetaStates = possibleMetaStates;
+    }
+
+    public MetaState getInitialMetaState() {
+        return initialMetaState;
+    }
+
+    public void setInitialMetaState(MetaState initialMetaState) {
+        this.initialMetaState = initialMetaState;
+    }
+    
+    /** this field will disappear when the StateMachineCompiler is complete. */
     private Class initialState;
+    
+    private MetaState initialMetaState;
+    
     private boolean retained=false;
+    private MetaState activeMetaState;
 
+    public MetaState getActiveMetaState() {
+        return activeMetaState;
+    }
+
+    public void setActiveMetaState(MetaState activeMetaState) {
+        this.activeMetaState = activeMetaState;
+    }
+    
     public Field getField() {
         return field;
     }

Added: river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/TransitionOnSubstate.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/TransitionOnSubstate.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/TransitionOnSubstate.java (added)
+++ river/jtsk/skunk/surrogate/src/org/apache/river/container/hsm/TransitionOnSubstate.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,40 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.apache.river.container.hsm;
+
+/**
+ * Holds a transition on a substate.
+ */
+public class TransitionOnSubstate {
+    
+    // package access, so the executor can use it directly.
+    SubstateInfo substate;
+
+    /**
+     * Create an instance specifying the target metastate for the given
+     * substate.
+     * 
+     * @param substate
+     * @param targetMetaState 
+     */
+    public TransitionOnSubstate(SubstateInfo substate, MetaState targetMetaState) {
+        this.substate = substate;
+        this.targetMetaState = targetMetaState;
+    }
+
+    public SubstateInfo getSubstate() {
+        return substate;
+    }
+
+
+    public MetaState getTargetMetaState() {
+        return targetMetaState;
+    }
+    
+    // package access, so the executor can use it directly.
+    MetaState targetMetaState;
+    
+    
+}

Added: river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/LoggingTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/LoggingTest.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/LoggingTest.java (added)
+++ river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/LoggingTest.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,58 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.apache.river.container.hsm;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author trasukg
+ */
+public class LoggingTest {
+
+    Logger log=Logger.getLogger(LoggingTest.class.getName());
+    
+    public LoggingTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        Logger.getLogger("org.apache.river.container.hsm").setLevel(Level.FINER);
+        System.setProperty("java.util.logging.ConsoleHandler.level", "FINER");
+    }
+   
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+    
+    @Test
+    public void testLogging() {
+        System.out.println("Should be seeing some logging...");
+        
+        
+        log.log(Level.FINEST, "Finest");
+        log.log(Level.FINER, "Finer");
+        log.log(Level.FINE, "Fine");
+        log.log(Level.INFO, "Info");
+        log.log(Level.WARNING, "Warning");
+        log.log(Level.SEVERE, "Severe");
+    }
+}

Copied: river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java (from r1427243, river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/SimpleMachineTest.java)
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java?p2=river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java&p1=river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/SimpleMachineTest.java&r1=1427243&r2=1432308&rev=1432308&view=diff
==============================================================================
--- river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/SimpleMachineTest.java (original)
+++ river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/PlainMachineExecutorTest.java Fri Jan 11 22:06:32 2013
@@ -36,15 +36,16 @@ import static org.junit.Assert.assertTru
  *
  * @author trasukg
  */
-public class SimpleMachineTest {
+public class PlainMachineExecutorTest {
 
-    public SimpleMachineTest() throws InstantiationException, IllegalAccessException,
+    public PlainMachineExecutorTest() throws InstantiationException, IllegalAccessException,
     NoSuchMethodException, InvocationTargetException {
     }
 
     @BeforeClass
     public static void setUpClass() throws Exception {
         Logger.getLogger("org.apache.river.container.hsm").setLevel(Level.FINER);
+        System.setProperty("java.util.logging.ConsoleHandler.level", "FINER");
     }
 
     @AfterClass
@@ -60,31 +61,7 @@ public class SimpleMachineTest {
     }
 
     TestSMInterface UUT=(TestSMInterface)
-                StateMachineFactory.createStateMachine(TestSM.class);
-
-    @Test
-    /**
-     Confirm that we understand the use of getAnnotation.
-     Getting the HSMRoot annotation on TestSM.class should yield
-     EventInterface.class.
-     */
-    public void testRootStateAnnotation() {
-        RootState ann=(RootState) TestSM.class.getAnnotation(RootState.class);
-        Class eventInterface=ann.value();
-        assertEquals(TestSMInterface.class, eventInterface);
-    }
-
-    @Test
-    /**
-     Make sure we can read the annotations on the 'state' field.
-     */
-    public void testStateAnnotation() throws NoSuchFieldException {
-        Class testSMClass=TestSM.class;
-        Field stateField=testSMClass.getDeclaredField("state");
-        /* Check the State annotation. */
-        State stateAnn=stateField.getAnnotation(State.class);
-        assertTrue("No state annotation present.", stateAnn != null);
-    }
+                PlainStateMachineExecutor.createProxy(TestSM.class);
 
     @Test
     /** Verify that the list of active states is correct at beginning.

Added: river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/StateMachineCompilerTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/StateMachineCompilerTest.java?rev=1432308&view=auto
==============================================================================
--- river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/StateMachineCompilerTest.java (added)
+++ river/jtsk/skunk/surrogate/test/org/apache/river/container/hsm/StateMachineCompilerTest.java Fri Jan 11 22:06:32 2013
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.river.container.hsm;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Test the State Machine Compiler
+ * 
+ */
+public class StateMachineCompilerTest {
+    
+    public StateMachineCompilerTest() {
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+    
+    StateMachineCompiler compiler=new StateMachineCompiler();
+    
+    @Test
+    public void testCompileReturnsMetaState() throws Exception {
+        MetaState stateMachine=compiler.compile(TestSM.class);
+        assertTrue(stateMachine + " isn't a MetaState", stateMachine instanceof MetaState);
+    }
+    
+    @Test 
+    public void testSampleActiveStates() throws Exception {
+        MetaState stateMachine=compiler.compile(TestSM.class);
+        List<Class> activeStates=stateMachine.getActiveStates();
+        checkContains(activeStates, TestSM.class);
+        checkContains(activeStates, TestSM.A.class);
+        checkContains(activeStates, TestSM.A.A1.class);
+    }
+
+    /**
+     * MetaState for TestSM should have event methods for sayHello and nullTransition,
+     * but nothing else.
+     * @throws Exception 
+     */
+    @Test 
+    public void testEventMethods() throws Exception {
+        MetaState stateMachine=compiler.compile(TestSM.class);
+        Collection<Method> methods=stateMachine.eventMethods.keySet();
+        Method sayHello=TestSMInterface.class.getMethod("sayHello");
+        assertNotNull("Didn't find sayHello() method in interface", sayHello);
+        checkContains(methods, sayHello);
+    }
+
+    /**
+     * A method annotated with @Guard should be reflected by a guarded transition
+     * operation.
+     * @throws Exception 
+     */
+    @Test
+    public void testGuardMethod() throws Exception {
+        MetaState rootState=compiler.compile(TestSM.class);
+        assertEquals("Number of guard methods on root metastate", 1, rootState.guardMethods.size());
+        
+    }
+    
+    private void checkContains(Collection<?> collection, Object requiredObject) {
+        assertTrue(collection + " doesn't include " + requiredObject, collection.contains(requiredObject));
+    }
+    
+    /**
+     * A method annotated with @Entry should be reflected by an invoke operation in the 
+     * metastate.
+     * @throws Exception 
+     */
+    @Test
+    public void testEntryMethod() throws Exception {
+        MetaState rootState=compiler.compile(TestSM.class);
+        MetaState metaStateA=findMetaState(rootState, TestSM.A.class);
+        assertEquals("Count of onEntry methods for A", 1, metaStateA.entryMethods.size());
+    }
+    
+    /**
+     * A method annotated with @Entry should be reflected by an invoke operation in the 
+     * metastate.
+     * @throws Exception 
+     */
+    @Test
+    public void testExitMethod() throws Exception {
+        MetaState rootState=compiler.compile(TestSM.class);
+        MetaState metaStateA=findMetaState(rootState, TestSM.A.class);
+        assertEquals("Count of onExit methods for A", 1, metaStateA.exitMethods.size());
+    }
+    
+    MetaState findMetaState(MetaState metaState, Class stateClass) {
+        for (SubstateInfo ssi: metaState.substates) {
+            for (MetaState ms: ssi.getPossibleMetaStates()) {
+                if (ms.stateClass == stateClass) {
+                    return ms;
+                }
+            }
+        }
+        return null;
+    }
+}

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=1432308&r1=1432307&r2=1432308&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 Fri Jan 11 22:06:32 2013
@@ -31,7 +31,7 @@ public class TestSM {
     @State({A.class, B.class})
     @Initial(A.class)
     @Retained
-    Object state;
+    public Object state;
     @Controller
     StateMachine controller;
     int nullTransitionEntryCount = 0;
@@ -49,12 +49,14 @@ public class TestSM {
         }
     }
 
+    @Transition(A.class)
     public void gotoA() {
-        controller.transition(A.class);
+        //controller.transition(A.class);
     }
 
+    @Transition(B.class)
     public void gotoB() {
-        controller.transition(B.class);
+        //controller.transition(B.class);
     }
 
     public int getAEntryCount() {
@@ -73,15 +75,27 @@ public class TestSM {
         return "Hello";
     }
 
+    @Guard(A.class)
+    public boolean beFalse() {
+        return false;
+        
+    }
+    
     public class A {
 
+        @State({A1.class})
+        @Initial(A1.class)
+        public Object state;
+        
+        @Transition(B.class)
         public String sayHello() {
-            controller.transition(B.class);
+            //controller.transition(B.class);
             return "Hello";
         }
 
+        @Transition(A.class)
         public void nullTransition() {
-            controller.transition(A.class);
+            //controller.transition(A.class);
         }
 
         @OnEntry
@@ -94,6 +108,8 @@ public class TestSM {
         public void onExit() {
             aExitCount++;
         }
+        
+        public class A1 {}
     }
 
     public class B {