Reputation: 45
Let's say we've got the following state machine configuration:
transitions.withExternal()
.source(FIRST)
.target(SECOND)
.event(STEP_EVENT)
.and()
.source(SECOND)
.target(EXIT)
.event(EXIT_EVENT)
Events list: STEP_EVENT, EXIT_EVENT, UNUSED_EVENT
stateMachine.init();
// FIRST state
stateMachine.sendEvent(STEP_EVENT);
/* state moves to SECOND
because there is a transition with current state as a source
and STEP_EVENT as transition event */
stateMachine.sendEvent(UNUSED_EVENT);
/* no state change.
This will trigger "eventNotAccepted(Message<Events> event)"
in state machine listener,
because UNUSED_EVENT is never mentioned in SM config */
stateMachine.sendEvent(STEP_EVENT);
/* nothing will happen!!!
No state change, as there is no transition
which has current state (SECOND) as source
and STEP_EVENT as transition event,
and no eventNotAccepted call.
But I need it, I want to fail here! */
stateMachine.sendEvent(EXIT_EVENT);
// state will move to EXIT
The issue is that when I sent an event which is part of configuration but is not applicable for current state, nothing happens.
I don't know whether state didn't change because of a guard or because there is no transition with current state and my event.
Is there any way to handle such cases?
Upvotes: 3
Views: 4457
Reputation: 31443
To log events which are not applicable for your current state you can use a StateMachine listener. There's a method called each time an event, which does not satisfy the defined transitions and events, is passed in the State Machine.
In the state machine Configuration you need to override:
public void configure(StateMachineConfigurationConfigurer<State, Event> config) {
config.withConfiguration()
.listener(customListener());
}
and implement your own Listener - the easiest way is to use the StateMachineListenerAdapter
and override the eventNotAccepted(Message event)
method:
private StateMachineListenerAdapter<State, Event> customListener() {
return new StateMachineEventListenerAdapter<State, Event>() {
@Override
public void eventNotAccepted(Message event) {
//LOG which event was not accepted etc.
}
}
}
For logging results of guards - use log messages in the guards themselves.
If you want to expose the reason outside of guards, you can construct a key-value pair and use the StateMachine's extended context to record the guard name and the reason why an event was rejected. The context can be used to construct a custom exception or communicate to the caller code what happened.
Upvotes: 4
Reputation: 45
Solved! As it often happens, solution was too simple for me to observe it :(. So the method which sends an event to SM has boolean as return parameter. And if event was handled and processed it returns true, and false otherwise.
That's it - Just check return value!
Upvotes: 1