Squirrel
squirrel-foundation is a State Machine library, which provided a lightweight, easy use, type safe and programmable state machine implementation for Java.
Install / Use
/learn @hekailiang/SquirrelREADME
squirrel-foundation
What is it?
Just like the squirrel, a small, agile, smart, alert and cute animal, squirrel-foundation is aimed to provide a lightweight, highly flexible and extensible, diagnosable, easy use and type 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".
Maven
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.10</version>
</dependency>
Latest Snapshot Version:
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.11-SNAPSHOT</version>
</dependency>
Quick Start
To quickly try squirrel state machine functions, please create a maven project and include squirrel-foundation dependency properly. Then just run following sample code.
public class QuickStartSample {
// 1. Define State Machine Event
enum FSMEvent {
ToA, ToB, ToC, ToD
}
// 2. Define State Machine Class
@StateMachineParameters(stateType=String.class, eventType=FSMEvent.class, contextType=Integer.class)
static class StateMachineSample extends AbstractUntypedStateMachine {
protected void fromAToB(String from, String to, FSMEvent event, Integer context) {
System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+
"' with context '"+context+"'.");
}
protected void ontoB(String from, String to, FSMEvent event, Integer context) {
System.out.println("Entry State \'"+to+"\'.");
}
}
public static void main(String[] args) {
// 3. Build State Transitions
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
builder.onEntry("B").callMethod("ontoB");
// 4. Use State Machine
UntypedStateMachine fsm = 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]][9] [[qt-state-machine]][10]
User Guide
Get Starting
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.
-
StateMachine interface takes four generic type parameters.
- T stands for the type of implemented state machine.
- S stands for the type of implemented state.
- E stands for the type of implemented event.
- C stands for the type of implemented external context.
-
State Machine Builder
- State machine builder is used to generate state machine definition. StateMachineBuilder can be created by StateMachineBuilderFactory.
- The StateMachineBuilder is composed of *TransitionBuilder (InternalTransitionBuilder / LocalTransitionBuilder / ExternalTransitionBuilder) which is used to build transition between states, and EntryExitActionBuilder which is used to build the actions during entry or exit state.
- The internal state is implicitly built during transition creation or state action creation.
- All the state machine instances created by the same state machine builder share the same definition data for memory usage optimize.
- State machine builder generate state machine definition in a lazy manner. When builder create first state machine instance, the state machine definition will be generated which is time consumed. But after state machine definition generated, the following state machine instance creation will be much faster. Generally, state machine builder should be reused as much as possible.
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).
-
Fluent API
After state machine builder was created, we can use fluent API to define state/transition/action of the state machine.
builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.GoToB);An external 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);An internal 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( new Condition<MyContext>() { @Override public boolean isSatisfied(MyContext context) { return context!=null && context.getValue()>80; } @Override public String name() { return "MyCondition"; } }).callMethod("myInternalTransitionCall");An conditional 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 use [MVEL][7](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.
-
Method Call Action
User can define anonymous actions during define transitions or state entry/exit. However, the action code will be scattered over many places which may make code hard to maintain. Moreover, other user cannot override the actions. So squirrel-foundation also support to define state machine method call action which comes along with state machine class itself.
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 class public class MyStateMachine<...> extends AbstractStateMachine<...> { protected void fromAToB(MyState from, MyState to, MyEvent event, MyContext context) { // 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 a Convention 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.
protected void transitFromAToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)The method named as transitFrom[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.
protected void transitFromAnyToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)transitFromAnyTo[TargetStateName]On[EventName] The method will be invoked when transit from any state to state 'B' on event 'GoToB'.
protected void exitA(MyState from, MyState to, MyEvent event, MyContext context)exit[StateName] The method will be invoked when exit state 'A'. So as the entry[StateName] , beforeExitAny/afterExitAny and beforeEntryAny/afterEntryAny.
Other Supported Naming Patterns:
transitFrom[fromStateName]To[t
