Just like the squirrel, asmall,agile,smart,alert andcute animal, squirrel-foundation is aimed to provide alightweight, highlyflexible andextensible,diagnosable,easy use andtype safe Java state machine implementation for enterprise usage.
Here is the state machine diagram which describes the state change of an ATM:

The sample code could be found in package“org.squirrelframework.foundation.fsm.atm”.
squirrel-foundation has been deployed to maven central repository, so you only need to add following dependency to the pom.xml.
Latest Released Version:
<dependency><groupId>org.squirrelframework</groupId> <artifactId>squirrel-foundation</artifactId> <version>0.3.8</version></dependency>Latest Snapshot Version:
<dependency><groupId>org.squirrelframework</groupId> <artifactId>squirrel-foundation</artifactId> <version>0.3.9-SNAPSHOT</version></dependency>To quickly try squirrel state machine functions, please create a maven project and include squirrel-foundation dependency properly. Then just run following sample code.
publicclassQuickStartSample{// 1. Define State Machine EventenumFSMEvent{ToA,ToB,ToC,ToD}// 2. Define State Machine Class@StateMachineParameters(stateType=String.class,eventType=FSMEvent.class,contextType=Integer.class)staticclassStateMachineSampleextendsAbstractUntypedStateMachine{protectedvoidfromAToB(Stringfrom,Stringto,FSMEventevent,Integercontext){System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+"' with context '"+context+"'.");}protectedvoidontoB(Stringfrom,Stringto,FSMEventevent,Integercontext){System.out.println("Entry State \'"+to+"\'.");}}publicstaticvoidmain(String[]args){// 3. Build State TransitionsUntypedStateMachineBuilderbuilder=StateMachineBuilderFactory.create(StateMachineSample.class);builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");builder.onEntry("B").callMethod("ontoB");// 4. Use State MachineUntypedStateMachinefsm=builder.newStateMachine("A");fsm.fire(FSMEvent.ToB,10);System.out.println("Current state is "+fsm.getCurrentState());}}At now you may have many questions about the sample code, please be patient. The following user guide will answer most of your questions. But before getting into the details, it requires you have basic understanding on state machine concepts. These materials are good for understanding state machine concepts.[state-machine-diagrams][qt-state-machine]
squirrel-foundation supports both fluent API and declarative manner to declare a state machine, and also enable user to define the action methods in a straightforward manner.
In order to create a state machine, user need to create state machine builder first. For example:
StateMachineBuilder<MyStateMachine,MyState,MyEvent,MyContext>builder=StateMachineBuilderFactory.create(MyStateMachine.class,MyState.class,MyEvent.class,MyContext.class);The state machine builder takes for parameters which are type of state machine(T), state(S), event(E) and context(C).
builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.GoToB);Anexternal transition is built between state ‘A’ to state ‘B’ and triggered on received event ‘GoToB’.
builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(myAction);Aninternal transition with priority set to high is build inside state ‘A’ on event ‘WithinA’ perform ‘myAction’. The internal transition means after transition complete, no state is exited or entered. The transition priority is used to override original transition when state machine extended.
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).when(newCondition<MyContext>(){@OverridepublicbooleanisSatisfied(MyContextcontext){returncontext!=null&&context.getValue()>80;}@OverridepublicStringname(){return"MyCondition";}}).callMethod("myInternalTransitionCall");Anconditional transition is built from state ‘C’ to state ‘D’ on event ‘GoToD’ when external context satisfied the condition restriction, then call action method “myInternalTransitionCall”. User can also useMVEL(a powerful expression language) to describe condition in the following way.
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).whenMvel("MyCondition:::(context!=null && context.getValue()>80)").callMethod("myInternalTransitionCall");Note: Characters ‘:::’ use to separate condition name and condition expression. The ‘context’ is the predefined variable point to current Context object.
builder.onEntry(MyState.A).perform(Lists.newArrayList(action1,action2))A list of state entry actions is defined in above sample code.
StateMachineBuilder<...>builder=StateMachineBuilderFactory.create(MyStateMachine.class,MyState.class,MyEvent.class,MyContext.class);builder.externalTransition().from(A).to(B).on(toB).callMethod("fromAToB");// All transition action method stays with state machine classpublicclassMyStateMachine<...>extendsAbstractStateMachine<...>{protectedvoidfromAToB(MyStatefrom,MyStateto,MyEventevent,MyContextcontext){// this method will be called during transition from "A" to "B" on event "toB"// the action method parameters types and order should match...}}Moreover, squirrel-foundation also support define method call actions in aConvention Over Configuration manner. Basically, this means that if the method declared in state machine satisfied naming and parameters convention, it will be added into the transition action list and also be invoked at certain phase. e.g.
protectedvoidtransitFromAToBOnGoToB(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)The method named astransitFrom[SourceStateName]To[TargetStateName]On[EventName], and parameterized as [MyState, MyState, MyEvent, MyContext] will be added into transition “A-(GoToB)->B” action list. When transiting from state ‘A’ to state ‘B’ on event ‘GoToB’, this method will be invoked.
protectedvoidtransitFromAnyToBOnGoToB(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)transitFromAnyTo[TargetStateName]On[EventName] The method will be invoked when transit from any state to state ‘B’ on event ‘GoToB’.
protectedvoidexitA(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)exit[StateName] The method will be invoked when exit state ‘A’. So as theentry[StateName] ,beforeExitAny/afterExitAny andbeforeEntryAny/afterEntryAny.
Other Supported Naming Patterns:
transitFrom[fromStateName]To[toStateName]On[eventName]When[conditionName] transitFrom[fromStateName]To[toStateName]On[eventName] transitFromAnyTo[toStateName]On[eventName] transitFrom[fromStateName]ToAnyOn[eventName] transitFrom[fromStateName]To[toStateName] on[eventName]Those method conventions listed above also providedAOP-like functionalities, which provided build-in flexible extension capability for squirrel state machine at any granularity.
For more information, please refer to test case “org.squirrelframework.foundation.fsm.ExtensionMethodCallTest”.
Since 0.3.1, there is another way to define these AOP-like extension methods which is through fluent API (thanks suggestion fromvittali), e.g.
// since 0.3.1// the same effect as add method transitFromAnyToCOnToC in your state machinebuilder.transit().fromAny().to("C").on("ToC").callMethod("fromAnyToC");// the same effect as add method transitFromBToAnyOnToC in your state machinebuilder.transit().from("B").toAny().on("ToC").callMethod("fromBToAny");// the same effect as add method transitFromBToAny in your state machinebuilder.transit().from("B").toAny().onAny().callMethod("fromBToAny");Or through declarative annotation, e.g.
// since 0.3.1@Transitions({@Transit(from="B",to="E",on="*",callMethod="fromBToEOnAny"),@Transit(from="*",to="E",on="ToE",callMethod="fromAnyToEOnToE")})Note: These action methods will be attached tomatched and already existed transitions but not to create any new transitions.
Since 0.3.4, multiple transitions can also be defined once at a time using following API, e.g.
// transitions(A->B@A2B=>a2b, A->C@A2C=>a2c, A->D@A2D) will be defined at oncebuilder.transitions().from(State._A).toAmong(State.B,State.C,State.D).onEach(Event.A2B,Event.A2C,Event.A2D).callMethod("a2b|a2c|_");// transitions(A->_A@A2ANY=>DecisionMaker, _A->A@ANY2A) will be defined at oncebuilder.localTransitions().between(State.A).and(State._A).onMutual(Event.A2ANY,Event.ANY2A).perform(Lists.newArrayList(newDecisionMaker("SomeLocalState"),null));More information can be found inorg.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest;
@States({@State(name="A",entryCallMethod="entryStateA",exitCallMethod="exitStateA"),@State(name="B",entryCallMethod="entryStateB",exitCallMethod="exitStateB")})@Transitions({@Transit(from="A",to="B",on="GoToB",callMethod="stateAToStateBOnGotoB"),@Transit(from="A",to="A",on="WithinA",callMethod="stateAToStateAOnWithinA",type=TransitionType.INTERNAL)})interfaceMyStateMachineextendsStateMachine<MyStateMachine,MyState,MyEvent,MyContext>{voidentryStateA(MyStatefrom,MyStateto,MyEventevent,MyContextcontext);voidstateAToStateBOnGotoB(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)voidstateAToStateAOnWithinA(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)voidexitStateA(MyStatefrom,MyStateto,MyEventevent,MyContextcontext);...}The annotation can be defined in both implementation class of state machine or any interface that state machine will be implemented. It also can be used mixed with fluent API, which means the state machine defined in fluent API can also be extended by these annotations. (One thing you may need to be noticed, the method defined within interface must be public, which means also the method call action implementation will be public to caller.)
publicinterfaceConverter<T>extendsSquirrelComponent{/** * Convert object to string. * @param obj converted object * @return string description of object */StringconvertToString(Tobj);/** * Convert string to object. * @param name name of the object * @return converted object */TconvertFromString(Stringname);}Then register these converters toConverterProvider. e.g.
ConverterProvider.INSTANCE.register(MyEvent.class,newMyEventConverter());ConverterProvider.INSTANCE.register(MyState.class,newMyStateConverter());Note: If you only use fluent API to define state machine, there is no need to implement corresponding converters. And also if the Event or State class is type of String or Enumeration, you don’t need to implement or register a converter explicitly at most of cases.
TnewStateMachine(SinitialStateId,Object...extraParams);To create a new state machine instance from state machine builder, you need to pass following parameters.
initialStateId: When started, the initial state of the state machine.extraParams: Extra parameters that needed for create new state machine instance. Set to“new Object[0]” for no extra parameters needed.
a. If user passed extra parameters while creating a new state machine instance, please be sure that StateMachineBuilderFactory also had defined type of extra parameters when creating the state machine builder. Otherwise, extra parameter will be ignored.
b. Extra parameters can be passed into state machine instance in two ways. One is through state machine constructor which means user need to define a constructor with the same parameters’ type and order for the state machine instance. Another way is define a method namedpostConstruct and also with the same parameters’ type and order.
If no extra parameters need to passed to state machine, user can simply callT newStateMachine(S initialStateId) to create a new state machine instance.
New state machine from state machine builder. (In this case, no extra parameters need to be passed.)
MyStateMachinestateMachine=builder.newStateMachine(MyState.Initial);stateMachine.fire(MyEvent.Prepare,newMyContext("Testing"));enumTestEvent{toA,toB,toC,toD}@Transitions({@Transit(from="A",to="B",on="toB",callMethod="fromAToB"),@Transit(from="B",to="C",on="toC"),@Transit(from="C",to="D",on="toD")})@StateMachineParameters(stateType=String.class,eventType=TestEvent.class,contextType=Integer.class)classUntypedStateMachineSampleextendsAbstractUntypedStateMachine{// No need to specify constructor anymore since 0.2.9// protected UntypedStateMachineSample(ImmutableUntypedState initialState,// Map<Object, ImmutableUntypedState> states) {// super(initialState, states);// }protectedvoidfromAToB(Stringfrom,Stringto,TestEventevent,Integercontext){// transition action still type safe ...}protectedvoidtransitFromDToAOntoA(Stringfrom,Stringto,TestEventevent,Integercontext){// transition action still type safe ...}}UntypedStateMachineBuilderbuilder=StateMachineBuilderFactory.create(UntypedStateMachineSample.class);// state machine builder not type safe anymorebuilder.externalTransition().from("D").to("A").on(TestEvent.toA);UntypedStateMachinefsm=builder.newStateMachine("A");To build an UntypedStateMachine, user need to create an UntypedStateMachineBuilder through StateMachineBuilderFactory first. StateMachineBuilderFactory takes only one parameter which is type of state machine class to create UntypedStateMachineBuilder.@StateMachineParameters is used to declare state machine generic parameter types.AbstractUntypedStateMachine is the base class of any untyped state machine.
@ContextInsensitivepublicclassATMStateMachineextendsAbstractStateMachine<ATMStateMachine,ATMState,String,Void>{// no need to add context parameter here anymorepublicvoidtransitFromIdleToLoadingOnConnected(ATMStatefrom,ATMStateto,Stringevent){...}publicvoidentryLoading(ATMStatefrom,ATMStateto,Stringevent){...}}protectedvoidafterTransitionCausedException(...){throwe;}If state machine can be recovered from this exception, user can extend afterTransitionCausedException method, and add corresponding the recovery logic in this method.DONOT forget to set state machine status back to normal at the end. e.g.
@OverrideprotectedvoidafterTransitionCausedException(ObjectfromState,ObjecttoState,Objectevent,Objectcontext){ThrowabletargeException=getLastException().getTargetException();// recover from IllegalArgumentException thrown out from state 'A' to 'B' caused by event 'ToB'if(targeExceptioninstanceofIllegalArgumentException&&fromState.equals("A")&&toState.equals("B")&&event.equals("ToB")){// do some error clean up job here// ...// after recovered from this exception, reset the state machine status back to normalsetStatus(StateMachineStatus.IDLE);}elseif(...){// recover from other exception ...}else{super.afterTransitionCausedException(fromState,toState,event,context);}}voiddefineSequentialStatesOn(SparentStateId,S...childStateIds);builder.defineSequentialStatesOn(State.A, State.BinA, StateCinA) defines two child states “BinA” and “CinA” under parent state “A”, the first defined child state will also be the initial state of the hierarchical state “A”. The same hierarchical state can also be defined through annotation, e.g.
@States({@State(name="A",entryMethodCall="entryA",exitMethodCall="exitA"),@State(parent="A",name="BinA",entryMethodCall="entryBinA",exitMethodCall="exitBinA",initialState=true),@State(parent="A",name="CinA",entryMethodCall="entryCinA",exitMethodCall="exitCinA")})
// defines two region states "RegionState1" and "RegionState2" under parent parallel state "Root"builder.defineParallelStatesOn(MyState.Root,MyState.RegionState1,MyState.RegionState2);builder.defineSequentialStatesOn(MyState.RegionState1,MyState.State11,MyState.State12);builder.externalTransition().from(MyState.State11).to(MyState.State12).on(MyEvent.Event1);builder.defineSequentialStatesOn(MyState.RegionState2,MyState.State21,MyState.State22);builder.externalTransition().from(MyState.State21).to(MyState.State22).on(MyEvent.Event2);or
@States({@State(name="Root",entryCallMethod="enterRoot",exitCallMethod="exitRoot",compositeType=StateCompositeType.PARALLEL),@State(parent="Root",name="RegionState1",entryCallMethod="enterRegionState1",exitCallMethod="exitRegionState1"),@State(parent="Root",name="RegionState2",entryCallMethod="enterRegionState2",exitCallMethod="exitRegionState2")})To get current sub states of the parallel state
stateMachine.getSubStatesOn(MyState.Root);// return list of current sub states of parallel stateWhen all the parallel states reached final state, aFinish context event will be fired.
@ContextEvent(finishEvent="Finish")staticclassParallelStateMachineextendsAbstractStateMachine<...>{}or
StateMachineBuilder<...>builder=StateMachineBuilderFactory.create(...);...builder.defineFinishEvent(HEvent.Start);builder.defineTerminateEvent(HEvent.Terminate);builder.defineStartEvent(HEvent.Finish);// defined history type of state "A" as "deep"builder.defineSequentialStatesOn(MyState.A,HistoryType.DEEP,MyState.A1,MyState.A2)or
@State(parent="A",name="A1",entryCallMethod="enterA1",exitCallMethod="exitA1",historyType=HistoryType.DEEP)Note: Before 0.3.7, user need to define “HistoryType.DEEP” for each level of historical state, which is not quite convenient.(Thanks toVoskuijlen to provide solutionIssue33). Now user only define “HistoryType.DEEP” at the top level of historical state, and all its children state historical information will be remembered.
Transition Types
According to the UML specification, a transition may be one of these three kinds:
- Internal Transition Implies that the Transition, if triggered, occurs without exiting or entering the source State (i.e., it does not cause a state change). This means that the entry or exit condition of the source State will not be invoked. An internal Transition can be taken even if the StateMachine is in one or more Regions nested within the associated State.
- Local Transition
Implies that the Transition, if triggered, will not exit the composite (source) State, but it will exit and re-enter any state within the composite State that is in the current state configuration.- External Transition
Implies that the Transition, if triggered, will exit the composite (source) State
squirrel-foundation supports both API and annotation to declare all kinds of transitions, e.g.
builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.A2B);builder.internalTransition().within(MyState.A).on(MyEvent.innerA);builder.localTransition().from(MyState.A).to(MyState.CinA).on(MyEvent.intoC)or
@Transitions({@Transition(from="A",to="B",on="A2B"),//default value of transition type is EXTERNAL@Transition(from="A",on="innerA",type=TransitionType.INTERNAL),@Transition(from="A",to="CinA",on="intoC",type=TransitionType.LOCAL),})State Machine Lifecycle Events|--StateMachineEvent /* Base event of all state machine event */ |--StartEvent/* Fired when state machine started */ |--TerminateEvent/* Fired when state machine terminated */ |--TransitionEvent/* Base event of all transition event */ |--TransitionBeginEvent/* Fired when transition began */ |--TransitionCompleteEvent/* Fired when transition completed */ |--TransitionExceptionEvent/* Fired when transition threw exception */ |--TransitionDeclinedEvent/* Fired when transition declined */ |--TransitionEndEvent/* Fired when transition end no matter declined or complete */User can add a listener to listen StateMachineEvent, which means all events fired during state machine lifecycle will be caught by this listener, e.g.,
stateMachine.addStateMachineListener(newStateMachineListener<...>(){@OverridepublicvoidstateMachineEvent(StateMachineEvent<...>event){// ...}});And User can also add a listener to listen TransitionEvent through StateMachine.addTransitionListener, which means all events fired during each state transition including TransitionBeginEvent, TransitionCompleteEvent and TransitionEndEvent will be caught by this listener.
Or user can add specific listener e.g. TransitionDeclinedListener to listen TransitionDeclinedEvent when transition request was declined.
staticclassExternalModule{@OnTransitionEnd@ListenerOrder(10)// Since 0.3.1 ListenerOrder can be used to insure listener invoked orderlypublicvoidtransitionEnd(){// method annotated with TransitionEnd will be invoked when transition end...// the method must be public and return nothing}@OnTransitionBeginpublicvoidtransitionBegin(TestEventevent){// method annotated with TransitionBegin will be invoked when transition begin...}// 'event'(E), 'from'(S), 'to'(S), 'context'(C) and 'stateMachine'(T) can be used in MVEL scripts@OnTransitionBegin(when="event.name().equals(\"toB\")")publicvoidtransitionBeginConditional(){// method will be invoked when transition begin while transition caused by event "toB"}@OnTransitionCompletepublicvoidtransitionComplete(Stringfrom,Stringto,TestEventevent,Integercontext){// method annotated with TransitionComplete will be invoked when transition complete...}@OnTransitionDeclinepublicvoidtransitionDeclined(Stringfrom,TestEventevent,Integercontext){// method annotated with TransitionDecline will be invoked when transition declined...}@OnBeforeActionExecutedpublicvoidonBeforeActionExecuted(ObjectsourceState,ObjecttargetState,Objectevent,Objectcontext,int[]mOfN,Action<?,?,?,?>action){// method annotated with OnAfterActionExecuted will be invoked before action invoked}@OnAfterActionExecutedpublicvoidonAfterActionExecuted(ObjectsourceState,ObjecttargetState,Objectevent,Objectcontext,int[]mOfN,Action<?,?,?,?>action){// method annotated with OnAfterActionExecuted will be invoked after action invoked}@OnActionExecExceptionpublicvoidonActionExecException(Action<?,?,?,?>action,TransitionExceptione){// method annotated with OnActionExecException will be invoked when action thrown exception}}ExternalModuleexternalModule=newExternalModule();fsm.addDeclarativeListener(externalModule);...fsm.removeDeclarativeListener(externalModule);By doing this external module code does not need to implement any state machine listener interface. Only add few annotations on methods which will be hooked during transition phase. The parameters of method is also type safe, and will automatically be inferred to match corresponding event. This is a good approach forSeparation of Concerns. User can find sample usage inorg.squirrelframework.foundation.fsm.StateMachineLogger.
protectedvoidafterTransitionCausedException(Exceptione,SfromState,StoState,Eevent,Ccontext){}protectedvoidbeforeTransitionBegin(SfromState,Eevent,Ccontext){}protectedvoidafterTransitionCompleted(SfromState,StoState,Eevent,Ccontext){}protectedvoidafterTransitionEnd(SfromState,StoState,Eevent,Ccontext){}protectedvoidafterTransitionDeclined(SfromState,Eevent,Ccontext){}protectedvoidbeforeActionInvoked(SfromState,StoState,Eevent,Ccontext){}Typically, user can hook in your business processing logic in these extension methods during each state transition, while the various event listener serves as boundary of state machine based control system, which can interact with external modules (e.g. UI, Auditing, ESB and so on).
For example, user can extend the method afterTransitionCausedException for environment clean up when exception happened during transition, and also notify user interface module to display error message through TransitionExceptionEvent.
// define state entry action 'goEntryD' weight -150@State(name="D",entryCallMethod="goEntryD:-150")// define transition action 'goAToC1' weight +150@Transit(from="A",to="C",on="ToC",callMethod="goAToC1:+150")Another way is override weight method of Action class, e.g.
Action<...>newAction=newAction<...>(){...@Overridepublicintweight(){return100;}}squirrel-foundation also support a conventional manner to declare action weight. The weight of method call action whose name started with ‘before’ will be set to 100, so as the name started with ‘after’ will be set to -100. Generally it means that the action method name started with ‘before’ will be invoked at first, while the action method name started with ‘after’ will be invoked at last. “method1:ignore” means method1 will not be invoked.
For more information, please refer to test case ‘org.squirrelframework.foundation.fsm.WeightedActionTest’;
@ContextInsensitive@StateMachineParameters(stateType=String.class,eventType=String.class,contextType=Void.class)publicclassConcurrentSimpleStateMachineextendsAbstractUntypedStateMachine{// No need to specify constructor anymore since 0.2.9// protected ConcurrentSimpleStateMachine(ImmutableUntypedState initialState,// Map<Object, ImmutableUntypedState> states) {//super(initialState, states);// }@AsyncExecuteprotectedvoidfromAToB(Stringfrom,Stringto,Stringevent){// this action method will be invoked asynchronously}}Define asynchronously dispatched event:
publicclassDeclarativeListener{@OnTransitionBegin@AsyncExecutepublicvoidonTransitionBegin(...){// transition begin event will be dispatched asynchronously to this listener method}}Asynchronous execution task will be submit to aExecutorService. User can register your ExecutorService implementation instance throughSquirrelSingletonProvider, e.g.
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);SquirrelSingletonProvider.getInstance().register(ExecutorService.class,executorService);If no ExecutorService instance was registered,SquirrelConfiguration will provide a default one.
// 1 User defined a state machine interfaceinterfaceMyStateMachineextendsStateMachine<MyStateMachine,MyState,MyEvent,MyContext>{...}// 2 Both MyStateMachineImpl and MyStateMachineImplEx are implemented MyStateMachineclassMyStateMachineImplimplementsMyStateMachine{...}classMyStateMachineImplEximplementsMyStateMachine{...}// 3 User define a state machine post processorMyStateMachinePostProcessorimplementsSquirrelPostProcessor<MyStateMachine>{voidpostProcess(MyStateMachinecomponent){...}}// 4 User register state machine post processSquirrelPostProcessorProvider.getInstance().register(MyStateMachine.class,MyStateMachinePostProcessor.class);For this case, when user created both MyStateMachineImpl and MyStateMachineImplEx instance, the registered post processor MyStateMachinePostProcessor will be called to do some work.
SCXMLVisitorvisitor=SquirrelProvider.getInstance().newInstance(SCXMLVisitor.class);stateMachine.accept(visitor);visitor.convertSCXMLFile("MyStateMachine",true);BTW, user can also callStateMachine.exportXMLDefinition(true) to export beautified XML definition.
DotVisitor can be used to generate state diagram which can be viewed byGraphViz.
DotVisitorvisitor=SquirrelProvider.getInstance().newInstance(DotVisitor.class);stateMachine.accept(visitor);visitor.convertDotFile("SnakeStateMachine");UntypedStateMachineBuilderbuilder=newUntypedStateMachineImporter().importDefinition(scxmlDef);ATMStateMachinestateMachine=builder.newAnyStateMachine(ATMState.Idle);Note: The UntypedStateMachineImporter provided an XML-style to define the state machine just like the state machine builder API or declarative annotations. The SCXML-similar definition is not equal to standard SCXML.
StateMachineData.Reader<MyStateMachine,MyState,MyEvent,MyContext>savedData=stateMachine.dumpSavedData();And also user can load abovesavedData into another state machine whose status is terminated or just initialized.
newStateMachineInstance.loadSavedData(savedData);NOTE: The state machine data can be serialized to/deserialized from Base64 encoded string with the help ofObjectSerializableSupport class.
UntypedStateMachinefsm=builder.newUntypedStateMachine("a",StateMachineConfiguration.create().enableAutoStart(false).setIdProvider(IdProvider.UUIDProvider.getInstance()),newObject[0]);// since 0.3.0fsm.fire(TestEvent.toA);The sample code above is used to create a state machine instance with UUID as its identifier and disable auto start function.
StateMachineConfigure can also be set on state machine builder which means all the state machine instance created bybuilder.newStateMachine(S initialStateId) orbuilder.newStateMachine(S initialStateId, Object... extraParams) will use this configuration.
StateMachine<?,?,?,?>stateMachine=builder.newStateMachine(HState.A);StateMachineLoggerfsmLogger=newStateMachineLogger(stateMachine);fsmLogger.startLogging();...stateMachine.fire(HEvent.B2A,1);...fsmLogger.terminateLogging();-------------------------------------------------------------------------------------------ConsoleLog:HierachicalStateMachine:Transitionfrom"B2a"on"B2A"withcontext"1"begin.Beforeexecutemethodcallaction"leftB2a"(1of6).Beforeexecutemethodcallaction"exitB2"(2of6)....Beforeexecutemethodcallaction"entryA1"(6of6).HierachicalStateMachine:Transitionfrom"B2a"to"A1"on"B2A"completewhichtook2ms....Since v0.3.0 state machine logger can be used more easy way by just set StateMachineConfiguration enable debug mode to ture, e.g.
StateMachine<?,?,?,?> stateMachine = builder.newStateMachine(HState.A, StateMachineConfiguration.create().enableDebugMode(true), new Object[0]);StateMachinePerformanceMonitor can be used to monitor state machine execution performance information, including total transition times count, average transition consumed time and so on, e.g.
finalUntypedStateMachinefsm=builder.newStateMachine("D");finalStateMachinePerformanceMonitorperformanceMonitor=newStateMachinePerformanceMonitor("Sample State Machine Performance Info");fsm.addDeclarativeListener(performanceMonitor);for(inti=0;i<10000;i++){fsm.fire(FSMEvent.ToA,10);fsm.fire(FSMEvent.ToB,10);fsm.fire(FSMEvent.ToC,10);fsm.fire(FSMEvent.ToD,10);}fsm.removeDeclarativeListener(performanceMonitor);System.out.println(performanceMonitor.getPerfModel());-------------------------------------------------------------------------------------------ConsoleLog:==========================SampleStateMachinePerformanceInfo==========================TotalTransitionInvoked:40000TotalTransitionFailed:0TotalTransitionDeclained:0AverageTransitionComsumed:0.0004msTransitionKeyInvokedTimesAverageTimeMaxTimeMinTimeC--{ToD,10}->D100000.0007ms5ms0msB--{ToC,10}->C100000.0001ms1ms0msD--{ToA,10}->A100000.0009ms7ms0msA--{ToB,10}->B100000.0000ms1ms0msTotalActionInvoked:40000TotalActionFailed:0AverageActionExecutionComsumed:0.0000msActionKeyInvokedTimesAverageTimeMaxTimeMinTimeinstan...Test$1400000.0000ms1ms0ms==========================SampleStateMachinePerformanceInfo==========================Add@LogExecTime on action method will log out the execution time of the method. And also add the@LogExecTime on state machine class will log out all the action method execution time. For example, the execution time of methodtransitFromAToBOnGoToB will be logged out.
@LogExecTimeprotectedvoidtransitFromAToBOnGoToB(MyStatefrom,MyStateto,MyEventevent,MyContextcontext)ScheduledExecutorServicescheduler=Executors.newScheduledThreadPool(1);SquirrelSingletonProvider.getInstance().register(ScheduledExecutorService.class,scheduler);If no ScheduledExecutorService instance was registered,SquirrelConfiguration will provide a default one. After that, a timed state can be defined by state machine builder, e.g.
// after 50ms delay fire event "FIRST" every 100ms with null contextbuilder.defineTimedState("A",50,100,"FIRST",null);builder.internalTransition().within("A").on("FIRST");NOTE: Make sure timed state must be defined before describe its transitions or entry/exit actions.timeInterval less than or equal to 0 will be considered only execute once afterinitialDelay.
Linked State (so called Submachine State)
Alinked state specifies the insertion of the specification of a submachine state machine. The state machine that contains the linked state is called the containing state machine. The same state machine may be a submachine more than once in the context of a single containing state machine.
A linked state is semantically equivalent to a composite state. The regions of the submachine state machine are the regions of the composite state. The entry, exit, and behavior actions and internal transitions are defined as part of the state. Submachine state is a decomposition mechanism that allows factoring of common behaviors and their reuse.
The linked state can be defined by following sample code.
builderOfTestStateMachine.definedLinkedState(LState.A,builderOfLinkedStateMachine,LState.A1);UntypedStateMachineBuilderbuilder=StateMachineBuilderFactory.create(...);builder.setStateMachineConfiguration(StateMachineConfiguration.create().enableRemoteMonitor(true));SeeEXAMPLES file.
SeeRELEASE NOTES file.

If you use Squirrel State Machine code in your application, I’ll be appreciate if you inform the author about it (email: hekailiang@gmail.com) like this:
Subject: Squirrel State Machine Usage NotificationText: I use Squirrel State Machine <lib_version> in <project_name> - http://link_to_project. I [allow | don’t allow] to mention my project in section “Who using Squirrel State Machine” on GitHub.