Home > Software design >  How to get the current substate and the parent state out of the Spring Statemachine?
How to get the current substate and the parent state out of the Spring Statemachine?

Time:04-21

I am running a hierachical Spring Statemachine and - after walking through the inital transitions into state UP with the default substate STOPPED - want to use statemachine.getState(). Trouble is, it gives me only the parent state UP, and I cannot find an obvious way to retrieve both the parent state and the sub state.

The machine has states constructed like so:

    StateMachineBuilder.Builder<ToolStates, ToolEvents> builder = StateMachineBuilder.builder();


    builder.configureStates()
       .withStates()
          .initial(ToolStates.UP)
          .state(ToolStates.UP, new ToolUpEventAction(), null)
          .state(ToolStates.DOWN                
          .and()
       .withStates()
          .parent(ToolStates.UP)
          .initial(ToolStates.STOPPED)
          .state(ToolStates.STOPPED,new ToolStoppedEventAction(), null )
          .state(ToolStates.IDLE)
          .state(ToolStates.PROCESSING,
                 new ToolBeginProcessingPartAction(),
                 new ToolDoneProcessingPartAction());

    ...

    builder.build();

ToolStates and ToolEvents are just enums. In the client class, after running the builder code above, the statemachine is started with statemachine.start(); When I subsequently call statemachine.getState().getId(); it gives me UP. No events sent to statemachine before that call. I have been up and down the Spring statemachine docs and examples. I know from debugging that the entry actions of both states UP and STOPPED have been invoked, so I am assuming they are both "active" and would want to have both states presented when querying the statemachine. Is there a clean way to achieve this ? I want to avoid storing the substate somewhere from inside the Action classes, since I believe I have delegated all state management issues to the freakin Statemachine in the first place and I would rather like to learn how to use its API for this purpose.

Hopefully this is something embarrasingly obvious...

Any advice most welcome!

CodePudding user response:

The documentation describes getStates():

https://docs.spring.io/spring-statemachine/docs/current/api/org/springframework/statemachine/state/State.html

java.util.Collection<State<S,E>>    getStates()
Gets all possible states this state knows about including itself and substates.

stateMachine.getState().getStates();

CodePudding user response:

to wrap it up after SMA's most helpful advice: turns out the stateMachine.getState().getStates(); does in my case return a list of four elements:

  • a StateMachineState instance containing UP and STOPPED

  • three ObjectState instances containing IDLE, STOPPED and PROCESSING, respectively.

this leads me to go forward for the time being with the following solution:

public List<ToolStates> getStates() {
    List<ToolStates> result = new ArrayList<>();
    Collection<State<ToolStates, ToolEvents>> states = this.stateMachine.getState().getStates();
    Iterator<State<ToolStates, ToolEvents>> iter = states.iterator();
    while (iter.hasNext()) {
        State<ToolStates, ToolEvents> candidate = iter.next();
        if (!candidate.isSimple()) {
            Collection<ToolStates> ids = candidate.getIds();
            Iterator<ToolStates> i = ids.iterator();
            while (i.hasNext()) {
                result.add(i.next());
            }
        }
    }
    return result;
}

This maybe would be more elegant with some streaming and filtering, but does the trick for now. I don't like it much, though. It's a lot of error-prone logic and I'll have to see if it holds in the future - I wonder why there isn't a function in the Spring Statemachine that gives me a list of the enum values of all the currently active states, rather than giving me everything possible and forcing me to poke around in it with external logic...

  • Related