/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.utils.incubator; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedExecutionQueue; /** * An abstract base class to simplify the creation of state machine implementations. * * @param <S> the Enum defining the states of this state machine * @param <E> the type representing events posted to this state maching * @author Robert Mischke */ public abstract class AbstractStateMachine<S extends Enum<?>, E> { private S currentState; private AsyncOrderedExecutionQueue eventQueue; public AbstractStateMachine(S initialState) { this.currentState = initialState; this.eventQueue = ConcurrencyUtils.getFactory().createAsyncOrderedExecutionQueue(AsyncCallbackExceptionPolicy.LOG_AND_PROCEED); } /** * Asynchronously post an event to the state machine. Events are processed in order, and each event may or may not change the state * machine's state. * * @param event the event to post, which the state machine's logic will process asynchronously */ public void postEvent(final E event) { eventQueue.enqueue(new Runnable() { @Override public void run() { processEventInternal(event); } }); } /** * Triggers a state transition. Setting the new state is always a blocking operation. Whether the actions caused by the state change are * blocking or asynchronous is left to the subclasses' implementations. * * @param newState the state this state machine should transition to * @throws StateChangeException if the state change failed; the internal state will be unchanged */ private synchronized void processEventInternal(E event) { try { S newState = processEvent(currentState, event); if (newState == null || newState == currentState) { // no state change requested; done return; } try { checkProposedStateChange(currentState, newState); } catch (StateChangeException e) { // re-wrap to add old and new state information throw new StateChangeException(e, currentState, newState); } S oldState = currentState; currentState = newState; // internal state change handler first... onStateChanged(oldState, newState); } catch (StateChangeException e) { onStateChangeException(event, e); } } public synchronized S getState() { return currentState; } protected abstract S processEvent(S oldState, E event) throws StateChangeException; protected void checkProposedStateChange(S oldState, S newState) throws StateChangeException {} protected void onStateChanged(S oldState, S newState) {} protected abstract void onStateChangeException(E event, StateChangeException e); }