17. Triggering Transitions

Driving a statemachine is done via transitions which are triggered by triggers. Currently supported triggers are EventTrigger and TimerTrigger.

17.1 EventTrigger

EventTrigger is the most useful trigger because it allows user to directly interact with a state machine by sending events to it. These events are also called signals. Trigger is added to a transition simply by associating a state to it during a configuration.

@Autowired
StateMachine<States, Events> stateMachine;

void signalMachine() {
    stateMachine.sendEvent(Events.E1);

    Message<Events> message = MessageBuilder
            .withPayload(Events.E2)
            .setHeader("foo", "bar")
            .build();
    stateMachine.sendEvent(message);
}

In above example we send an event using two different ways. Firstly we simply sent a type safe event using state machine api method sendEvent(E event). Secondly we send event wrapped in a Spring messaging Message using api method sendEvent(Message<E> message) with a custom event headers. This allows user to add arbitrary extra information with an event which is then visible to StateContext when for example user is implementing actions.

[Note]Note

Message headers are generally passed on until machine runs to completion for a specific event. For example if an event is causing transition into a state A which have an anonymous transition into a state B, original event is available for actions or guards in state B.

17.2 TimerTrigger

TimerTrigger is useful when something needs to be triggered automatically without any user interaction. Trigger is added to a transition by associating a timer with it during a configuration.

Currently there are two types of timers supported, one which fires continously and one which fires once a source state is entered.

@Configuration
@EnableStateMachine
public class Config2 extends StateMachineConfigurerAdapter<String, String> {

    @Override
    public void configure(StateMachineStateConfigurer<String, String> states)
            throws Exception {
        states
            .withStates()
                .initial("S1")
                .state("S2")
                .state("S3");
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<String, String> transitions)
            throws Exception {
        transitions
            .withExternal()
                .source("S1").target("S2").event("E1")
                .and()
            .withExternal()
                .source("S1").target("S3").event("E2")
                .and()
            .withInternal()
                .source("S2")
                .action(timerAction())
                .timer(1000)
                .and()
            .withInternal()
                .source("S3")
                .action(timerAction())
                .timerOnce(1000);
    }

    @Bean
    public TimerAction timerAction() {
        return new TimerAction();
    }
}

public class TimerAction implements Action<String, String> {

    @Override
    public void execute(StateContext<String, String> context) {
        // do something in every 1 sec
    }
}

In above we have three states, S1, S2 and S3. We have a normal external transition from S1 to S2 and from S1 to S3 with events E1 and E2 respectively. Interesting parts are when we define internal transitions for source states S2 and S3.

For both transitions we associate Action bean timerAction where source state S2 will use timer and S3 will use timerOnce. Values given are with milliseconds which in these cases mean 1000ms.

Once a state machine receive event E1 it does a transition from S1 to S2 and timer kicks in. As long as state is kept in S2 TimerTrigger executes and causes a transition associated with that state which in this case is the internal transition which has the timerAction defined.

Once a state machine receive event E2 it does a transition from S1 to S3 and timer kicks in. This timer is executed only once after state is entered after a delay defined in a timer.

[Note]Note

Behind a scenes timers are a simple triggers which may cause an transition to happen. Defining a transition with a timer() will keep firing triggers and only causes transition if source state is active. Transtition with timerOnce() is a little different as it will only trigger after a delay when source state is actually entered.

[Tip]Tip

Use timerOnce() if you want something to happen after a delay exactly once when state is entered.