10. Listening State Machine Events

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.

10.1 Application Context Events

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) {
    }
}

10.2 State Machine Listener

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;
    }

}

10.3 Limitations and Problems

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> {
}