transaction of Spring state machine and configuration of guard

Keywords: Programming Spring

Transactions in a spring state machine refer to the transition of a state machine from one state to another through an event. The simplest spring state machine includes configuration of States and transactions. Spring state machines support different transactions, including external, internal and local. Transactions can be triggered by events or timers. Spring state machine transactions are configured by the method configure (statemachinetransitionconfigurer < States, events > transitions), as shown in the following code example:

public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
		transitions.withExternal()
		           .source(States.S1).target(States.S2).event(Events.E1)
		           .and().withInternal().source(States.S2).event(Events.E2)
		           .and().withLocal().source(States.S2).target(States.S3).event(Events.E3);
	}

When configuring transaction, we can configure guard function. Guard is used to protect state machine state. It can implement guard interface to control whether StateContext can be accessed, that is, whether the transaction is executed. Guard is configured by calling the guard() or guardExpression() method. When the evaluate method of the interface guard returns true or the expression of guardExpression() is true, the transaction is executed for state transition. The code is as follows:

@Configuration
@EnableStateMachine
public class GuardConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
	@Override
	public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
		states.withStates().initial(States.S1).states(EnumSet.allOf(States.class));
	}

	@Override
	public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
		transitions.withExternal().source(States.S1).target(States.S2).event(Events.E1).guard(guard1()).and()
				.withExternal().source(States.S2).target(States.S3).event(Events.E2).guardExpression("true").and()
				.withExternal().source(States.S3).target(States.S1).event(Events.E3).guard(guard2());
	}

	@Bean
	public Guard<States, Events> guard1() {
		return new Guard<States, Events>() {

			@Override
			public boolean evaluate(StateContext<States, Events> context) {
				return true;
			}
		};
	}

	@Bean
	public Guard<States, Events> guard2() {
		return new Guard<States, Events>() {

			@Override
			public boolean evaluate(StateContext<States, Events> context) {
				return false;
			}
		};
	}
}

In the above code, S1 passes through E1 to S2. Because the method evaluate returns true, the status can be changed to S2. From S2 to S3, because the guardExpression("true") expression is true, the status can be changed to S3. From S3 through E3 to S1, since the method evaluate returns false, the transaction will not proceed and the status will not change.

The interface Guard and guardExpression can also implement the function of selecting state. As follows, we configure the initialization status as S1 and S2 as the selection status. When guard1() is met, change the status bit S3, and when guard2() is met, change the status as S4, otherwise change the status as S5.

@Configuration
@EnableStateMachine
public class ChoiceConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
	@Override
	public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
		states.withStates().initial(States.S1).choice(States.S2).states(EnumSet.allOf(States.class));
	}

	@Override
	public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
		transitions.withExternal().source(States.S1).target(States.S2).event(Events.E1).and().withChoice()
				.first(States.S3, guard1()).then(States.S4, guard2()).last(States.S5);
	}

	@Bean
	public Guard<States, Events> guard1() {
		return new Guard<States, Events>() {

			@Override
			public boolean evaluate(StateContext<States, Events> context) {
				return true;
			}
		};
	}

	@Bean
	public Guard<States, Events> guard2() {
		return new Guard<States, Events>() {

			@Override
			public boolean evaluate(StateContext<States, Events> context) {
				return false;
			}
		};
	}
}

The only regret is that the Spring state machine selector does not support event triggering, so if you want to use selection and want to use event triggering, you can only use the interface Guard for assistance, which is the only way I think of at present. If there are other methods, welcome to point out that the code is as follows:

@Override
	public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
		states.withStates().initial(States.S1).states(EnumSet.allOf(States.class));
	}

	@Override
	public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
		transitions.withExternal().source(States.S1).target(States.S2).event(Events.E1).guard(guard1()).and()
				.withExternal().source(States.S1).target(States.S3).event(Events.E1).guard(guard2()).and()
				.withExternal().source(States.S1).target(States.S4).event(Events.E1).guardExpression("extendedState.variables.get('myvar')");
	}

Posted by davidforbes on Tue, 22 Oct 2019 23:56:20 -0700