How to get the current substate and the parent state out of the Spring Statemachine?

1k Views Asked by At

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!

2

There are 2 best solutions below

2
On BEST ANSWER

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();
0
On

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...