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?