All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atomikos.finitestates.FSMImp Maven / Gradle / Ivy

/**
 * Copyright (C) 2000-2020 Atomikos 
 *
 * LICENSE CONDITIONS
 *
 * See http://www.atomikos.com/Main/WhichLicenseApplies for details.
 */

package com.atomikos.finitestates;

import java.util.EventListener;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import com.atomikos.recovery.TxState;


/**
 *
 *
 * Implementation of a finite state machine. The following
 * consistency is provided:
 * 
    *
  • getState returns a snapshot (state may change continuously)
  • *
  • FSMPreEnterListeners have the guarantee that getState() returns the same * value each time during the preEnter notification.
  • *
  • FSMEnterListeners have no guarantee that the getState() method will * return the state that was entered - this state may have changed since.
  • *
* */ public class FSMImp implements FSM { private TxState state_ = null; //the current state private Hashtable> enterlisteners_ = null; //the enter listeners private Hashtable> preenterlisteners_ = null; //pre enter listeners private Hashtable>> transitionlisteners_ = null; //transition listeners private Hashtable>> pretransitionlisteners_ = null; //pretransition listeners private Object eventsource_ = null; private Object stateLatch_; /** *Constructor. * *@param transitiontable The transitiontable with valid *transitions. * *@param initialstate The initial state of the FSM. */ public FSMImp ( TxState initialstate ) { this ( null, initialstate ); eventsource_ = this; } /** *Creates a new instance with a given event source. *Useful for cases where finite state machine behaviour is modelled *by delegation to an instance of this class. * *@param eventsource The object to be used as source of events. *@param transitiontable The transitiontable for state changes. *@param initialstate The initial state of the FSM. */ public FSMImp ( Object eventsource, TxState initialstate ) { state_ = initialstate; enterlisteners_ = new Hashtable>(); preenterlisteners_ = new Hashtable>(); transitionlisteners_ = new Hashtable>>(); pretransitionlisteners_ = new Hashtable>>(); eventsource_ = eventsource; stateLatch_ = new Object(); } /** *Help function for adding enter listeners. * *@param listeners One of the listener tables. *@param lstnr The listener to add. *@param state The state for which the listener wants to be notified. */ protected synchronized void addEnterListener(Hashtable> listeners, EventListener lstnr, TxState state) { Set lstnrs = listeners.get(state); if ( lstnrs == null ) lstnrs = new HashSet(); if ( !lstnrs.contains(lstnr) ) lstnrs.add( lstnr ); listeners.put( state , lstnrs ); } /** *Help function for adding transition listeners. * *@param listeners One of the transition listener tables. *@param lstnr The listener to add. *@param from The start state of the transition. *@param to The end state of the transition. */ protected synchronized void addTransitionListener(Hashtable>> listeners, EventListener lstnr, TxState from, TxState to) { Hashtable> lstnrs = listeners.get(from); if (lstnrs == null) lstnrs = new Hashtable>(); Set tolstnrs = lstnrs.get(to); if (tolstnrs == null) tolstnrs = new HashSet(); if (!tolstnrs.contains(lstnr)) tolstnrs.add(lstnr); lstnrs.put(to,tolstnrs); listeners.put(from,lstnrs); } /** *Notify the enter listeners. * *@param listeners One of the enter listener tables. *@param state The state about to enter (or entered). *@param pre True iff before entering. */ protected void notifyListeners(Hashtable> listeners, TxState state, boolean pre) { Set lstnrs = null; FSMEnterEvent event = new FSMEnterEvent (eventsource_, state); synchronized ( this ) { lstnrs= listeners.get ( state ); if ( lstnrs == null ) return; //clone to avoid concurrency effects outside synch block //during iteration hereafter lstnrs = new HashSet(lstnrs); } //notify OUTSIDE SYNCH to minimize deadlocks for (EventListener listener : lstnrs) { if ( pre && ( listener instanceof FSMPreEnterListener )) ((FSMPreEnterListener) listener).preEnter (event); else if (!pre && (listener instanceof FSMEnterListener)) ((FSMEnterListener) listener).entered ( event ); } } /** *Notify transition listeners. * *@param listeners One of the transition listener tables. *@param from The initial state. *@param to The end state. *@param pre True iff before transition. */ protected void notifyListeners ( Hashtable>> listeners, TxState from , TxState to , boolean pre ) { FSMTransitionEvent event = new FSMTransitionEvent (eventsource_, from, to ); Hashtable> lstnrs = null; Set tolstnrs = null; synchronized ( this ) { lstnrs = listeners.get( from ); if ( lstnrs == null ) return; tolstnrs = lstnrs.get( to ); if ( tolstnrs == null ) return; //clone to avoid concurrency effects //during iteration outside synch block lstnrs = new Hashtable> (lstnrs); tolstnrs = new HashSet(tolstnrs); } //iterator outside synch to avoid deadlocks for (EventListener listener : tolstnrs) { if ( pre && ( listener instanceof FSMPreTransitionListener )) { ((FSMPreTransitionListener)listener).beforeTransition(event); } else if (!pre && (listener instanceof FSMTransitionListener)) { ((FSMTransitionListener) listener).transitionPerformed(event); } } } /** *@see com.atomikos.finitestates.FSM */ public TxState getState() { //Note: this method should NOT be synchronized on the FSM itself, to avoid deadlocks //in re-entrant 2PC calls! TxState ret = null; //don't synch on FSM -> use latch object instead synchronized ( stateLatch_ ) { ret = state_; } return ret; } private void setStateObject ( TxState state ) { //synchronize on stateLatch ONLY to make sure that getState //returns the latest (non-cached) value synchronized ( stateLatch_ ) { this.state_ = state; } } /** *@see com.atomikos.finitestates.StateMutable */ public void setState(TxState state) throws IllegalStateException { TxState oldstate = null; synchronized ( this ) { if (!state_.transitionAllowedTo(state)) throw new IllegalStateException("Transition not allowed: "+state_ +" to "+state); oldstate = state_; notifyListeners(preenterlisteners_ , state , true); notifyListeners(pretransitionlisteners_ , oldstate , state , true); setStateObject ( state ); } //ENTER EVENTS ARE OUTSIDE SYNCH BLOCK TO MINIMIZE DEADLOCKS!!! notifyListeners(enterlisteners_ , state , false); notifyListeners(transitionlisteners_ , oldstate, state, false); } /** *@see com.atomikos.finitestates.FSMEnterEventSource */ public void addFSMEnterListener(FSMEnterListener lstnr, TxState state) { addEnterListener(enterlisteners_ , lstnr , state); } /** *@see com.atomikos.finitestates.FSMPreEnterEventSource */ public void addFSMPreEnterListener(FSMPreEnterListener lstnr, TxState state) { addEnterListener(preenterlisteners_ , lstnr , state); } /** *@see com.atomikos.finitestates.FSMTransitionEventSource */ public void addFSMTransitionListener(FSMTransitionListener lstnr, TxState from, TxState to) { addTransitionListener ( transitionlisteners_ , lstnr , from , to ); } /** *@see com.atomikos.finitestates.FSMPreTransitionEventSource */ public void addFSMPreTransitionListener(FSMPreTransitionListener lstnr, TxState from, TxState to) { addTransitionListener( pretransitionlisteners_ , lstnr , from , to ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy