spring-bootspring-statemachine

Spring state-machine: common transition


Is there a way to configure common external transition for multiple events? i.e.

  1. For all states X,Y,Z -> CANCELED on CANCEL event. Where X,Y,Z - sources, CANCELED - target state
  2. For CANCELED state run common internal transition on all events

I hame more than 20 states to apply this transition to and it is not convenient to manually configure all of them.

I am using EnumStateMachineConfigurerAdapter and @EnableStateMachineFactory to configure state machine, my configuration looks like this

@Configuration
@EnableStateMachineFactory
public class ContentOnlyStateMachineConfig extends EnumStateMachineConfigurerAdapter<TaskState, WorkflowEvent> {

private final Action<TaskState, WorkflowEvent> someAction;
private final Action<TaskState, WorkflowEvent> anotherAction;

@Autowired
public ContentOnlyStateMachineConfig(
        final Action<TaskState, WorkflowEvent> someAction,
        final Action<TaskState, WorkflowEvent> anotherAction) {
    this.someAction = someAction;
    this.anotherAction = anotherAction;
}




@Override
public void configure(final StateMachineStateConfigurer<TaskState, WorkflowEvent> states)
        throws Exception {

    states
            .withStates()
            .initial(TaskState.CONTENT)
            .end(TaskState.COMPLETE)
            .state(TaskState.CONTENT, someAction)
            .state(TaskState.COMPLETE, anotherAction);

}

@Override
public void configure(
        final StateMachineTransitionConfigurer<TaskState, WorkflowEvent> transitions)
        throws Exception {

    transitions
            .withExternal()
            .source(TaskState.CONTENT).target(TaskState.SOME_STATE)
            .event(WorkflowEvent.EXCEPTION)
            .and()
            .withExternal()
            .source(TaskState.EXCEPTION).target(TaskState.CANCELED)
            .event(WorkflowEvent.CANCEL)
            .and()
            .withExternal()
            .source(TaskState.SOME_STATE_2).target(TaskState.SOME_STATE_1)
            .event(WorkflowEvent.SOME_EVENT_3)
            .and()
            .withExternal()
            .source(TaskState.SOME_STATE).target(TaskState.SOME_STATE_2)
            .event(WorkflowEvent.SOME_EVENT_2)
            .and()
            .withExternal()
            .source(TaskState.SOME_STATE).target(TaskState.COMPLETE)
            .event(WorkflowEvent.ADMIN_ACCEPT)
            .and()
            .withExternal()
            .source(TaskState.SOME_STATE).target(TaskState.CONTENT)
            .event(WorkflowEvent.SOME_EVENT);
}
}

Solution

  • There's not shortcuts to allow user to create a spaghetti ;). Having said that, if your statechart after you have drawn it in a paper looks like spaghetti rather that clear statechart, I'd argue you're doing something wrong.

    Keeping everything in a one flat machine easily creates this kind of state explosion whey configuration itself is starting to look difficult to understand. Usually this is a moment when machine designer needs to start thinking about use of nested substates. If you have multiple states where same event should take machine to same state, you already have a shared functionality in those states.

    Putting those states into substates would then allow one single transition from their parent state to transit away. This is usually a model how things are done in machines.