http://www.javacodegeeks.com/2012/06/apache-commons-scxml-finite-state.html
For full source code visit https://github.com/ozkansari/atmstatemachine
Apache Commons SCXML: Finite State Machine Implementation
atm_status.xml
<pre name="code" class="html"><scxml initial="idle" name="atm.connRestored" version="0.9" xmlns="http://www.w3.org/2005/07/scxml"><!-- node-size-and-position x=0.0 y=0.0 w=1050.0 h=590.0 -->
<state id="idle"><!-- node-size-and-position x=40.0 y=220.0 w=100.0 h=100.0 -->
<transition event="atm.connected" target="loading"></transition>
</state>
<state id="loading"><!-- node-size-and-position x=300.0 y=220.0 w=100.0 h=100.0 -->
<transition event="atm.loadSuccess" target="inService"></transition>
<transition event="atm.connClosed" target="disconnected"></transition>
<transition event="atm.loadFail" target="outOfService"></transition>
</state>
<state id="inService"><!-- node-size-and-position x=570.0 y=30.0 w=100.0 h=100.0 -->
<transition event="atm.shutdown" target="outOfService"><!-- edge-path [outOfService] x=580.0 y=190.0 pointx=0.0 pointy=3.0 offsetx=-1.0 offsety=-2.0 --></transition>
<transition event="atm.connLost" target="disconnected"><!-- edge-path [disconnected] x=757.0 y=270.0 pointx=0.0 pointy=-12.0 offsetx=13.0 offsety=0.0 --></transition>
</state>
<state id="outOfService"><!-- node-size-and-position x=570.0 y=260.0 w=100.0 h=100.0 -->
<transition event="atm.startup" target="inService"><!-- edge-path [inService] x=660.0 y=190.0 --></transition>
<transition event="atm.connLost" target="disconnected"></transition>
</state>
<state id="disconnected"><!-- node-size-and-position x=570.0 y=450.0 w=100.0 h=100.0 -->
<transition event="atm.connRestored" target="inService"><!-- edge-path [inService] x=860.0 y=275.0 --></transition>
</state>
</scxml>
AtmStatusEventEnum.java
package net.javafun.example.atmstatusfsm;
public enum AtmStatusEventEnum {
CONNECT("atm.connected"),
CONNECTION_CLOSED("atm.connClosed"),
CONNECTION_LOST("atm.connLost"),
CONNECTION_RESTORED("atm.connRestored"),
LOAD_SUCCESS("atm.loadSuccess"),
LOAD_FAIL("atm.loadFail"),
SHUTDOWN("atm.shutdown"),
STARTUP("atm.startup"),
;
private final String eventName;
private AtmStatusEventEnum(String eventName) {
this.eventName = eventName;
}
public String getEventName() {
return eventName;
}
public static String getNamesAsCsv(){
StringBuilder sb = new StringBuilder();
for (AtmStatusEventEnum e : AtmStatusEventEnum.values()) {
sb.append(e.name());
sb.append(",");
}
return sb.substring(0,sb.length()-2);
}
}
AtmStatusFSM.java
package net.javafun.example.atmstatusfsm;
import java.util.Collection;
import java.util.Set;
import org.apache.commons.scxml.env.AbstractStateMachine;
import org.apache.commons.scxml.model.State;
/**
* Atm Status Finite State Machine Example
*
* @see <a href="http://commons.apache.org/scxml/index.html "> Apache Commons Scxml Library </a>
* @author ozkans
*
*/
public class AtmStatusFSM extends AbstractStateMachine {
/**
* State Machine uses this scmxml config file
*/
// private static final String SCXML_CONFIG_ATM_STATUS = "net/javafun/example/atmfsm/atm_status.xml";
private static final String SCXML_CONFIG_ATM_STATUS = "atm_status.xml";
/* ------------------------------------------------------------------------ */
/* CONSTRUCTOR(S) */
/* ------------------------------------------------------------------------ */
public AtmStatusFSM() {
super(AtmStatusFSM.class.getClassLoader().getResource(SCXML_CONFIG_ATM_STATUS));
}
/* ------------------------------------------------------------------------ */
/* HELPER METHOD(S) */
/* ------------------------------------------------------------------------ */
public void firePreDefinedEvent(AtmStatusEventEnum eventEnum){
System.out.println("EVENT: " + eventEnum);
this.fireEvent(eventEnum.getEventName());
}
public void callState(String name){
this.invoke(name);
}
public String getCurrentStateId() {
Set<?> states = getEngine().getCurrentStatus().getStates();
State state = (State) states.iterator().next();
return state.getId();
}
public State getCurrentState() {
Set<?> states = getEngine().getCurrentStatus().getStates();
return ( (State) states.iterator().next());
}
public Collection<?> getCurrentStateEvents() {
return getEngine().getCurrentStatus().getEvents();
}
/* ------------------------------------------------------------------------ */
// STATES
//
// Each method below is the activity corresponding to a state in the
// SCXML document (see class constructor for pointer to the document).
/* ------------------------------------------------------------------------ */
public void idle() {
System.out.println("STATE: idle 被触发");
}
public void loading() {
System.out.println("STATE: loading 被触发");
}
public void inService() {
System.out.println("STATE: inService 被触发");
}
public void outOfService() {
System.out.println("STATE: outOfService 被触发");
}
public void disconnected() {
System.out.println("STATE: disconnected 被触发");
}
}
用Java JFrame UI测试:
package net.javafun.example.atmstatusfsm;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.apache.commons.scxml.model.Transition;
import org.apache.commons.scxml.model.TransitionTarget;
public class AtmDisplay extends JFrame implements ActionListener {
private static final long serialVersionUID = -5083315372455956151L;
private AtmStatusFSM atmStatusFSM;
private JButton button;
private JLabel state;
private JComboBox eventComboBox = new JComboBox();
public static void main(String[] args) {
new AtmDisplay();
}
public AtmDisplay() {
super("ATM Display Demo");
atmStatusFSM = new AtmStatusFSM();
/*
//仅用作测试设置初始化状态,不设置的话使用atm_status.xml里面默认的初始状态
@SuppressWarnings("unchecked")
Map<String, TransitionTarget> mapTg = atmStatusFSM.getEngine().getStateMachine().getTargets();
for(Map.Entry<String, TransitionTarget> entry : mapTg.entrySet()){
System.out.println(entry.getKey() + " ==== " + entry.getValue().getId());
}
TransitionTarget tsTarget = mapTg.get("inService");
atmStatusFSM.getEngine().getStateMachine().setInitialTarget(tsTarget); //设置初始状态为inService
atmStatusFSM.resetMachine();
atmStatusFSM.fireEvent(AtmStatusEventEnum.SHUTDOWN.getEventName());
*/
setupUI();
}
@SuppressWarnings("deprecation")
private void setupUI() {
System.out.println("setupUI");
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setContentPane(panel);
button = makeButton("FIRE_EVENT", AtmStatusEventEnum.getNamesAsCsv(), "Submit" );
panel.add(button, BorderLayout.CENTER);
state = new JLabel(atmStatusFSM.getCurrentStateId());
panel.add(state, BorderLayout.SOUTH);
initEvents();
panel.add(eventComboBox, BorderLayout.NORTH);
pack();
setLocation(200, 200);
setResizable(false);
setSize(300, 125);
show();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
@SuppressWarnings("unchecked")
private void initEvents() {
System.out.println("initEvents");
eventComboBox.removeAllItems();
List<Transition> transitionList = atmStatusFSM.getCurrentState().getTransitionsList();
for (Transition transition : transitionList) {
eventComboBox.addItem(transition.getEvent() );
}
}
public void actionPerformed(ActionEvent e) {
System.out.println("actionPerformed");
String command = e.getActionCommand();
if(command.equals("FIRE_EVENT")) {
checkAndFireEvent();
}
}
private boolean checkAndFireEvent() {
System.out.println("checkAndFireEvent");
atmStatusFSM.fireEvent(eventComboBox.getSelectedItem().toString());
state.setText(atmStatusFSM.getCurrentStateId());
initEvents();
repaint();
return true;
}
private JButton makeButton(final String actionCommand, final String toolTipText, final String altText) {
JButton button = new JButton(altText);
button.setActionCommand(actionCommand);
button.setToolTipText(toolTipText);
button.addActionListener(this);
button.setOpaque(false);
return button;
}
}
不用UI测试:
package net.javafun.example.atmstatusfsm;
import java.util.List;
import java.util.Map;
import org.apache.commons.scxml.model.Transition;
import org.apache.commons.scxml.model.TransitionTarget;
public class TestAtm {
public static void main(String[] args) {
AtmStatusFSM atmStatusFSM = new AtmStatusFSM();
setInitStatus(atmStatusFSM); //仅用作测试设置初始化状态,不设置的话使用atm_status.xml里面默认的初始状态
show(atmStatusFSM);
show2(atmStatusFSM);
//atmStatusFSM.fireEvent(AtmStatusEventEnum.CONNECT.getEventName()); //如果当前状态下没有该Event,那么不会出错,也不会fireEvent
atmStatusFSM.fireEvent(AtmStatusEventEnum.SHUTDOWN.getEventName());
//show2(atmStatusFSM);
}
private static void setInitStatus(AtmStatusFSM atmStatusFSM) {
System.out.println("手动设置初始状态:");
Map<String, TransitionTarget> mapTg = atmStatusFSM.getEngine().getStateMachine().getTargets();
TransitionTarget tsTarget = mapTg.get("inService");
atmStatusFSM.getEngine().getStateMachine().setInitialTarget(tsTarget);
atmStatusFSM.resetMachine();
System.out.println();
}
@SuppressWarnings("unchecked")
public static void show(AtmStatusFSM atmStatusFSM){
System.out.println("列出所有targets:");
Map<String,TransitionTarget> mapTg = atmStatusFSM.getEngine().getStateMachine().getTargets();
for(Map.Entry<String, TransitionTarget> entry : mapTg.entrySet()){
System.out.println(entry.getKey() + " ==== " + entry.getValue().getId());
}
System.out.println("列出所有States:");
Map<String,TransitionTarget> mapSt = atmStatusFSM.getEngine().getStateMachine().getStates();
for(Map.Entry<String, TransitionTarget> entry : mapSt.entrySet()){
System.out.println(entry.getKey() + " ==== " + entry.getValue().getId());
}
System.out.println();
}
public static void show2(AtmStatusFSM atmStatusFSM){
System.out.println("列出当前状态及其所有target:");
String currentStatus = atmStatusFSM.getCurrentStateId();
System.out.println("currentStatus: " + currentStatus);
System.out.println("currentStatus's all events: " );
//List<String> list = atmStatusFSM.getCurrentStateEvents();
List<Transition> transitionList = atmStatusFSM.getCurrentState().getTransitionsList();
for (Transition transition : transitionList) {
System.out.println(" " + transition.getEvent());
}
System.out.println();
}
}