You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by at...@apache.org on 2014/04/03 09:13:44 UTC
svn commit: r1584272 [1/6] - in /commons/proper/scxml/trunk/src:
main/java/org/apache/commons/scxml2/ main/java/org/apache/commons/scxml2/env/
main/java/org/apache/commons/scxml2/env/groovy/
main/java/org/apache/commons/scxml2/env/javascript/ main/java...
Author: ate
Date: Thu Apr 3 07:13:42 2014
New Revision: 1584272
URL: http://svn.apache.org/r1584272
Log:
SCXML-196: Redefine SCXMLSemantics to align with the Algorithm for SCXML Interpretation as described in the SCXML specification
SCXML-197: Better separation of concern between SCXMLExecutor and SCInstance and introducing a new SCXMLExecutionContext
See: http://issues.apache.org/jira/browse/SCXML-196 and http://issues.apache.org/jira/browse/SCXML-197
This replaces the existing SCXMLSemantics completely with a new definition and implementation conforming to the current SCXML specification: http://www.w3.org/TR/2014/CR-scxml-20140313/#AlgorithmforSCXMLInterpretation
As this also required major structural changes, which actually 'fitted well' with SCXML-197, the result is also a far better, almost complete, separation of concerns between SCXMLExecutor, SCInstance and SCXMLExecutionContext.
For more details please check the above issue links
Added:
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java (with props)
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/Step.java
- copied, changed from r1583086, commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Step.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/TransitionTargetTest.java (with props)
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/semantics/
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImplTest.java (with props)
Removed:
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLHelper.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Step.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Path.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/SCXMLHelperTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/PathTest.java
Modified:
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Builtin.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Context.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCInstance.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLListener.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLSemantics.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLSystemContext.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Status.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/TriggerEvent.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/AbstractSCXMLListener.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/AbstractStateMachine.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/LogUtils.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/SimpleContext.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/SimpleErrorReporter.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/SimpleSCXMLListener.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/SimpleScheduler.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/Tracer.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/groovy/GroovySCXMLScript.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlBuiltin.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/FunctionResolver.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathEvaluator.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/AsyncTrigger.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Action.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Assign.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/CustomAction.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/EnterableState.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Final.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Invoke.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/OnEntry.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/OnExit.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Parallel.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SCXML.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Send.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SimpleTransition.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/State.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Transition.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/TransitionTarget.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/test/StandaloneUtils.java
commons/proper/scxml/trunk/src/site/xdoc/guide/scxml-documents.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/NamespacePrefixedXPathsTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/SCInstanceTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/SCXMLExecutorTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/SCXMLTestHelper.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/StatusTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/TieBreakerTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/AbstractSCXMLListenerTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/AbstractStateMachineTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/LogUtilsTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/StopWatch.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/StopWatchDisplay.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/StopWatchTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluatorTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/javascript/JSEvaluatorTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/javascript/example-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/datamodel-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/datamodel-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/datamodel-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/datamodel-04.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/stateless-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/history-deep-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/history-shallow-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/invoke/InvokeParamNameTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/invoke/invoker-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/invoke/invoker-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/invoke/invoker-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLReaderTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/src-test-1.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/src-test-2.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/src-test-4.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/src-test-5.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/issues/issue62-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/issues/issue62-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/DatamodelTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/HistoryTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/ScxmlInitialAttributeTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/StatelessModelTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/TransitionTest.java
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/parallel-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/model/stateless-parallel-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/prefix-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/send-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/send-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/tie-breaker-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/tie-breaker-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/tie-breaker-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/transitions-01.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/transitions-02.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/transitions-03.xml
commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/transitions-04.xml
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java Thu Apr 3 07:13:42 2014
@@ -42,8 +42,8 @@ public class ActionExecutionContext {
/**
* @return Returns the state machine
*/
- public SCXML getStatemachine() {
- return exctx.getStatemachine();
+ public SCXML getStateMachine() {
+ return exctx.getStateMachine();
}
/**
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Builtin.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Builtin.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Builtin.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Builtin.java Thu Apr 3 07:13:42 2014
@@ -32,8 +32,7 @@ import javax.xml.xpath.XPathFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.scxml2.model.TransitionTarget;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
+import org.w3c.dom.*;
/**
* Implementations of builtin functions defined by the SCXML
@@ -144,11 +143,9 @@ public class Builtin implements Serializ
* @return The first node matching the path, coerced to a String, or null
* if no nodes match.
*/
- public static Object data(final Map<String, String> namespaces, final Object data,
- final String path) {
+ public static Object data(final Map<String, String> namespaces, final Object data, final String path) {
Object retVal = null;
- String strVal = SCXMLHelper.getNodeValue(dataNode(namespaces,
- data, path));
+ String strVal = getNodeValue(dataNode(namespaces, data, path));
// try as a double
try {
double d = Double.parseDouble(strVal);
@@ -167,6 +164,45 @@ public class Builtin implements Serializ
}
/**
+ * Retrieve a DOM node value as a string depending on its type.
+ *
+ * @param node A node to be retreived
+ * @return The value as a string
+ */
+ private static String getNodeValue(final Node node) {
+ String result = "";
+ if (node == null) {
+ return result;
+ }
+ switch(node.getNodeType()) {
+ case Node.ATTRIBUTE_NODE:
+ result = node.getNodeValue();
+ break;
+ case Node.ELEMENT_NODE:
+ if (node.hasChildNodes()) {
+ Node child = node.getFirstChild();
+ StringBuilder buf = new StringBuilder();
+ while (child != null) {
+ if (child.getNodeType() == Node.TEXT_NODE) {
+ buf.append(((CharacterData) child).getData());
+ }
+ child = child.getNextSibling();
+ }
+ result = buf.toString();
+ }
+ break;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ result = ((CharacterData) node).getData();
+ break;
+ default:
+ String err = "Trying to get value of a strange Node type: " + node.getNodeType();
+ throw new IllegalArgumentException(err);
+ }
+ return result.trim();
+ }
+
+ /**
* XPath {@link NamespaceContext} for Commons SCXML expressions.
*
* <b>Code duplication:</b> Also in XPathEvaluator.java. Class is not
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Context.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Context.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Context.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/Context.java Thu Apr 3 07:13:42 2014
@@ -23,6 +23,12 @@ import java.util.Map;
* a SCXML root or State object.
*/
public interface Context {
+
+ /**
+ * Current namespaces are saved under this key in the context.
+ */
+ String NAMESPACES_KEY = "_ALL_NAMESPACES";
+
/**
* Assigns a new value to an existing variable or creates a new one.
* The method searches the chain of parent Contexts for variable
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java Thu Apr 3 07:13:42 2014
@@ -129,15 +129,16 @@ public final class NotificationRegistry
* @param from The source EnterableState
* @param to The destination EnterableState
* @param transition The Transition that was taken
+ * @param event The event name triggering the transition
*/
public synchronized void fireOnTransition(final Observable source,
final TransitionTarget from, final TransitionTarget to,
- final Transition transition) {
+ final Transition transition, final String event) {
if (source != null && source.getObservableId() != null) {
Set<SCXMLListener> entries = regs.get(source.getObservableId());
if (entries != null) {
for (SCXMLListener lst : entries) {
- lst.onTransition(from, to, transition);
+ lst.onTransition(from, to, transition, event);
}
}
}
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCInstance.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCInstance.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCInstance.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCInstance.java Thu Apr 3 07:13:42 2014
@@ -19,15 +19,20 @@ package org.apache.commons.scxml2;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import org.apache.commons.scxml2.model.Data;
import org.apache.commons.scxml2.model.Datamodel;
import org.apache.commons.scxml2.model.EnterableState;
import org.apache.commons.scxml2.model.History;
+import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;
import org.apache.commons.scxml2.model.TransitionalState;
+import org.apache.commons.scxml2.semantics.ErrorConstants;
+import org.w3c.dom.Node;
/**
* The <code>SCInstance</code> performs book-keeping functions for
@@ -42,6 +47,26 @@ public class SCInstance implements Seria
private static final long serialVersionUID = 2L;
/**
+ * SCInstance cannot be initialized without setting a state machine.
+ */
+ private static final String ERR_NO_STATE_MACHINE = "SCInstance: State machine not set";
+
+ /**
+ * SCInstance cannot be initialized without setting an evaluator.
+ */
+ private static final String ERR_NO_EVALUATOR = "SCInstance: Evaluator not set";
+
+ /**
+ * SCInstance cannot be initialized without setting an error reporter.
+ */
+ private static final String ERR_NO_ERROR_REPORTER = "SCInstance: ErrorReporter not set";
+
+ /**
+ * Flag indicating the state machine instance has been initialized (before).
+ */
+ private boolean initialized;
+
+ /**
* The stateMachine being executed.
*/
private SCXML stateMachine;
@@ -52,27 +77,24 @@ public class SCInstance implements Seria
private final Status currentStatus;
/**
- * The owning state machine executor.
+ * The Evaluator used for this state machine instance.
*/
- private transient SCXMLExecutor executor;
+ private transient Evaluator evaluator;
/**
- * The <code>Map</code> of <code>Context</code>s per
- * <code>EnterableState</code>.
+ * The error reporter.
*/
- private final Map<EnterableState, Context> contexts;
+ private transient ErrorReporter errorReporter = null;
/**
- * The <code>Map</code> of last known configurations per
- * <code>History</code>.
+ * The map of contexts per EnterableState.
*/
- private final Map<History, Set<EnterableState>> histories;
+ private final Map<EnterableState, Context> contexts = new HashMap<EnterableState, Context>();
/**
- * <code>Map</code> for recording the run to completion status of
- * composite states.
+ * The map of last known configurations per History.
*/
- private final Map<EnterableState, Boolean> completions;
+ private final Map<History, Set<EnterableState>> histories = new HashMap<History, Set<EnterableState>>();
/**
* The root context.
@@ -92,14 +114,86 @@ public class SCInstance implements Seria
/**
* Constructor
*
- * @param executor The executor that this instance is attached to.
+ * @param evaluator The evaluator
+ * @param errorReporter The error reporter
*/
- SCInstance(final SCXMLExecutor executor) {
+ protected SCInstance(final Evaluator evaluator, final ErrorReporter errorReporter) {
+ this.evaluator = evaluator;
+ this.errorReporter = errorReporter;
this.currentStatus = new Status();
- this.executor = executor;
- this.contexts = new HashMap<EnterableState, Context>();
- this.histories = new HashMap<History, Set<EnterableState>>();
- this.completions = new HashMap<EnterableState, Boolean>();
+ }
+
+ /**
+ * (re)Initializes the state machine instance, clearing all variable contexts, histories and current status,
+ * and clones the SCXML root datamodel into the root context.
+ * @throws ModelException if the state machine hasn't been setup for this instance
+ */
+ protected void initialize() throws ModelException {
+ if (stateMachine == null) {
+ throw new ModelException(ERR_NO_STATE_MACHINE);
+ }
+ if (evaluator == null) {
+ throw new ModelException(ERR_NO_EVALUATOR);
+ }
+ if (errorReporter == null) {
+ throw new ModelException(ERR_NO_ERROR_REPORTER);
+ }
+ systemContext = null;
+ globalContext = null;
+ contexts.clear();
+ histories.clear();
+ currentStatus.clear();
+
+ // Clone root datamodel
+ Datamodel rootdm = stateMachine.getDatamodel();
+ cloneDatamodel(rootdm, getRootContext(), evaluator, errorReporter);
+ initialized = true;
+ }
+
+ /**
+ * Detach this state machine instance to allow external serialization.
+ * <p>
+ * This clears the evaluator and errorReporter members.
+ * </p>
+ */
+ protected void detach() {
+ this.evaluator = null;
+ this.errorReporter = null;
+ }
+
+ /**
+ * Set or re-attach the evaluator
+ * <p>
+ * If this state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
+ * @param evaluator The evaluator for this state machine instance.
+ * @throws ModelException if an attempt is made to set a null value for the evaluator
+ */
+ protected void setEvaluator(Evaluator evaluator) throws ModelException {
+ if (evaluator == null) {
+ throw new ModelException(ERR_NO_EVALUATOR);
+ }
+ if (this.evaluator != null && initialized) {
+ this.evaluator = evaluator;
+ // change of evaluator after initialization: re-initialize
+ initialize();
+ }
+ else {
+ this.evaluator = evaluator;
+ }
+ }
+
+ /**
+ * Set or re-attach the error reporter
+ * @param errorReporter The error reporter for this state machine instance.
+ * @throws ModelException if an attempt is made to set a null value for the error reporter
+ */
+ protected void setErrorReporter(ErrorReporter errorReporter) throws ModelException {
+ if (errorReporter == null) {
+ throw new ModelException(ERR_NO_ERROR_REPORTER);
+ }
+ this.errorReporter = errorReporter;
}
/**
@@ -110,35 +204,75 @@ public class SCInstance implements Seria
}
/**
- * Sets the state machine for this instance
+ * Sets the state machine for this instance.
+ * <p>
+ * If this state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
* @param stateMachine The state machine for this instance
+ * @throws ModelException if an attempt is made to set a null value for the state machine
*/
- void setStateMachine(SCXML stateMachine) {
- this.stateMachine = stateMachine;
- currentStatus.getStates().clear();
- contexts.clear();
- histories.clear();
- completions.clear();
- if (systemContext != null) {
- // reset _name system variable
- String scxmlName = stateMachine.getName() != null ? stateMachine.getName() : "";
- systemContext.getContext().set(SCXMLSystemContext.VARIABLE_NAME, scxmlName);
+ protected void setStateMachine(SCXML stateMachine) throws ModelException {
+ if (stateMachine == null) {
+ throw new ModelException(ERR_NO_STATE_MACHINE);
+ }
+ if (this.stateMachine != null && initialized) {
+ this.stateMachine = stateMachine;
+ // change of state machine after initialization: re-initialize
+ initialize();
+ }
+ else {
+ this.stateMachine = stateMachine;
}
}
/**
- * @return Returns the current status (active atomic states) for this instance
- */
- public Status getCurrentStatus() {
- return currentStatus;
+ * Clone data model.
+ *
+ * @param ctx The context to clone to.
+ * @param datamodel The datamodel to clone.
+ * @param evaluator The expression evaluator.
+ * @param errorReporter The error reporter
+ */
+ protected void cloneDatamodel(final Datamodel datamodel, final Context ctx, final Evaluator evaluator,
+ final ErrorReporter errorReporter) {
+ if (datamodel == null) {
+ return;
+ }
+ List<Data> data = datamodel.getData();
+ if (data == null) {
+ return;
+ }
+ for (Data datum : data) {
+ Node datumNode = datum.getNode();
+ Node valueNode = null;
+ if (datumNode != null) {
+ valueNode = datumNode.cloneNode(true);
+ }
+ // prefer "src" over "expr" over "inline"
+ if (datum.getSrc() != null) {
+ ctx.setLocal(datum.getId(), valueNode);
+ } else if (datum.getExpr() != null) {
+ Object value = null;
+ try {
+ ctx.setLocal(Context.NAMESPACES_KEY, datum.getNamespaces());
+ value = evaluator.eval(ctx, datum.getExpr());
+ ctx.setLocal(Context.NAMESPACES_KEY, null);
+ } catch (SCXMLExpressionException see) {
+ errorReporter.onError(ErrorConstants.EXPRESSION_ERROR, see.getMessage(), datum);
+ }
+ ctx.setLocal(datum.getId(), value);
+ } else {
+ ctx.setLocal(datum.getId(), valueNode);
+ }
+ }
}
/**
- * Re-attach the executor
- * @param executor The executor that this instance will be re-attached to.
+ * @return Returns the current status (active atomic states) for this instance
*/
- void setExecutor(SCXMLExecutor executor) {
- this.executor = executor;
+ public Status getCurrentStatus() {
+ return currentStatus;
}
/**
@@ -147,24 +281,24 @@ public class SCInstance implements Seria
* @return The root context.
*/
public Context getRootContext() {
- if (rootContext == null && executor.getEvaluator() != null) {
- rootContext = executor.getEvaluator().newContext(null);
+ if (rootContext == null && evaluator != null) {
+ rootContext = evaluator.newContext(null);
}
return rootContext;
}
/**
- * Set the root context.
- * <p>
- * Note: clears all other contexts!
- * </p>
- *
+ * Set or replace the root context.
* @param context The new root context.
*/
- void setRootContext(final Context context) {
+ protected void setRootContext(final Context context) {
this.rootContext = context;
- globalContext = null;
- contexts.clear();
+ // force initialization of rootContext
+ getRootContext();
+ if (systemContext != null) {
+ // re-parent the system context
+ systemContext.setSystemContext(evaluator.newContext(rootContext));
+ }
}
/**
@@ -172,37 +306,39 @@ public class SCInstance implements Seria
*
* @return The unwrapped system context.
*/
- Context getSystemContext() {
+ public Context getSystemContext() {
if (systemContext == null) {
// force initialization of rootContext
getRootContext();
if (rootContext != null) {
- systemContext = new SCXMLSystemContext(executor.getEvaluator().newContext(rootContext));
- systemContext.getContext().set(SCXMLSystemContext.VARIABLE_SESSIONID, UUID.randomUUID().toString());
+ systemContext = new SCXMLSystemContext(evaluator.newContext(rootContext));
+ systemContext.getContext().set(SCXMLSystemContext.SESSIONID_KEY, UUID.randomUUID().toString());
String _name = stateMachine != null && stateMachine.getName() != null ? stateMachine.getName() : "";
- systemContext.getContext().set(SCXMLSystemContext.VARIABLE_NAME, _name);
+ systemContext.getContext().set(SCXMLSystemContext.SCXML_NAME_KEY, _name);
}
}
return systemContext != null ? systemContext.getContext() : null;
}
+ /**
+ * @return Returns the global context, which is the top context <em>within</em> the state machine.
+ */
public Context getGlobalContext() {
if (globalContext == null) {
// force initialization of systemContext
getSystemContext();
if (systemContext != null) {
- globalContext = executor.getEvaluator().newContext(systemContext);
+ globalContext = evaluator.newContext(systemContext);
}
}
return globalContext;
}
/**
- * Get the <code>Context</code> for this <code>EnterableState</code>.
- * If one is not available it is created.
+ * Get the context for an EnterableState or create one if not created before.
*
* @param state The EnterableState.
- * @return The Context.
+ * @return The context.
*/
public Context getContext(final EnterableState state) {
Context context = contexts.get(state);
@@ -210,13 +346,13 @@ public class SCInstance implements Seria
EnterableState parent = state.getParent();
if (parent == null) {
// docroot
- context = executor.getEvaluator().newContext(getGlobalContext());
+ context = evaluator.newContext(getGlobalContext());
} else {
- context = executor.getEvaluator().newContext(getContext(parent));
+ context = evaluator.newContext(getContext(parent));
}
if (state instanceof TransitionalState) {
Datamodel datamodel = ((TransitionalState)state).getDatamodel();
- SCXMLHelper.cloneDatamodel(datamodel, context, executor.getEvaluator(), null);
+ cloneDatamodel(datamodel, context, evaluator, errorReporter);
}
contexts.put(state, context);
}
@@ -224,21 +360,24 @@ public class SCInstance implements Seria
}
/**
- * Get the <code>Context</code> for this <code>EnterableState</code>.
- * May return <code>null</code>.
+ * Get the context for an EnterableState if available.
+ *
+ * <p>Note: used for testing purposes only</p>
*
- * @param state The <code>EnterableState</code>.
- * @return The Context.
+ * @param state The EnterableState
+ * @return The context or null if not created yet.
*/
Context lookupContext(final EnterableState state) {
return contexts.get(state);
}
/**
- * Set the <code>Context</code> for this <code>EnterableState</code>.
+ * Set the context for an EnterableState
+ *
+ * <p>Note: used for testing purposes only</p>
*
* @param state The EnterableState.
- * @param context The Context.
+ * @param context The context.
*/
void setContext(final EnterableState state,
final Context context) {
@@ -287,8 +426,9 @@ public class SCInstance implements Seria
/**
* Resets the history state.
*
+ * <p>Note: used for testing purposes only</p>
+ *
* @param history The history.
- * @see org.apache.commons.scxml2.SCXMLExecutor#reset()
*/
public void reset(final History history) {
Set<EnterableState> lastConfiguration = histories.get(history);
@@ -296,37 +436,5 @@ public class SCInstance implements Seria
lastConfiguration.clear();
}
}
-
- /**
- * Get the completion status for this composite
- * {@link EnterableState}.
- *
- * @param state The <code>EnterableState</code>.
- * @return The completion status.
- *
- * @since 0.7
- */
- @SuppressWarnings("boxing")
- public boolean isDone(final EnterableState state) {
- if (completions.containsKey(state)) {
- return completions.get(state);
- }
- return false;
- }
-
- /**
- * Set the completion status for this composite
- * {@link EnterableState}.
- *
- * @param state The EnterableState.
- * @param done The completion status.
- *
- * @since 0.7
- */
- @SuppressWarnings("boxing")
- public void setDone(final EnterableState state,
- final boolean done) {
- completions.put(state, done);
- }
}
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java Thu Apr 3 07:13:42 2014
@@ -25,9 +25,12 @@ import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.commons.scxml2.env.SimpleDispatcher;
+import org.apache.commons.scxml2.env.SimpleErrorReporter;
import org.apache.commons.scxml2.invoke.Invoker;
import org.apache.commons.scxml2.invoke.InvokerException;
import org.apache.commons.scxml2.model.Invoke;
+import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;
/**
@@ -42,14 +45,39 @@ public class SCXMLExecutionContext {
private Log appLog = LogFactory.getLog(SCXMLExecutionContext.class);
/**
- * The executor this execution context belongs to.
+ * The action execution context instance, providing restricted access to this execution context
*/
- private final SCXMLExecutor executor;
+ private final ActionExecutionContext actionExecutionContext;
/**
- * The action execution context instance, providing restricted access to this execution context
+ * The SCInstance.
*/
- private final ActionExecutionContext actionExecutionContext;
+ private SCInstance scInstance;
+
+ /**
+ * The evaluator for expressions.
+ */
+ private Evaluator evaluator;
+
+ /**
+ * The external IOProcessor for Invokers to communicate back on
+ */
+ private SCXMLIOProcessor externalIOProcessor;
+
+ /**
+ * The event dispatcher to interface with external documents etc.
+ */
+ private EventDispatcher eventdispatcher;
+
+ /**
+ * The environment specific error reporter.
+ */
+ private ErrorReporter errorReporter = null;
+
+ /**
+ * The notification registry.
+ */
+ private NotificationRegistry notificationRegistry;
/**
* The internal event queue
@@ -57,6 +85,11 @@ public class SCXMLExecutionContext {
private final Queue<TriggerEvent> internalEventQueue = new LinkedList<TriggerEvent>();
/**
+ * The Invoker classes map, keyed by invoke target types (specified using "type" attribute).
+ */
+ private final Map<String, Class<? extends Invoker>> invokerClasses = new HashMap<String, Class<? extends Invoker>>();
+
+ /**
* The map storing the unique invokeId for an Invoke with an active Invoker
*/
private final Map<Invoke, String> invokeIds = new HashMap<Invoke, String>();
@@ -73,12 +106,26 @@ public class SCXMLExecutionContext {
/**
* Constructor
- * @param executor the executor this execution context belongs to
- */
- SCXMLExecutionContext(SCXMLExecutor executor) {
- this.executor = executor;
+ *
+ * @param externalIOProcessor The external IO Processor
+ * @param evaluator The evaluator
+ * @param eventDispatcher The event dispatcher, if null a SimpleDispatcher instance will be used
+ * @param errorReporter The error reporter, if null a SimpleErrorReporter instance will be used
+ */
+ protected SCXMLExecutionContext(SCXMLIOProcessor externalIOProcessor, Evaluator evaluator,
+ EventDispatcher eventDispatcher, ErrorReporter errorReporter) {
+ this.externalIOProcessor = externalIOProcessor;
+ this.evaluator = evaluator;
+ this.eventdispatcher = eventDispatcher != null ? eventDispatcher : new SimpleDispatcher();
+ this.errorReporter = errorReporter != null ? errorReporter : new SimpleErrorReporter();
+ this.notificationRegistry = new NotificationRegistry();
+
+ this.scInstance = new SCInstance(this.evaluator, this.errorReporter);
this.actionExecutionContext = new ActionExecutionContext(this);
- running = true;
+ }
+
+ public SCXMLIOProcessor getExternalIOProcessor() {
+ return externalIOProcessor;
}
/**
@@ -103,16 +150,20 @@ public class SCXMLExecutionContext {
}
/**
- * Internal reset which will cancel all current active Invokers, clear the internal event queue and mark the
+ * Initialize method which will cancel all current active Invokers, clear the internal event queue and mark the
* state machine process as running (again).
+ *
+ * @throws ModelException if the state machine instance failed to initialize.
*/
- void reset() {
+ public void initialize() throws ModelException {
+ running = false;
if (!invokeIds.isEmpty()) {
for (Invoke invoke : new ArrayList<Invoke>(invokeIds.keySet())) {
cancelInvoker(invoke);
}
}
internalEventQueue.clear();
+ scInstance.initialize();
running = true;
}
@@ -126,56 +177,153 @@ public class SCXMLExecutionContext {
/**
* @return Returns the state machine
*/
- public SCXML getStatemachine() {
- return executor.getStateMachine();
+ public SCXML getStateMachine() {
+ return scInstance.getStateMachine();
+ }
+
+ /**
+ * Set or replace the state machine to be executed
+ * <p>
+ * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
+ * @param stateMachine The state machine to set
+ * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+ */
+ protected void setStateMachine(SCXML stateMachine) throws ModelException {
+ scInstance.setStateMachine(stateMachine);
}
/**
* @return Returns the SCInstance
*/
public SCInstance getScInstance() {
- return executor.getSCInstance();
+ return scInstance;
}
/**
* @return Returns The evaluator.
*/
public Evaluator getEvaluator() {
- return executor.getEvaluator();
+ return evaluator;
+ }
+
+ /**
+ * Set or replace the evaluator
+ * <p>
+ * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
+ * @param evaluator The evaluator to set
+ * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+ */
+ protected void setEvaluator(Evaluator evaluator) throws ModelException {
+ scInstance.setEvaluator(evaluator);
+ this.evaluator = evaluator;
}
/**
* @return Returns the error reporter
*/
public ErrorReporter getErrorReporter() {
- return executor.getErrorReporter();
+ return errorReporter;
+ }
+
+ /**
+ * Set or replace the error reporter
+ *
+ * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
+ */
+ protected void setErrorReporter(ErrorReporter errorReporter) {
+ this.errorReporter = errorReporter != null ? errorReporter : new SimpleErrorReporter();
+ try {
+ scInstance.setErrorReporter(errorReporter);
+ }
+ catch (ModelException me) {
+ // won't happen
+ return;
+ }
}
/**
* @return Returns the event dispatcher
*/
public EventDispatcher getEventDispatcher() {
- return executor.getEventdispatcher();
+ return eventdispatcher;
+ }
+
+ /**
+ * Set or replace the event dispatch
+ *
+ * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
+ */
+ protected void setEventdispatcher(EventDispatcher eventdispatcher) {
+ this.eventdispatcher = eventdispatcher != null ? eventdispatcher : new SimpleDispatcher();
}
/**
* @return Returns the notification registry
*/
public NotificationRegistry getNotificationRegistry() {
- return executor.getNotificationRegistry();
+ return notificationRegistry;
}
/**
- * Get the {@link Invoker} for this {@link Invoke}.
- * May return <code>null</code>. A non-null {@link Invoker} will be
- * returned if and only if the {@link Invoke} parent TransitionalState is
- * currently active and contains the <invoke> child.
+ * Detach the current SCInstance to allow external serialization.
+ * <p>
+ * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
+ * </p>
+ * @return the detached instance
+ */
+ protected SCInstance detachInstance() {
+ SCInstance instance = scInstance;
+ scInstance.detach();
+ scInstance = null;
+ return instance;
+ }
+
+ /**
+ * Re-attach a previously detached SCInstance.
+ * <p>
+ * Note: an already attached instance will get overwritten (and thus lost).
+ * </p>
+ * @param instance An previously detached SCInstance
+ */
+ protected void attachInstance(SCInstance instance) {
+ if (scInstance != null ) {
+ scInstance.detach();
+ }
+ scInstance = instance;
+ if (scInstance != null) {
+ scInstance.detach();
+ try {
+ scInstance.setEvaluator(evaluator);
+ scInstance.setErrorReporter(errorReporter);
+ }
+ catch (ModelException me) {
+ // should not happen
+ return;
+ }
+ }
+ }
+
+ /**
+ * Register an Invoker for this target type.
*
- * @param invoke The <code>Invoke</code>.
- * @return The Invoker.
+ * @param type The target type (specified by "type" attribute of the invoke element).
+ * @param invokerClass The Invoker class.
*/
- public Invoker getInvoker(final Invoke invoke) {
- return invokers.get(invokeIds.get(invoke));
+ protected void registerInvokerClass(final String type, final Class<? extends Invoker> invokerClass) {
+ invokerClasses.put(type, invokerClass);
+ }
+
+ /**
+ * Remove the Invoker registered for this target type (if there is one registered).
+ *
+ * @param type The target type (specified by "type" attribute of the invoke element).
+ */
+ protected void unregisterInvokerClass(final String type) {
+ invokerClasses.remove(type);
}
/**
@@ -185,11 +333,33 @@ public class SCXMLExecutionContext {
* @return An {@link Invoker} for the specified type, if an
* invoker class is registered against that type,
* <code>null</code> otherwise.
- * @throws InvokerException When a suitable {@link Invoker} cannot
- * be instantiated.
+ * @throws InvokerException When a suitable {@link Invoker} cannot be instantiated.
*/
- public Invoker newInvoker(String type) throws InvokerException {
- return executor.newInvoker(type);
+ public Invoker newInvoker(final String type) throws InvokerException {
+ Class<? extends Invoker> invokerClass = invokerClasses.get(type);
+ if (invokerClass == null) {
+ throw new InvokerException("No Invoker registered for type \"" + type + "\"");
+ }
+ try {
+ return invokerClass.newInstance();
+ } catch (InstantiationException ie) {
+ throw new InvokerException(ie.getMessage(), ie.getCause());
+ } catch (IllegalAccessException iae) {
+ throw new InvokerException(iae.getMessage(), iae.getCause());
+ }
+ }
+
+ /**
+ * Get the {@link Invoker} for this {@link Invoke}.
+ * May return <code>null</code>. A non-null {@link Invoker} will be
+ * returned if and only if the {@link Invoke} parent TransitionalState is
+ * currently active and contains the <invoke> child.
+ *
+ * @param invoke The <code>Invoke</code>.
+ * @return The Invoker.
+ */
+ public Invoker getInvoker(final Invoke invoke) {
+ return invokers.get(invokeIds.get(invoke));
}
/**
@@ -201,7 +371,7 @@ public class SCXMLExecutionContext {
*/
public String setInvoker(final Invoke invoke, final Invoker invoker) {
String invokeId = invoke.getId();
- if (SCXMLHelper.isStringEmpty(invokeId)) {
+ if (invokeId == null) {
invokeId = UUID.randomUUID().toString();
}
invokeIds.put(invoke, invokeId);
@@ -236,8 +406,7 @@ public class SCXMLExecutionContext {
try {
invokers.get(invokeId).cancel();
} catch (InvokerException ie) {
- TriggerEvent te = new TriggerEvent(invokeId
- + ".invoke.cancel.failed", TriggerEvent.ERROR_EVENT);
+ TriggerEvent te = new TriggerEvent("failed.invoke.cancel."+invokeId, TriggerEvent.ERROR_EVENT);
addInternalEvent(te);
}
removeInvoker(invoke);
@@ -245,7 +414,7 @@ public class SCXMLExecutionContext {
}
/**
- * Add an event to the internal queue
+ * Add an event to the internal event queue
* @param event The event
*/
public void addInternalEvent(TriggerEvent event) {
@@ -253,9 +422,16 @@ public class SCXMLExecutionContext {
}
/**
- * @return Returns the next event from the internal queue, if available
+ * @return Returns the next event from the internal event queue, if available
*/
- TriggerEvent nextInternalEvent() {
+ public TriggerEvent nextInternalEvent() {
return internalEventQueue.poll();
}
+
+ /**
+ * @return Returns true if the internal event queue isn't empty
+ */
+ public boolean hasPendingInternalEvent() {
+ return !internalEventQueue.isEmpty();
+ }
}
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java Thu Apr 3 07:13:42 2014
@@ -16,25 +16,17 @@
*/
package org.apache.commons.scxml2;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.scxml2.invoke.Invoker;
-import org.apache.commons.scxml2.invoke.InvokerException;
-import org.apache.commons.scxml2.model.Datamodel;
import org.apache.commons.scxml2.model.EnterableState;
-import org.apache.commons.scxml2.model.History;
import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.Observable;
import org.apache.commons.scxml2.model.SCXML;
-import org.apache.commons.scxml2.model.TransitionTarget;
-import org.apache.commons.scxml2.model.TransitionalState;
import org.apache.commons.scxml2.semantics.SCXMLSemanticsImpl;
-import org.apache.commons.scxml2.system.EventVariable;
/**
* <p>The SCXML "engine" that executes SCXML documents. The
@@ -44,10 +36,12 @@ import org.apache.commons.scxml2.system.
* <p>The default implementation is
* <code>org.apache.commons.scxml2.semantics.SCXMLSemanticsImpl</code></p>
*
+ * <p>The executor uses SCXMLExecutionContext to manage the state and
+ * provide all the services to the SCXMLSemantics implementation.</p>
+ *
* @see SCXMLSemantics
*/
-@SuppressWarnings("unused") // TODO: remove again after refactoring is done
-public class SCXMLExecutor {
+public class SCXMLExecutor implements SCXMLIOProcessor {
/**
* SCXMLExecutor put into motion without setting a model (state machine).
@@ -60,144 +54,19 @@ public class SCXMLExecutor {
private Log log = LogFactory.getLog(SCXMLExecutor.class);
/**
- * The evaluator for expressions.
- */
- private Evaluator evaluator;
-
- /**
- * The event dispatcher to interface with external documents etc.
- */
- private EventDispatcher eventdispatcher;
-
- /**
- * The environment specific error reporter.
- */
- private ErrorReporter errorReporter = null;
-
- /**
- * The notification registry.
- */
- private NotificationRegistry notificationRegistry;
-
- /**
- * Run-to-completion.
- */
- private boolean superStep = true;
-
- /**
* Interpretation semantics.
*/
private SCXMLSemantics semantics;
/**
- * The SCInstance.
- */
- private SCInstance scInstance;
-
- /**
- * The external event queue
- */
- private final Queue<TriggerEvent> externalEventQueue = new ConcurrentLinkedQueue<TriggerEvent>();
-
- /**
- * The <code>Invoker</code> classes <code>Map</code>, keyed by
- * <invoke> target types (specified using "type" attribute).
- */
- private final Map<String, Class<? extends Invoker>> invokerClasses;
-
- /**
* The state machine execution context
*/
private SCXMLExecutionContext exctx;
/**
- * Get the state chart instance for this executor.
- *
- * @return The SCInstance for this executor.
- */
- SCInstance getSCInstance() {
- return scInstance;
- }
-
- /**
- * Detach the current SCInstance to allow external serialization.
- * <p>
- * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
- * </p>
- * <p>
- * Note: until an instance is re-attached, no operations are allowed (and probably throw exceptions) except
- * for {@link #addEvent(TriggerEvent)} which might still be used (concurrently) by running Invokers, or
- * {@link #hasPendingEvents()} to check for possible pending events.
- * </p>
- * @return the detached instance
- */
- public SCInstance detachInstance() {
- SCInstance instance = scInstance;
- scInstance.setExecutor(null);
- scInstance = null;
- return instance;
- }
-
- /**
- * Re-attach a previously detached SCInstance.
- * <p>
- * Note: an already attached instance will get overwritten (and thus lost).
- * </p>
- * @param instance An previously detached SCInstance
- */
- public void attachInstance(SCInstance instance) {
- if (scInstance != null ) {
- scInstance.setExecutor(null);
- }
- scInstance = instance;
- if (scInstance != null) {
- scInstance.setExecutor(this);
- }
- }
-
- /**
- * Log the current set of active states.
- */
- private void logState() {
- if (log.isDebugEnabled()) {
- StringBuilder sb = new StringBuilder("Current States: [ ");
- for (EnterableState es : getCurrentStatus().getStates()) {
- sb.append(es.getId()).append(", ");
- }
- int length = sb.length();
- sb.delete(length - 2, length).append(" ]");
- log.debug(sb.toString());
- }
- }
-
- /**
- * @param step The most recent Step
- */
- private void updateStatus(final Step step) {
- getCurrentStatus().getStates().clear();
- getCurrentStatus().getStates().addAll(step.getAfterStatus().getStates());
- scInstance.getSystemContext().setLocal(SCXMLSystemContext.VARIABLE_ALL_STATES, getCurrentStatus().getAllStates());
- }
-
- /**
- * @param evt The event being triggered.
+ * The external event queue
*/
- private void setSystemEventVariable(final TriggerEvent evt, boolean internalQueue) {
- Context systemContext = scInstance.getSystemContext();
- EventVariable eventVar = null;
- if (evt != null) {
- String eventType = internalQueue ? EventVariable.TYPE_INTERNAL : EventVariable.TYPE_EXTERNAL;
-
- final int triggerEventType = evt.getType();
- if (triggerEventType == TriggerEvent.ERROR_EVENT || triggerEventType == TriggerEvent.CHANGE_EVENT) {
- eventType = EventVariable.TYPE_PLATFORM;
- }
-
- // TODO: determine sendid, origin, originType and invokeid based on context later.
- eventVar = new EventVariable(evt.getName(), eventType, null, null, null, null, evt.getPayload());
- }
- systemContext.setLocal(SCXMLSystemContext.VARIABLE_EVENT, eventVar);
- }
+ private final Queue<TriggerEvent> externalEventQueue = new ConcurrentLinkedQueue<TriggerEvent>();
/**
* Convenience constructor.
@@ -229,38 +98,33 @@ public class SCXMLExecutor {
public SCXMLExecutor(final Evaluator expEvaluator,
final EventDispatcher evtDisp, final ErrorReporter errRep,
final SCXMLSemantics semantics) {
- this.evaluator = expEvaluator;
- this.eventdispatcher = evtDisp;
- this.errorReporter = errRep;
- if (semantics == null) {
- // Use default semantics, if none provided
- this.semantics = new SCXMLSemanticsImpl();
- } else {
- this.semantics = semantics;
- }
- this.scInstance = new SCInstance(this);
- this.notificationRegistry = new NotificationRegistry();
- this.invokerClasses = new HashMap<String, Class<? extends Invoker>>();
- this.exctx = new SCXMLExecutionContext(this);
+ this.semantics = semantics != null ? semantics : new SCXMLSemanticsImpl();
+ this.exctx = new SCXMLExecutionContext(this, expEvaluator, evtDisp, errRep);
}
/**
- * Get the current status.
+ * Get the current state machine instance status.
*
* @return The current Status
*/
public synchronized Status getCurrentStatus() {
- return scInstance.getCurrentStatus();
+ return exctx.getScInstance().getCurrentStatus();
}
/**
- * Set the expression evaluator.
- * <b>NOTE:</b> Should only be used before the executor is set in motion.
- *
- * @param evaluator The evaluator to set.
+ * Set or replace the expression evaluator
+ * <p>
+ * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
+ * <p>
+ * Also the external event queue will be cleared.
+ * </p>
+ * @param evaluator The evaluator to set
+ * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
*/
- public void setEvaluator(final Evaluator evaluator) {
- this.evaluator = evaluator;
+ public void setEvaluator(final Evaluator evaluator) throws ModelException {
+ exctx.setEvaluator(evaluator);
}
/**
@@ -269,53 +133,58 @@ public class SCXMLExecutor {
* @return Evaluator The evaluator in use.
*/
public Evaluator getEvaluator() {
- return evaluator;
+ return exctx.getEvaluator();
}
/**
- * Set the root context for this execution.
- * <b>NOTE:</b> Should only be used before the executor is set in motion.
+ * Get the root context for the state machine execution.
*
- * @param rootContext The Context that ties to the host environment.
+ * @return Context The root context.
*/
- public void setRootContext(final Context rootContext) {
- this.scInstance.setRootContext(rootContext);
+ public Context getRootContext() {
+ return exctx.getScInstance().getRootContext();
}
/**
- * Get the root context for this execution.
+ * Set the root context for the state machine execution.
+ * <b>NOTE:</b> Should only be used before the executor is set in motion.
*
- * @return Context The root context.
+ * @param rootContext The Context that ties to the host environment.
*/
- public Context getRootContext() {
- return scInstance.getRootContext();
+ public void setRootContext(final Context rootContext) {
+ exctx.getScInstance().setRootContext(rootContext);
}
/**
* Get the state machine that is being executed.
* <b>NOTE:</b> This is the state machine definition or model used by this
* executor instance. It may be shared across multiple executor instances
- * and as a best practice, should not be altered. Also note that
+ * and should not be altered once in use. Also note that
* manipulation of instance data for the executor should happen through
* its root context or state contexts only, never through the direct
- * manipulation of any {@link Datamodel}s associated with this state
+ * manipulation of any {@link org.apache.commons.scxml2.model.Datamodel}s associated with this state
* machine definition.
*
* @return Returns the stateMachine.
*/
public SCXML getStateMachine() {
- return scInstance.getStateMachine();
+ return exctx.getStateMachine();
}
/**
- * Set the state machine to be executed.
- * <b>NOTE:</b> Should only be used before the executor is set in motion.
- *
- * @param stateMachine The stateMachine to set.
+ * Set or replace the state machine to be executed
+ * <p>
+ * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+ * state!
+ * </p>
+ * <p>
+ * Also the external event queue will be cleared.
+ * </p>
+ * @param stateMachine The state machine to set
+ * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
*/
- public void setStateMachine(final SCXML stateMachine) {
- exctx.reset();
- scInstance.setStateMachine(semantics.normalizeStateMachine(stateMachine, errorReporter));
+ public void setStateMachine(final SCXML stateMachine) throws ModelException {
+ exctx.setStateMachine(semantics.normalizeStateMachine(stateMachine, exctx.getErrorReporter()));
externalEventQueue.clear();
}
@@ -325,16 +194,16 @@ public class SCXMLExecutor {
* @return Returns the errorReporter.
*/
public ErrorReporter getErrorReporter() {
- return errorReporter;
+ return exctx.getErrorReporter();
}
/**
- * Set the environment specific error reporter.
+ * Set or replace the error reporter
*
- * @param errorReporter The errorReporter to set.
+ * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
*/
public void setErrorReporter(final ErrorReporter errorReporter) {
- this.errorReporter = errorReporter;
+ exctx.setErrorReporter(errorReporter);
}
/**
@@ -343,16 +212,16 @@ public class SCXMLExecutor {
* @return Returns the eventdispatcher.
*/
public EventDispatcher getEventdispatcher() {
- return eventdispatcher;
+ return exctx.getEventDispatcher();
}
/**
- * Set the event dispatcher.
+ * Set or replace the event dispatch
*
- * @param eventdispatcher The eventdispatcher to set.
+ * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
*/
public void setEventdispatcher(final EventDispatcher eventdispatcher) {
- this.eventdispatcher = eventdispatcher;
+ exctx.setEventdispatcher(eventdispatcher);
}
/**
@@ -361,43 +230,81 @@ public class SCXMLExecutor {
* @return The notification registry.
*/
public NotificationRegistry getNotificationRegistry() {
- return notificationRegistry;
+ return exctx.getNotificationRegistry();
+ }
+
+ /**
+ * Add a listener to the {@link Observable}.
+ *
+ * @param observable The {@link Observable} to attach the listener to.
+ * @param listener The SCXMLListener.
+ */
+ public void addListener(final Observable observable, final SCXMLListener listener) {
+ exctx.getNotificationRegistry().addListener(observable, listener);
}
/**
- * Set the notification registry.
+ * Remove this listener from the {@link Observable}.
*
- * @param notifRegistry The notification registry.
+ * @param observable The {@link Observable}.
+ * @param listener The SCXMLListener to be removed.
*/
- @SuppressWarnings("unused")
- void setNotificationRegistry(final NotificationRegistry notifRegistry) {
- this.notificationRegistry = notifRegistry;
+ public void removeListener(final Observable observable,
+ final SCXMLListener listener) {
+ exctx.getNotificationRegistry().removeListener(observable, listener);
}
/**
- * Use "super-step", default is <code>true</code>
- * (that is, run-to-completion is default).
+ * Register an Invoker for this target type.
*
- * @return Returns the superStep property.
- * @see #setSuperStep(boolean)
+ * @param type The target type (specified by "type" attribute of the invoke element).
+ * @param invokerClass The Invoker class.
*/
- public boolean isSuperStep() {
- return superStep;
+ public void registerInvokerClass(final String type, final Class<? extends Invoker> invokerClass) {
+ exctx.registerInvokerClass(type, invokerClass);
}
/**
- * Set the super step.
+ * Remove the Invoker registered for this target type (if there is one registered).
*
- * @param superStep
- * if true, the internal derived events are also processed
- * (run-to-completion);
- * if false, the internal derived events are stored in the
- * CurrentStatus property and processed within the next
- * triggerEvents() invocation, also the immediate (empty event) transitions
- * are deferred until the next step
+ * @param type The target type (specified by "type" attribute of the invoke element).
*/
- public void setSuperStep(final boolean superStep) {
- this.superStep = superStep;
+ public void unregisterInvokerClass(final String type) {
+ exctx.unregisterInvokerClass(type);
+ }
+
+ /**
+ * Detach the current SCInstance to allow external serialization.
+ * <p>
+ * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
+ * </p>
+ * <p>
+ * Note: until an instance is re-attached, no operations are allowed (and probably throw exceptions) except
+ * for {@link #addEvent(TriggerEvent)} which might still be used (concurrently) by running Invokers, or
+ * {@link #hasPendingEvents()} to check for possible pending events.
+ * </p>
+ * @return the detached instance
+ */
+ public SCInstance detachInstance() {
+ return exctx.detachInstance();
+ }
+
+ /**
+ * Re-attach a previously detached SCInstance.
+ * <p>
+ * Note: an already attached instance will get overwritten (and thus lost).
+ * </p>
+ * @param instance An previously detached SCInstance
+ */
+ public void attachInstance(SCInstance instance) {
+ exctx.attachInstance(instance);
+ }
+
+ /**
+ * @return Returns true if the state machine is running
+ */
+ public boolean isRunning() {
+ return exctx.isRunning();
}
/**
@@ -412,67 +319,52 @@ public class SCXMLExecutor {
}
/**
- * Clear all state and begin from "initialstate" indicated
- * on root SCXML element.
+ * Clear all state and begin executing the state machine
*
- * @throws ModelException in case there is a fatal SCXML object
- * model problem.
+ * @throws ModelException if the state machine instance failed to initialize
*/
- public synchronized void reset() throws ModelException {
- if (getStateMachine() == null) {
- log.error(ERR_NO_STATE_MACHINE);
- throw new ModelException(ERR_NO_STATE_MACHINE);
- }
- // Reset all variable contexts
- exctx.reset();
- Context rootContext = scInstance.getRootContext();
- // Clone root datamodel
- SCXML stateMachine = getStateMachine();
- Datamodel rootdm = stateMachine.getDatamodel();
- SCXMLHelper.cloneDatamodel(rootdm, rootContext, getEvaluator(), log);
- if (scInstance.getGlobalContext() != null) {
- scInstance.getGlobalContext().reset();
- }
- // all states and parallels, only states have variable contexts
- for (TransitionTarget tt : stateMachine.getTargets().values()) {
- if (tt instanceof EnterableState) {
- Context context = scInstance.lookupContext((EnterableState)tt);
- if (context != null) {
- context.reset();
- if (tt instanceof TransitionalState) {
- Datamodel dm = ((TransitionalState)tt).getDatamodel();
- SCXMLHelper.cloneDatamodel(dm, context, getEvaluator(), log);
- }
- }
- } else if (tt instanceof History) {
- scInstance.reset((History) tt);
- }
+ public void reset() throws ModelException {
+ // clear any pending external events
+ externalEventQueue.clear();
+
+ // go
+ semantics.firstStep(exctx);
+
+ if (!exctx.isRunning()) {
+ semantics.finalStep(exctx);
}
- // Clear currentStatus
- getCurrentStatus().getStates().clear();
- Step step = new Step(null, getCurrentStatus());
- // execute global script if defined
- semantics.executeGlobalScript(exctx, step);
- // DetermineInitialStates
- semantics.determineInitialStates(exctx, step);
- // enter initial states
- semantics.enterStates(exctx, step);
- // AssignCurrentStatus
- updateStatus(step);
- // Execute Immediate Transitions
-
- TriggerEvent event = exctx.nextInternalEvent();
-
- if (event != null) {
- handleEvent(event);
- } else {
- // InitiateInvokes only after state machine has stabilized
- semantics.initiateInvokes(this, exctx, step);
- logState();
+
+ logState();
+ }
+
+ /**
+ * Add a new external event, which may be done concurrently, and even when the current SCInstance is detached.
+ * <p>
+ * No processing of the vent will be done, until the next triggerEvent methods is invoked.
+ * </p>
+ * @param evt an external event
+ */
+ public void addEvent(final TriggerEvent evt) {
+ if (evt != null) {
+ externalEventQueue.add(evt);
}
}
/**
+ * @return Returns true if there are pending external events to be processed.
+ */
+ public boolean hasPendingEvents() {
+ return !externalEventQueue.isEmpty();
+ }
+
+ /**
+ * @return Returns the current number of pending external events to be processed.
+ */
+ public int getPendingEvents() {
+ return externalEventQueue.size();
+ }
+
+ /**
* Convenience method when only one event needs to be triggered.
*
* @param evt
@@ -512,148 +404,47 @@ public class SCXMLExecutor {
}
/**
- * Add a new external event, which may be done concurrently, and even when the current SCInstance is detached.
- * <p>
- * No processing of the vent will be done, until the next triggerEvent methods is invoked.
- * </p>
- * @param evt an external event
- */
- public void addEvent(final TriggerEvent evt) {
- if (evt != null) {
- externalEventQueue.add(evt);
- }
- }
-
- /**
- * @return Returns true if there are pending external events to be processed.
- */
- public boolean hasPendingEvents() {
- return !externalEventQueue.isEmpty();
- }
-
- /**
* Trigger all pending and incoming events, until there are no more pending events
* @throws ModelException in case there is a fatal SCXML object model problem.
*/
public void triggerEvents() throws ModelException {
TriggerEvent evt;
- while ((evt = externalEventQueue.poll()) != null) {
- // Forward events (external only) to any existing invokes,
- // and finalize processing
- semantics.processInvokes(exctx, evt);
- handleEvent(evt);
+ while (exctx.isRunning() && (evt = externalEventQueue.poll()) != null) {
+ eventStep(evt);
}
}
- /**
- * The internal worker method for handling the next external event
- * @param evt an external event
- * @throws ModelException in case there is a fatal SCXML object model problem.
- */
- protected void handleEvent(final TriggerEvent evt)
- throws ModelException {
- TriggerEvent event = evt;
-
- Step step;
-
- boolean internalQueue = false;
-
- do {
- setSystemEventVariable(event, internalQueue);
-
- // CreateStep
- step = new Step(event, getCurrentStatus());
- // EnumerateReachableTransitions
- semantics.enumerateReachableTransitions(exctx, step);
- // FilterTransitionSet
- semantics.filterTransitionsSet(exctx, step);
- // FollowTransitions
- semantics.followTransitions(exctx, step);
- // UpdateHistoryStates
- semantics.updateHistoryStates(exctx, step);
- // ExecuteActions
- semantics.executeActions(exctx, step);
- // AssignCurrentStatus
- updateStatus(step);
-
- internalQueue = true;
- event = exctx.nextInternalEvent();
+ protected void eventStep(TriggerEvent event) throws ModelException {
+ semantics.nextStep(exctx, event);
- } while (event != null);
-
- // InitiateInvokes only after state machine has stabilized
- semantics.initiateInvokes(this, exctx, step);
+ if (!exctx.isRunning()) {
+ semantics.finalStep(exctx);
+ }
logState();
}
/**
- * Add a listener to the {@link Observable}.
- *
- * @param observable The {@link Observable} to attach the listener to.
- * @param listener The SCXMLListener.
- */
- public void addListener(final Observable observable, final SCXMLListener listener) {
- notificationRegistry.addListener(observable, listener);
- }
-
- /**
- * Remove this listener from the {@link Observable}.
- *
- * @param observable The {@link Observable}.
- * @param listener The SCXMLListener to be removed.
- */
- public void removeListener(final Observable observable,
- final SCXMLListener listener) {
- notificationRegistry.removeListener(observable, listener);
- }
-
- /**
- * Register an <code>Invoker</code> for this target type.
- *
- * @param type The target type (specified by "type"
- * attribute of <invoke> tag).
- * @param invokerClass The <code>Invoker</code> <code>Class</code>.
- */
- public void registerInvokerClass(final String type,
- final Class<? extends Invoker> invokerClass) {
- invokerClasses.put(type, invokerClass);
- }
-
- /**
- * Remove the <code>Invoker</code> registered for this target
- * type (if there is one registered).
+ * Get the state chart instance for this executor.
*
- * @param type The target type (specified by "type"
- * attribute of <invoke> tag).
+ * @return The SCInstance for this executor.
*/
- public void unregisterInvokerClass(final String type) {
- invokerClasses.remove(type);
+ protected SCInstance getSCInstance() {
+ return exctx.getScInstance();
}
/**
- * Create a new {@link Invoker}
- *
- * @param type The type of the target being invoked.
- * @return An {@link Invoker} for the specified type, if an
- * invoker class is registered against that type,
- * <code>null</code> otherwise.
- * @throws org.apache.commons.scxml2.invoke.InvokerException When a suitable {@link Invoker} cannot
- * be instantiated.
+ * Log the current set of active states.
*/
- Invoker newInvoker(final String type)
- throws InvokerException {
- Class<? extends Invoker> invokerClass = invokerClasses.get(type);
- if (invokerClass == null) {
- throw new InvokerException("No Invoker registered for type \""
- + type + "\"");
- }
- try {
- return invokerClass.newInstance();
- } catch (InstantiationException ie) {
- throw new InvokerException(ie.getMessage(), ie.getCause());
- } catch (IllegalAccessException iae) {
- throw new InvokerException(iae.getMessage(), iae.getCause());
+ protected void logState() {
+ if (log.isDebugEnabled()) {
+ StringBuilder sb = new StringBuilder("Current States: [ ");
+ for (EnterableState es : getCurrentStatus().getStates()) {
+ sb.append(es.getId()).append(", ");
+ }
+ int length = sb.length();
+ sb.delete(length - 2, length).append(" ]");
+ log.debug(sb.toString());
}
}
}
Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java?rev=1584272&view=auto
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java (added)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java Thu Apr 3 07:13:42 2014
@@ -0,0 +1,31 @@
+/*
+ * 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.commons.scxml2;
+
+/**
+ * The SCXML I/O Processor provides the interface for an external system or invoked child SCXML process
+ * ({@link org.apache.commons.scxml2.invoke.Invoker}) to asynchronously send events to the SCXMLExecutor.
+ */
+public interface SCXMLIOProcessor {
+
+ /**
+ * Send an asynchronous event to the SCXMLExecutor
+ * <p>
+ * @param event the event to send
+ */
+ void addEvent(TriggerEvent event);
+}
Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLIOProcessor.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLListener.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLListener.java?rev=1584272&r1=1584271&r2=1584272&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLListener.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/SCXMLListener.java Thu Apr 3 07:13:42 2014
@@ -52,9 +52,10 @@ public interface SCXMLListener {
* @param from The source TransitionTarget
* @param to The destination TransitionTarget
* @param transition The Transition taken
+ * @param event The event name triggering the transition
*/
void onTransition(TransitionTarget from, TransitionTarget to,
- Transition transition);
+ Transition transition, String event);
}