-
Notifications
You must be signed in to change notification settings - Fork 3
Flow
Nikolaos Pougounias edited this page Jul 29, 2014
·
1 revision
To give a better understanding of how the FSM Engine works, a step-by-step workflow is provided below:
- Everything starts within an implementation class of the FiniteStateMachine interface as described earlier. A client (e.g. a piece of code in your application) calls the FiniteStateMachine#processEvent(event, stateContext) method passing as parameters a certain Event and an instance of the client application class which implements the StateContext interface and whose states are managed by the FiniteStateMachine.
- The exact implementation of the FiniteStateMachine#processEvent() method might depend on the client application, but the behavior provided by the default implementation class that comes along with FSM Engine is as follows:
- Check whether the triggering Event is valid delegating to FiniteStateMachine#isValidEvent(). The default implementation of this method actually only checks whether the given Event is included in the list of specified Events for the State that the StateContext implementation class is currently in (by consulting the FiniteStateMachine#getValidEvents() method), but the client application could easily override this method in order to add other checks such as whether the user actually is authorised to trigger a given Event etc.
- If the triggering Event is indeed valid, find the equivalent State object that corresponds to the current StateContext#getStateName() and delegate the processing of the Event to the State object by calling the State#processEvent(event, stateContext) method, since the state-specific behavior is directly defined in the appropriate State implementation class (as per the State Design Pattern). If the Event is not valid, stop any further execution and inform the client accordingly throwing a proper runtime exception.
- The control is now transferred to an implementation class of the State interface which represents a specific state of the state machine as explained earlier. Each one of the different State instances is configured with a populated map property where Events are stored as keys and StateChangers (i.e. Transitions or Branches) as values. The default implementation of State#processEvent(event, stateContext) finds the StateChanger instance that corresponds to the passed Event and delegates further execution to a Transition (or Branch) instance by calling the StateChanger#execute(stateContext) method. If the StateChanger found happens to be a Branch instance, this Branch instance iterates over the list of possible Transitions that it holds and delegates execution to each one of them. However, as explained earlier, only one Transition instance actually proceeds further and this will depend on the configured GuardCondition for each Transition (i.e. only one of those Transitions should evaluate to true). The entire process remains transparent to the client code due to polymorphism magic.
- The control is now transferred to a Transition instance. The implementation of the DefaultTransition#execute() class/method provided along with the FSM Engine is the following:
- Check if there’s a GuardCondition implementation configured for this Transition. If so, evaluate the condition and if the result is true proceed to the next step (by delegating to DefaultTransition#proceed()) otherwise do nothing (just log a proper message).
- If the Transition is allowed to be executed after the GuardCondition has been evaluated, the DefaultTransition#proceed(stateContext) method changes the State (or the StateName to be precise) of the passed StateContext instance according to what has been configured for this specific Transition instance upon initialisation, i.e. according to the Transition#getToState() value. Note that it is also possible to configure internal Transitions, i.e. Transitions that do not lead to a change of State, by leaving the Transition#toState property null. This might be useful in cases for example where only some Transition Actions need to be performed (see below).
- Once the state transition (or the internal transition) has been executed, the configured Actions (if any) are executed as well by delegation to the DefaultTransition#executeActions() method. This method iterates over all the configured Action instances for a given Transition instance and executes them one by one by calling the Action#execute(stateContext) method. Note that this operation is atomic, meaning that if one of the Actions throws an exception for some reason while being executed, all of the previous Actions that might have been executed up to this point will be rolled back, provided that the Action#rollback() method has been properly overridden by the implementing class.
- Normally, at this point the state machine execution is completed and the control is handed over back to the client code, unless one or more of the Transition Actions trigger new Events, thus leading to nested state machine executions in which case the workflow starts again in step 1 above.
The sequence diagram below visually depicts the FSM Engine flow described so far:
