There are use cases where you just want to know what is happening with a state machine, react to something or simply get logging for debugging purposes. SSM provides interfaces for adding listeners which then gives an option to get callback when various state changes, actions, etc are happening.
You basically have two options, either to listen Spring application context events or directly attach listener to a state machine. Both of these basically will provide same information where one is producing events as event classes and other producing callbacks via a listener interface. Both of these have pros and cons which will be discussed later.
Application context events classes are OnTransitionStartEvent, OnTransitionEvent, OnTransitionEndEvent, OnStateExitEvent, OnStateEntryEvent, OnStateChangedEvent, OnStateMachineStart and OnStateMachineStop. These can be used as is with spring typed ApplicationListener class but they also share a common class StateMachineEvent which can be used to get statemachine related events.
static class StateMachineApplicationEventListener implements ApplicationListener<StateMachineEvent> { @Override public void onApplicationEvent(StateMachineEvent event) { } }
Using StateMachineListener you can either extend it and implement all callback methods or use StateMachineListenerAdapter class which contains stub method implementations and choose which ones to override.
static class StateMachineEventListener extends StateMachineListenerAdapter<States, Events> { @Override public void stateChanged(State<States, Events> from, State<States, Events> to) { } @Override public void stateEntered(State<States, Events> state) { } @Override public void stateExited(State<States, Events> state) { } @Override public void transition(Transition<States, Events> transition) { } @Override public void transitionStarted(Transition<States, Events> transition) { } @Override public void transitionEnded(Transition<States, Events> transition) { } @Override public void stateMachineStarted(StateMachine<States, Events> stateMachine) { } @Override public void stateMachineStopped(StateMachine<States, Events> stateMachine) { } }
In above example we simply created our own listener class StateMachineEventListener which extends StateMachineListenerAdapter.
Once you have your own listener defined, it can be registered into a state machine via its interface as shown below. It’s just a matter of flavour if it’s hooked up within a spring configuration or done manually at any time of application life-cycle.
static class Config7 { @Autowired StateMachine<States, Events> stateMachine; @Bean public StateMachineEventListener stateMachineEventListener() { StateMachineEventListener listener = new StateMachineEventListener(); stateMachine.addStateListener(listener); return listener; } }
Spring application context is not a fastest event bus out there so it
is advised to give some thought what is a rate of events state machine
is sending. For better performance it may be better to use
StateMachineListener interface. For this specific reason it is
possible to use contextEvents
flag with @EnableStateMachine and
@EnableStateMachineFactory to disable Spring application context
events as shown above.
@Configuration @EnableStateMachine(contextEvents = false) public static class Config8 extends EnumStateMachineConfigurerAdapter<States, Events> { } @Configuration @EnableStateMachineFactory(contextEvents = false) public static class Config9 extends EnumStateMachineConfigurerAdapter<States, Events> { }