Strict Machine is a declarative DSL for building asynchronously evaluated Finite State Machines.
Release builds are available from Maven Central and SNAPSHOT builds are available from the Sonatype OSS Snapshot Repository.
dependencies {
compile("com.digitalpetri.fsm:strict-machine:1.0.0")
}
<dependencies>
<dependency>
<groupId>com.digitalpetri.fsm</groupId>
<artifactId>strict-machine</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Given this simple ATM example:
Define the State and Event types:
enum State {
Idle,
Loading,
OutOfService,
InService,
Disconnected
}
enum Event {
Connected,
ConnectionClosed,
ConnectionLost,
ConnectionRestored,
LoadFail,
LoadSuccess,
Shutdown,
Startup
}
Define the transitions:
FsmBuilder<State, Event> fb = new FsmBuilder<>();
/* Idle */
fb.when(State.Idle)
.on(Event.Connected)
.transitionTo(State.Loading);
/* Loading */
fb.when(State.Loading)
.on(Event.LoadSuccess)
.transitionTo(State.InService);
fb.when(State.Loading)
.on(Event.LoadFail)
.transitionTo(State.OutOfService);
fb.when(State.Loading)
.on(Event.ConnectionClosed)
.transitionTo(State.Disconnected);
/* OutOfService */
fb.when(State.OutOfService)
.on(Event.Startup)
.transitionTo(State.InService);
fb.when(State.OutOfService)
.on(Event.ConnectionLost)
.transitionTo(State.Disconnected);
/* InService */
fb.when(State.InService)
.on(Event.ConnectionLost)
.transitionTo(State.Disconnected);
fb.when(State.InService)
.on(Event.Shutdown)
.transitionTo(State.OutOfService);
/* Disconnected */
fb.when(State.Disconnected)
.on(Event.ConnectionRestored)
.transitionTo(State.InService);
Fsm<State, Event> fsm = fb.build(State.Idle);
Fire events to be evaluated asynchronously:
fsm.fireEvent(Event.Connected);
or block waiting for the state that resulted from evaluating the event:
State state = fsm.fireEventBlocking(Event.Connected);
Guard conditions that prevent a transition from occurring unless they're met can be defined using guardedBy
:
fb.when(State.Idle)
.on(Event.Connected)
.transitionTo(State.Loading)
.guardedBy(ctx -> ...guard condition here...);
Where the guard provided to guardedBy
is a Predicate
that receives the FsmContext
.
Of course, in order for your state machine to actually do something, you need to define actions that execute when events are evaluated and transitions occur.
This is accomplished using the onTransitionTo
, onTransitionFrom
, and onInternalTransition
methods on FsmBuilder
.
For example, logging a message on the transition from Idle
to Loading
might be defined in either of two ways:
// define the action in the context of a transition away from Idle
fb.onTransitionFrom(State.Idle)
.to(State.Loading)
.via(Event.Connected)
.execute(ctx -> System.out.println("S(Idle) x E(Connected) = S'(Loading)"));
// define the action in the context of a transition to Loading
fb.onTransitionTo(State.Loading)
.from(State.Idle)
.via(Event.Connected)
.execute(ctx -> System.out.println("S(Idle) x E(Connected) = S'(Loading)"));
See the full AtmFsm defined in the test suite.
Advanced examples from production code: