Reputation: 731
I working with Spring State Machines. I followed the documentation and other stuff. I need to keep different state machines for each employee. But when called factory to get state machine each time returns a state machine whose state is initial.
public enum EmployeeEvent {
CREATED, CHECKSTARTED, APPROVE, ACTIVATED
}
public enum EmployeeState {
ADDED, INCHECK, APPROVED, ACTIVE
}
States are events like above. Configuration is below
@Configuration
@EnableStateMachineFactory
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<EmployeeState, EmployeeEvent> {
@Override
public void configure(StateMachineConfigurationConfigurer<EmployeeState, EmployeeEvent> config)
throws Exception {
config
.withConfiguration()
.autoStartup(true)
.listener(listener());
}
@Override
public void configure(StateMachineStateConfigurer<EmployeeState, EmployeeEvent> states)
throws Exception {
states
.withStates()
.initial(EmployeeState.ADDED)
.states(EnumSet.allOf(EmployeeState.class))
.end(EmployeeState.ACTIVE);
}
@Override
public void configure(StateMachineTransitionConfigurer<EmployeeState, EmployeeEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(EmployeeState.ADDED).target(EmployeeState.INCHECK).event(EmployeeEvent.CHECKSTARTED)
.and()
.withExternal()
.source(EmployeeState.INCHECK).target(EmployeeState.APPROVED).event(EmployeeEvent.APPROVE)
.and()
.withExternal()
.source(EmployeeState.APPROVED).target(EmployeeState.ACTIVE).event(EmployeeEvent.ACTIVATED);
}
@Bean
public StateMachineListener<EmployeeState, EmployeeEvent> listener() {
return new StateMachineListenerAdapter<EmployeeState, EmployeeEvent>() {
@Override
public void stateChanged(State<EmployeeState, EmployeeEvent> from, State<EmployeeState, EmployeeEvent> to) {
System.out.println("State change to " + to.getId());
}
};
}
}
factory build code like below
private StateMachine<EmployeeState, EmployeeEvent> build(Long employeeId){
Optional<Employee> byId = employeeRepository.findById(employeeId);
Employee employee = byId.get();
StateMachine<EmployeeState, EmployeeEvent> stateMachine = stateMachineFactory.getStateMachine(Long.toString(employee.getId()));
stateMachine.stop();
stateMachine.getStateMachineAccessor().doWithAllRegions(sma -> {
sma.addStateMachineInterceptor(new StateMachineInterceptorAdapter<>() {
@Override
public void preStateChange(State<EmployeeState, EmployeeEvent> state, Message<EmployeeEvent> message, Transition<EmployeeState, EmployeeEvent> transition, StateMachine<EmployeeState, EmployeeEvent> stateMachine, StateMachine<EmployeeState, EmployeeEvent> stateMachine1) {
Optional.ofNullable(message).ifPresent(msg -> {
Long employeeId = Long.class.cast(msg.getHeaders().getOrDefault("EMPLOYEE_ID", -1));
Employee employee = employeeRepository.getById(employeeId);
employee.setState(state.getId());
employeeRepository.save(employee);
});
}
});
sma.resetStateMachine(new DefaultStateMachineContext<>(EmployeeState.valueOf(employee.getState().name()), null, null, null));
});
stateMachine.start();
return stateMachine;
}
But I recognized that when I called build multiple times, it returns a state machine whose state is initial. Then I wrote tests like below.
@Test
public void sameUuid(){
UUID uuid = UUID.randomUUID();
StateMachine<EmployeeState, EmployeeEvent> stateMachine = factory.getStateMachine(uuid);
stateMachine.sendEvent(EmployeeEvent.CHECKSTARTED);
StateMachine<EmployeeState, EmployeeEvent> stateMachine2 = factory.getStateMachine(uuid);
assertEquals(stateMachine.getUuid(), stateMachine2.getUuid());
assertEquals(stateMachine.getState().getId(), stateMachine2.getState().getId());
}
Uuids are the same but states are different.`
@Test
public void sameId(){
StateMachine<EmployeeState, EmployeeEvent> stateMachine = factory.getStateMachine("1");
stateMachine.sendEvent(EmployeeEvent.CHECKSTARTED);
StateMachine<EmployeeState, EmployeeEvent> stateMachine2 = factory.getStateMachine("1");
assertEquals(stateMachine.getId(), stateMachine2.getId());
assertEquals(stateMachine.getUuid(), stateMachine2.getUuid());
assertEquals(stateMachine.getState().getId(), stateMachine2.getState().getId());
}
Ids are the same but UUIDs are different.
I need one state machine for my one employee object. How can be solved?
Upvotes: 0
Views: 1672
Reputation: 169
You need to change the way you retrieving state machines.
The @EnableStateMachineFactory
annotation is used properly, but to store and restore the state machine better to use SpringStateMachineService that checks if there is SM with the given ID and restore it, or creates a new one.
See the detailed answer on how to persist state machine and how to restore them from storage here
Upvotes: 0