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 {