/*******************************************************************************
* 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.nc.sm.generic;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.scxml.ErrorReporter;
import org.apache.commons.scxml.EventDispatcher;
import org.apache.commons.scxml.SCInstance;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.apache.commons.scxml.model.Action;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.semantics.ErrorConstants;
/**
* Action implementation class to be instantiated by the SCXML framework.
* Forwards the execute call to the user-supplied dispatcher,
* from where it goes to user-instantiated action impl objects.
* <p>
* The idea is to overcome Apache SCXML's limitation that action handlers
* cannot be injected, but instead get created by the framwork.
* This allows flexible implementation of one or many actions in one class,
* the use of normal JDK/ACS loggers in the action code,
* and flexibility in how the action objects can delegate among themselves
* and to other objects.
*
* @author hsommer
*/
public class AcsScxmlDispatchingAction<E extends Enum<E>> extends Action
{
/** Serial version UID. */
private static final long serialVersionUID = 1L;
/**
* Set by the framework right after construction, via {@link #setName(String)}.
* This mechanism is based on beanutils, working with SCXML elements such as
* <customActionDomain:createEnvironment name="createEnvironment"/>
* that have a name attribute.
*/
private String actionName;
/**
* The enum version of {@link #actionName}.
* Only available after the first call to {@link #getActionDispatcher(SCInstance)}
* because we need the concrete enum class for the String-enum conversion,
* which is only available through the user-supplied dispatcher.
*/
private E action;
/**
* Cache, see {@link #getActionDispatcher(SCInstance)}.
*/
private volatile AcsScxmlActionDispatcher<E> disp;
/**
* Called by the SCXML framework
*/
public AcsScxmlDispatchingAction() {
}
/**
* Gets the action name.
*
* @return Returns the name.
*/
public String getName() {
return actionName;
}
/**
* Sets the name. Stores it in {@link #actionName}.
*/
public void setName(String name) {
actionName = name;
}
// @SuppressWarnings("rawtypes")
public void execute(final EventDispatcher evtDispatcher, final ErrorReporter errRep, final SCInstance scInstance,
final Log appLog, final Collection derivedEvents) throws ModelException, SCXMLExpressionException {
try {
getActionDispatcher(scInstance);
} catch (Exception ex) {
errRep.onError(ErrorConstants.UNDEFINED_VARIABLE, "Failed to get UserActionDispatcher object from root context.", ex);
}
if (disp != null && action != null) {
// forward the action call to the dispatcher
disp.execute(action, evtDispatcher, errRep, scInstance, derivedEvents);
}
else {
errRep.onError(ErrorConstants.UNDEFINED_VARIABLE, "Failed to get UserActionDispatcher object from root context.", null);
}
}
/**
* Retrieves the user-supplied action dispatcher from the sm root context and
* caches it in {@link #disp}.
* @param scInstance
*/
private void getActionDispatcher(SCInstance scInstance) {
if (disp == null) {
synchronized (this) {
if (disp == null) {
disp = (AcsScxmlActionDispatcher<E>) scInstance.getRootContext().get(AcsScxmlActionDispatcher.class.getName());
// cache the action enum
action = Enum.valueOf(disp.getActionType(), this.actionName);
}
}
}
}
}