State Machine doesn't get restored using the stateMachineContext
I followed the datapersist sample, using Redis to persist a state machine.
The issue is that, when I acquire a state machine that has been persisted before, the state machine gets created with a new context, This happens when I restart application (the java app), otherwise it restores the state machine correctly as it is using the map in DefaultStateMachineService:
Map<String, StateMachine<S, E>> machines = new HashMap<String, StateMachine<S, E>>()
I have debugged the DefaultStateMachineService's public StateMachine<S, E> acquireStateMachine(String machineId, boolean start) method:
...
if (stateMachinePersist != null) {
try {
StateMachineContext<S, E> stateMachineContext = stateMachinePersist.read(machineId);
stateMachine = restoreStateMachine(stateMachine, tateMachineContext);
} catch (Exception e) {
log.error("Error handling context", e);
throw new StateMachineException("Unable to read context from store", e);
}
}
...
So, stateMachinePersist.read(machineId) reads the context from Redis using the machineId, there is no problem with that as I debugged and the context is read correctly, BUT the restoreStateMachine(stateMachine, tateMachineContext) does NOT use that context, and as result I get a new state machine with a new context.
Um, I don't quite follow. What do you mean it does not use that context?
I tried this by:
$ docker run --name test-redis -d -p 6379:6379 redis
$ java -jar spring-statemachine-samples/datapersist/build/libs/spring-statemachine-samples-datapersist-2.1.0.BUILD-SNAPSHOT.jar --spring.profiles.active=redis
If I put those machines to different states and then restart sample, machines are restored.
I mean that the new state machine that I am getting comes with a new context, current state, the extended state variables all the stuff is lost..
The AbstractStateMachine' resetStateMachine() method doesn't make use of the context, I debugged and as I found that it relies on the boolean stateSet flag which is not set to true.. and as result the following block doesn't get executed :
if (stateSet && stateMachineContext.getExtendedState() != null) { this.extendedState.getVariables().clear(); this.extendedState.getVariables().putAll(stateMachineContext.getExtendedState().getVariables()); }
I am basing my code/config on the datatpersist sample, in my case, I add variables to the extended state of the machine, in the actions and guards, well all is working. except when I do a restart. the next time it restores the state machine, the variables are not there.. because of that stateSet flag in the AbstractStateMachine's resetStateMachine method which is always false in my case.
If that flag is false then it feels like a bug. Can you roughly show a structure of your machine as that might show where things go wrong. I'm also starting to question myself what I actually did in that point in code where extended state is reset.
I debugged the AbstractStateMachine's resetStateMachine method, I found the reason why the flag is never set to true, inside the iterations over states, the following condition is always false:
state instanceof Enum && ss.getId() instanceof Enum && state.getClass() == ss.getId().getClass() && ((Enum) ss.getId()).ordinal() == ((Enum) state).ordinal()
Because of state.getClass() == ss.getId().getClass() is never true, as the class loaders are different, due to the fact that I have spring-devtools in my dependencies which make use of a different classloader.
It wasn't easy to find out why the restore is not working, I think it would be great to do some logging and tell the developer about that...
Ah devtools, plot thickens. Not a first time it's causing issues. Maybe we could change that comparison to not use == as it's kinda stupid way to do it.
Yes that's true.. anyway Thank you! So I solved the issue by excluding the spring-devtools dependency, but I came across few issues like having a state machine with (s1, s2), it was stored and the current state of the machine is s1, the machine is configured in such a way that there is a transition from s1 to s2 without any event, this transition doesn't take place after restoring the machine, and as a result the machine is broken and stuck in the state s1, another issue is restoring the machine and the current state has an Action, the action will not take place as well... any advises/ideas/possible workarounds to solve the both issues I will be very grateful!
I need to create a test for that as reset stops/starts a machine and start should fire up execution which in theory should pick that anonymous transition.
I debugged the AbstractStateMachine's resetStateMachine method, I found the reason why the flag is never set to true, inside the iterations over states, the following condition is always false:
state instanceof Enum && ss.getId() instanceof Enum && state.getClass() == ss.getId().getClass() && ((Enum) ss.getId()).ordinal() == ((Enum) state).ordinal()Because of
state.getClass() == ss.getId().getClass()is never true, as the class loaders are different, due to the fact that I have spring-devtools in my dependencies which make use of a different classloader. It wasn't easy to find out why the restore is not working, I think it would be great to do some logging and tell the developer about that...
Thank you! I had the same fucking issue here!
Removing devtools the StateMachine works fine.
hey everyone! We have faced the same problem and this is really crap. it is defenitely should be issue for spring