Saving and Restoring Current State in Spring Statemachine

I am introducing Spring Statemachine into an existing project, hoping to integrate and clarify our business logic. We have different JPA entities with associated states, and I am having problems setting the state while maintaining the current state of the existing state machine.

I use StateMachineFactory to create a new StateMachine instance for each instance of the object. I save the current state of StateMachine in a separate Hibernate field to save, and ideally you need to synchronize the value of the saved field with StateMachine. My question is how this should normally be achieved in Spring Statemachine.

@Entity @EntityListeners(MyEntityListener.class) public class MyEntity { @Column private MyState internalState; // Using AttributeConverter @Transient private StateMachine<MyState, Event> stateMachine; } 
 public class MyEntityListener { @PostLoad public void postLoad(MyEntity entity) { // TODO Set StateMachine current state to entity internal state ); } 
  • One approach may be to define local transitions to move the initial state to a constant state. Then I could do a conditional check to find the event associated with the local transition that would move the source state to the target state. It seems a little dirty to me, and I would like my configuration of the state of the machine to be as clean as possible.

  • I don’t see how I can set the current state of StateMachine through the open API without going through the transition, and so the other approach I studied is to wrap an instance of StateMachine to output the following method (since it’s convenient by default Volume) :

 package org.springframework.statemachine.support; public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSupport<S, E> implements StateMachine<S, E>, StateMachineAccess<S, E> { void setCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, boolean exit, StateMachine<S, E> stateMachine) } 
 package org.springframework.statemachine.support; public class MyStateMachineWrapper<S, E> { private AbstractStateMachine<S, E> stateMachine; public MyStateMachineWrapper(StateMachine<S, E> stateMachine) { if (stateMachine instanceof AbstractStateMachine) { this.stateMachine = (AbstractStateMachine<S, E>)stateMachine; } else { throw new IllegalArgumentException("Provided StateMachine is not a valid type"); } } public void setCurrentState(S status) { stateMachine.setCurrentState(findState(status), null, null, false, stateMachine); } private State<S, E> findState(S status) { for (State<S, E> state : stateMachine.getStates()) { if (state.getId() == status) { return state; } } throw new IllegalArgumentException("Specified status does not equate to valid State"); } } 

Then I could output the following code in MyEntityListener.postLoad:

 MyStateMachineWrapper<MyState, Event> myStateMachineWrapper = new MyStateMachineWrapper<>(entity.getStateMachine()); myStateMachineWrapper.setCurrentState(entity.getInternalState()); 

The above approach works fine, but I can’t imagine how it was intended to work. Surely there is a cleaner method to achieve this, or maybe the project is not mature enough and does not include this functionality yet?

Thanks for any thoughts and opinions.

+5
source share
1 answer

I cleared option # 2 above by changing the wrapper class to the utils class. To be clear, this approach uses the setCurrentState method, which has a default accessory, and therefore it can be a fragile solution.

 package org.springframework.statemachine.support; public abstract class MyStateMachineUtils extends StateMachineUtils { public static <S, E> void setCurrentState(StateMachine<S, E> stateMachine, S state) { if (stateMachine instanceof AbstractStateMachine) { setCurrentState((AbstractStateMachine<S, E>)stateMachine, state); } else { throw new IllegalArgumentException("Provided StateMachine is not a valid type"); } } public static <S, E> void setCurrentState(AbstractStateMachine<S, E> stateMachine, S state) { stateMachine.setCurrentState(findState(stateMachine, state), null, null, false, stateMachine); } private static <S, E> State<S, E> findState(AbstractStateMachine<S, E> stateMachine, S stateId) { for (State<S, E> state : stateMachine.getStates()) { if (state.getId() == stateId) { return state; } } throw new IllegalArgumentException("Specified State ID is not valid"); } } 

This can then be used pretty well, for example:

 MyStateMachineUtils.setCurrentState(entity.getStateMachine(), entity.getInternalState()); 
+2
source

All Articles