Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.scxml.env; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import javax.xml.stream.XMLStreamException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.scxml.Context; import org.apache.commons.scxml.Evaluator; import org.apache.commons.scxml.SCXMLExecutor; import org.apache.commons.scxml.SCXMLListener; import org.apache.commons.scxml.TriggerEvent; import org.apache.commons.scxml.env.jexl.JexlContext; import org.apache.commons.scxml.env.jexl.JexlEvaluator; import org.apache.commons.scxml.io.SCXMLReader; import org.apache.commons.scxml.model.ModelException; import org.apache.commons.scxml.model.SCXML; import org.apache.commons.scxml.model.Transition; import org.apache.commons.scxml.model.TransitionTarget; /** * <p> * This class demonstrates one approach for providing the base functionality * needed by classes representing stateful entities, whose behaviors are defined * via SCXML documents. * </p> * * <p> * SCXML documents (more generically, UML state chart diagrams) can be used to * define stateful behavior of objects, and Commons SCXML enables developers to * use this model directly into the corresponding code artifacts. The resulting * artifacts tend to be much simpler, embody a useful separation of concerns and * are easier to understand and maintain. As the size of the modeled entity * grows, these benefits become more apparent. * </p> * * <p> * This approach functions by registering an SCXMLListener that gets notified * onentry, and calls the namesake method for each state that has been entered. * </p> * * <p> * This class swallows all exceptions only to log them. Developers of subclasses * should think of themselves as "component developers" catering to * other end users, and therefore ensure that the subclasses are free of * <code>ModelException</code>s and the like. Most methods are * <code>protected</code> for ease of subclassing. * </p> * */ public abstract class AbstractStateMachine { /** * The state machine that will drive the instances of this class. */ private SCXML stateMachine; /** * The instance specific SCXML engine. */ private SCXMLExecutor engine; /** * The log. */ private Log log; /** * The method signature for the activities corresponding to each state in * the SCXML document. */ private static final Class<?>[] SIGNATURE = new Class[0]; /** * The method parameters for the activities corresponding to each state in * the SCXML document. */ private static final Object[] PARAMETERS = new Object[0]; /** * Convenience constructor, object instantiation incurs parsing cost. * * @param scxmlDocument * The URL pointing to the SCXML document that describes the * "lifecycle" of the instances of this class. */ public AbstractStateMachine(final URL scxmlDocument) { // default is JEXL this(scxmlDocument, new JexlContext(), new JexlEvaluator()); } /** * Primary constructor, object instantiation incurs parsing cost. * * @param scxmlDocument * The URL pointing to the SCXML document that describes the * "lifecycle" of the instances of this class. * @param rootCtx * The root context for this instance. * @param evaluator * The expression evaluator for this instance. * * @see Context * @see Evaluator */ public AbstractStateMachine(final URL scxmlDocument, final Context rootCtx, final Evaluator evaluator) { log = LogFactory.getLog(this.getClass()); try { stateMachine = SCXMLReader.read(scxmlDocument); } catch (IOException ioe) { logError(ioe); } catch (XMLStreamException xse) { logError(xse); } catch (ModelException me) { logError(me); } initialize(stateMachine, rootCtx, evaluator); } /** * Convenience constructor. * * @param stateMachine * The parsed SCXML instance that describes the * "lifecycle" of the instances of this class. * * @since 0.7 */ public AbstractStateMachine(final SCXML stateMachine) { // default is JEXL this(stateMachine, new JexlContext(), new JexlEvaluator()); } /** * Primary constructor. * * @param stateMachine * The parsed SCXML instance that describes the * "lifecycle" of the instances of this class. * @param rootCtx * The root context for this instance. * @param evaluator * The expression evaluator for this instance. * * @see Context * @see Evaluator * * @since 0.7 */ public AbstractStateMachine(final SCXML stateMachine, final Context rootCtx, final Evaluator evaluator) { log = LogFactory.getLog(this.getClass()); initialize(stateMachine, rootCtx, evaluator); } /** * Instantiate and initialize the underlying executor instance. * * @param stateMachine * The state machine * @param rootCtx * The root context * @param evaluator * The expression evaluator */ private void initialize(final SCXML stateMachine, final Context rootCtx, final Evaluator evaluator) { engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(), new SimpleErrorReporter()); engine.setStateMachine(stateMachine); engine.setSuperStep(true); engine.setRootContext(rootCtx); engine.addListener(stateMachine, new EntryListener()); try { engine.go(); } catch (ModelException me) { logError(me); } } /** * Fire an event on the SCXML engine. * * @param event * The event name. * @return Whether the state machine has reached a "final" * configuration. */ public boolean fireEvent(final String event) { TriggerEvent[] evts = { new TriggerEvent(event, TriggerEvent.SIGNAL_EVENT) }; try { engine.triggerEvents(evts); } catch (ModelException me) { logError(me); } return engine.getCurrentStatus().isFinal(); } /** * Get the SCXML engine driving the "lifecycle" of the instances * of this class. * * @return Returns the engine. */ public SCXMLExecutor getEngine() { return engine; } /** * Get the log for this class. * * @return Returns the log. */ public Log getLog() { return log; } /** * Set the log for this class. * * @param log * The log to set. */ public void setLog(final Log log) { this.log = log; } /** * Invoke the no argument method with the following name. * * @param methodName * The method to invoke. * @return Whether the invoke was successful. */ public boolean invoke(final String methodName) { Class<?> clas = this.getClass(); try { Method method = clas.getDeclaredMethod(methodName, SIGNATURE); method.invoke(this, PARAMETERS); } catch (SecurityException se) { logError(se); return false; } catch (NoSuchMethodException nsme) { logError(nsme); return false; } catch (IllegalArgumentException iae) { logError(iae); return false; } catch (IllegalAccessException iae) { logError(iae); return false; } catch (InvocationTargetException ite) { logError(ite); return false; } return true; } /** * Reset the state machine. * * @return Whether the reset was successful. */ public boolean resetMachine() { try { engine.reset(); } catch (ModelException me) { logError(me); return false; } return true; } /** * Utility method for logging error. * * @param exception * The exception leading to this error condition. */ protected void logError(final Exception exception) { if (log.isErrorEnabled()) { log.error(exception.getMessage(), exception); } } /** * A SCXMLListener that is only concerned about "onentry" * notifications. */ protected class EntryListener implements SCXMLListener { /** * {@inheritDoc} */ @Override public void onEntry(final TransitionTarget entered) { invoke(entered.getId()); } /** * No-op. * * @param from * The "source" transition target. * @param to * The "destination" transition target. * @param transition * The transition being followed. */ @Override public void onTransition(final TransitionTarget from, final TransitionTarget to, final Transition transition) { // nothing to do } /** * No-op. * * @param exited * The transition target being exited. */ @Override public void onExit(final TransitionTarget exited) { // nothing to do } @Override public void onStable() { // nothing to do } } }