官网:http://commons.apache.org/proper/commons-scxml/index.html
SCXML W3C规范:http://www.w3.org/TR/scxml/
pom.xml
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0
com.ctrip.infosec.offline
fsm
0.0.1-SNAPSHOT
Java.Net
http://download.java.net/maven/2/
JBoss repository
http://repository.jboss.com/maven2/
maven
http://repo1.maven.org/maven2/
commons-scxml
commons-scxml
0.9
commons-jexl
commons-jexl
20040901.055348
commons-logging
commons-logging
1.1.3
org.codehaus.groovy
groovy-all
2.2.2
xalan
xalan
2.7.1
javax.servlet
servlet-api
2.5
jstl
jstl
1.2
src
src
**/*.java
maven-compiler-plugin
2.3.2
1.7
1.7
myfsm.xml
version="1.0" initial="reset">
SCXMLEngine.java(状态机引擎,参考AbstractStateMachine类)
package com.ctrip.infosec.offline.fsm;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import org.apache.commons.scxml.SCXMLExecutor;
import org.apache.commons.scxml.SCXMLListener;
import org.apache.commons.scxml.Status;
import org.apache.commons.scxml.TriggerEvent;
import org.apache.commons.scxml.env.SimpleDispatcher;
import org.apache.commons.scxml.env.SimpleErrorHandler;
import org.apache.commons.scxml.env.SimpleErrorReporter;
import org.apache.commons.scxml.env.jexl.JexlContext;
import org.apache.commons.scxml.env.jexl.JexlEvaluator;
import org.apache.commons.scxml.io.SCXMLParser;
import org.apache.commons.scxml.model.CustomAction;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.model.SCXML;
import org.apache.commons.scxml.model.State;
import org.apache.commons.scxml.model.Transition;
import org.apache.commons.scxml.model.TransitionTarget;
import org.xml.sax.SAXException;
public class SCXMLEngine {
private static SCXMLExecutor engine = null;
public static void main(String[] args) {
engine = new SCXMLExecutor(new JexlEvaluator(), new SimpleDispatcher(),
new SimpleErrorReporter());
// (1) Create a list of custom actions, add as many as are needed
List customActions = new ArrayList();
CustomAction ca = new CustomAction(
"http://my.custom-actions.domain/custom1", "hello", Hello.class);
customActions.add(ca);
SCXML stateMachine;
try {
stateMachine = SCXMLParser.parse(SCXMLEngine.class.getClassLoader()
.getResource("myfsm.xml"), new SimpleErrorHandler(),
customActions);
TransitionTarget tt = null;
Map targets = stateMachine.getTargets();
tt = (TransitionTarget)targets.get("paused");
stateMachine.setInitialTarget(tt);
engine.setStateMachine(stateMachine);
engine.setSuperStep(true);
engine.setRootContext(new JexlContext());
engine.addListener(stateMachine,
new SCXMLEngine().new EntryListener());
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (ModelException e) {
e.printStackTrace();
}
try {
engine.go();
} catch (ModelException me) {
me.printStackTrace();
}
String event = null;
Scanner input = new Scanner(System.in);
while (true) {
event = input.nextLine();
if (event.trim() != null && !event.trim().equals("")) {
if (event.equals("exit")) {
input.close();
break;
} else {
TriggerEvent[] evts = { new TriggerEvent(event,
TriggerEvent.SIGNAL_EVENT, null) };
try {
engine.triggerEvents(evts);
Status currStatus = engine.getCurrentStatus();
Set states = currStatus.getStates();
for(Object object : states) {
State state = ((State)object);
System.out.println("current status id is : " + state.getId());
/*if (((State)object).getId().equals("reset")) {
TransitionTarget parent = state.getParent();
System.out.println("parent is : " + parent.getId());
}*/
}
} catch (ModelException me) {
me.printStackTrace();
}
}
}
}
}
/**
* Invoke the no argument method with the following name.
*
* @param methodName The method to invoke.
* @return Whether the invoke was successful.
*/
public boolean invoke(final String methodName) {
Class clas = this.getClass();
try {
Method method = clas.getDeclaredMethod(methodName, new Class[0]);
method.invoke(this, new Object[0]);
} catch (SecurityException se) {
System.out.println(se);
return false;
} catch (NoSuchMethodException nsme) {
System.out.println(nsme);
return false;
} catch (IllegalArgumentException iae) {
System.out.println(iae);
return false;
} catch (IllegalAccessException iae) {
System.out.println(iae);
return false;
} catch (InvocationTargetException ite) {
System.out.println(ite);
return false;
}
return true;
}
public void reset() {
System.out.println("reset method called");
}
public void running() {
System.out.println("running method called");
}
public void paused() {
System.out.println("paused method called");
}
public void stopped() {
System.out.println("stopped method called");
}
/**
* A SCXMLListener that is only concerned about "onentry"
* notifications.
*/
protected class EntryListener implements SCXMLListener {
public void onEntry(final TransitionTarget entered) {
System.out.println("Entering State : " + entered.getId() + ", begin to invoke method " + entered.getId());
invoke(entered.getId());
}
public void onTransition(final TransitionTarget from,
final TransitionTarget to, final Transition transition) {
System.out.println("Transiting from " + from.getId() + " to "
+ to.getId());
}
public void onExit(final TransitionTarget exited) {
System.out.println("Exiting :" + exited.getId());
}
}
}
Hello.java
/*
* 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 com.ctrip.infosec.offline.fsm;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.scxml.ErrorReporter;
import org.apache.commons.scxml.EventDispatcher;
import org.apache.commons.scxml.SCInstance;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.apache.commons.scxml.TriggerEvent;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.model.Action;
/**
* Our custom "hello world" action.
*/
public class Hello extends Action {
/** Serial version UID. */
private static final long serialVersionUID = 1L;
/** This is who we say hello to. */
private String name;
/** We count callbacks to execute() as part of the test suite. */
public static int callbacks = 0;
/** Public constructor is needed for the I in SCXML IO. */
public Hello() {
super();
}
/**
* Get the name.
*
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* Set the name.
*
* @param name
* The name to set.
*/
public void setName(String name) {
this.name = name;
}
/*@Override
public void execute(ActionExecutionContext exctx) throws ModelException,
SCXMLExpressionException {
if (exctx.getAppLog().isInfoEnabled()) {
exctx.getAppLog().info("Hello " + name);
}
// For derived events payload testing
TriggerEvent event = new TriggerEvent("helloevent",
TriggerEvent.SIGNAL_EVENT, name);
exctx.getInternalIOProcessor().addEvent(event);
callbacks++;
}*/
@Override
public void execute(EventDispatcher evtDispatcher, ErrorReporter errRep,
SCInstance scInstance, Log appLog, Collection derivedEvents)
throws ModelException, SCXMLExpressionException {
/*if (appLog.isInfoEnabled()) {
appLog.info("Hello " + name);
}*/
System.out.println("Hello " + name);
// For derived events payload testing
TriggerEvent event = new TriggerEvent("watch.test",
TriggerEvent.SIGNAL_EVENT, name);
derivedEvents.add(event);
callbacks++;
System.out.println("callbacks=" + callbacks);
}
}
此外,官网还有一个StopWatch的usercase