/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) ESO - European Southern Observatory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ package alma.ACS.MasterComponentImpl.statemachine; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import alma.ACSErrTypeCommon.wrappers.AcsJIllegalStateEventEx; import alma.acs.genfw.runtime.sm.AcsState; import alma.acs.genfw.runtime.sm.AcsStateActionException; import alma.acs.genfw.runtime.sm.AcsStateChangeListener; import alma.acs.logging.AcsLogger; /** * The super context class for the AlmaSubsystem state machine. */ public class AlmaSubsystemContext { private AlmaSubsystemStateAbstract m_currentState; private final AlmaSubsystemActions m_actionDelegate; private List<AcsStateChangeListener> m_stateChangeListeners; private AcsState[] m_oldHierarchy; private final AcsLogger m_logger; /** * Executor for do/ actions in activity states. * This executor only has one worker thread and therefore synchronizes * concurrent requests. * @TODO: Discuss if and how we want to escape from a hanging action method. * Both timeout and asynchronous actions pose ugly implementation issues for the subsystem MCs. */ private final ThreadPoolExecutor sharedActivityExecutor; private boolean m_verbose = false; // state objects // todo: make instances private and add get methods public AvailableState m_stateAvailable; public ErrorState m_stateError; public OnlineState m_stateOnline; public OperationalState m_stateOperational; public OfflineState m_stateOffline; public ShutdownState m_stateShutdown; public ShuttingdownPass1State m_stateShuttingdownPass1; public InitializingPass2State m_stateInitializingPass2; public InitializingPass1State m_stateInitializingPass1; public ReinitializingState m_stateReinitializing; public PreInitializedState m_statePreInitialized; public PreShutdownState m_statePreShutdown; public ShuttingdownPass2State m_stateShuttingdownPass2; public AlmaSubsystemContext(AlmaSubsystemActions actions, AcsLogger logger, ThreadFactory threadFactory) { m_actionDelegate = actions; m_stateChangeListeners = new ArrayList<AcsStateChangeListener>(); m_logger = logger; // This choice of parameters is copied from the implementation of // Executors.newSingleThreadExecutor which unfortunately hides // in its returned type some methods we need. sharedActivityExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); m_stateAvailable = new AvailableState(this); m_stateOperational = new OperationalState(this, m_stateAvailable); m_stateError = new ErrorState(this, m_stateAvailable); m_stateOnline = new OnlineState(this, m_stateAvailable); m_stateOffline = new OfflineState(this, m_stateAvailable); m_stateShutdown = new ShutdownState(this, m_stateOffline, m_logger); m_stateShuttingdownPass1 = new ShuttingdownPass1State(this, m_stateOffline, m_logger); m_stateInitializingPass2 = new InitializingPass2State(this, m_stateOffline, m_logger); m_stateInitializingPass1 = new InitializingPass1State(this, m_stateOffline, m_logger); m_stateReinitializing = new ReinitializingState(this, m_stateOffline, m_logger); m_statePreInitialized = new PreInitializedState(this, m_stateOffline, m_logger); m_statePreShutdown = new PreShutdownState(this, m_stateOffline, m_logger); m_stateShuttingdownPass2 = new ShuttingdownPass2State(this, m_stateOffline, m_logger); // initial state m_stateShutdown.activate("<init>"); m_oldHierarchy = getCurrentTopLevelState().getStateHierarchy(); } /** * Registers an object that will be notified about any state change in this state machine. * @param listener */ public synchronized void addAcsStateChangeListener(AcsStateChangeListener listener) { m_stateChangeListeners.add(listener); } void setState(AlmaSubsystemStateAbstract newState, String eventName) { AlmaSubsystemStateAbstract oldState = m_currentState; if (oldState != newState) { logTransition(m_currentState, newState, eventName); m_currentState = newState; m_currentState.entry(); } AcsState[] currentHierarchy = getCurrentTopLevelState().getStateHierarchy(); // check if there was a state change down the nesting hierarchy if (!Arrays.equals(currentHierarchy, m_oldHierarchy)) { // if so, notify listeners for (Iterator<AcsStateChangeListener> iter = m_stateChangeListeners.iterator(); iter.hasNext();) { AcsStateChangeListener listener = iter.next(); listener.stateChangedNotify(m_oldHierarchy, currentHierarchy); } m_oldHierarchy = currentHierarchy; } } public synchronized AlmaSubsystemStateAbstract getCurrentTopLevelState() { return m_currentState; } //====================================================================== // delegates incoming events to current state class //====================================================================== public synchronized void initPass1() throws AcsJIllegalStateEventEx { m_currentState.initPass1(); } public synchronized void initPass2() throws AcsJIllegalStateEventEx { m_currentState.initPass2(); } public synchronized void reinit() throws AcsJIllegalStateEventEx { m_currentState.reinit(); } public synchronized void start() throws AcsJIllegalStateEventEx { m_currentState.start(); } public synchronized void stop() throws AcsJIllegalStateEventEx { m_currentState.stop(); } public synchronized void shutdownPass1() throws AcsJIllegalStateEventEx { m_currentState.shutdownPass1(); } public synchronized void shutdownPass2() throws AcsJIllegalStateEventEx { m_currentState.shutdownPass2(); } public synchronized void error() throws AcsJIllegalStateEventEx { m_currentState.error(); } //====================================================================== // delegates actions to user-provided action handler // Note that actions of activity states are run in separate threads //====================================================================== public void initSubsysPass1() throws AcsStateActionException { try { m_actionDelegate.initSubsysPass1(); } catch (AcsStateActionException ex) { throw ex; } catch (Throwable thr) { throw new AcsStateActionException(thr); } } public void initSubsysPass2() throws AcsStateActionException { try { m_actionDelegate.initSubsysPass2(); } catch (AcsStateActionException ex) { throw ex; } catch (Throwable thr) { throw new AcsStateActionException(thr); } } public void reinitSubsystem() throws AcsStateActionException { try { m_actionDelegate.reinitSubsystem(); } catch (AcsStateActionException ex) { throw ex; } catch (Throwable thr) { throw new AcsStateActionException(thr); } } public void shutDownSubsysPass1() throws AcsStateActionException { try { m_actionDelegate.shutDownSubsysPass1(); } catch (AcsStateActionException ex) { throw ex; } catch (Throwable thr) { throw new AcsStateActionException(thr); } } public void shutDownSubsysPass2() throws AcsStateActionException { try { m_actionDelegate.shutDownSubsysPass2(); } catch (AcsStateActionException ex) { throw ex; } catch (Throwable thr) { throw new AcsStateActionException(thr); } } //====================================================================== // util methods //====================================================================== void illegalEvent(String stateName, String eventName) throws AcsJIllegalStateEventEx { String msg = "illegal event '" + eventName + "' in state '" + stateName + "'."; if (m_verbose) { m_logger.warning(msg); } // for (Iterator iter = m_stateChangeListeners.iterator(); iter.hasNext();) { // AcsStateChangeListener listener = (AcsStateChangeListener) iter.next(); // listener.illegalEventNotify(stateName, eventName); // } AcsJIllegalStateEventEx ex = new AcsJIllegalStateEventEx(); ex.setEvent(eventName); ex.setState(stateName); throw ex; } void logTransition(AcsState sourceState, AcsState targetState, String eventName) { if (m_verbose && sourceState != null) { // sourceState is null at initial state setting String msg = "event '" + eventName + "' causes transition from '" + sourceState.stateName() + "' to '" + targetState.stateName() + "'."; m_logger.info(msg); } } ThreadPoolExecutor getSharedActivityExecutor() { return sharedActivityExecutor; } /** * Final clean-up, must be called before unloading this class. * Afterwards it may be in an undefined state. */ public void cleanUp() { // wake up and terminate the activity worker thread. sharedActivityExecutor.shutdownNow(); } }