Coverage Report - org.apache.commons.scxml.env.AbstractStateMachine

Classes in this File Line Coverage Branch Coverage Complexity
AbstractStateMachine
46% 
50% 
2.462

 1  
 /*
 2  
  *
 3  
  *   Copyright 2006 The Apache Software Foundation.
 4  
  *
 5  
  *  Licensed under the Apache License, Version 2.0 (the "License");
 6  
  *  you may not use this file except in compliance with the License.
 7  
  *  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  *  Unless required by applicable law or agreed to in writing, software
 12  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 13  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  *  See the License for the specific language governing permissions and
 15  
  *  limitations under the License.
 16  
  *
 17  
  */
 18  
 package org.apache.commons.scxml.env;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.lang.reflect.InvocationTargetException;
 22  
 import java.lang.reflect.Method;
 23  
 import java.net.URL;
 24  
 
 25  
 import org.apache.commons.logging.Log;
 26  
 import org.apache.commons.logging.LogFactory;
 27  
 import org.apache.commons.scxml.Context;
 28  
 import org.apache.commons.scxml.Evaluator;
 29  
 import org.apache.commons.scxml.SCXMLExecutor;
 30  
 import org.apache.commons.scxml.SCXMLListener;
 31  
 import org.apache.commons.scxml.TriggerEvent;
 32  
 import org.apache.commons.scxml.env.jexl.JexlContext;
 33  
 import org.apache.commons.scxml.env.jexl.JexlEvaluator;
 34  
 import org.apache.commons.scxml.io.SCXMLDigester;
 35  
 import org.apache.commons.scxml.model.ModelException;
 36  
 import org.apache.commons.scxml.model.SCXML;
 37  
 import org.apache.commons.scxml.model.Transition;
 38  
 import org.apache.commons.scxml.model.TransitionTarget;
 39  
 import org.xml.sax.ErrorHandler;
 40  
 import org.xml.sax.SAXException;
 41  
 
 42  
 /**
 43  
  * This class demonstrates one approach for providing the base
 44  
  * functionality needed by classes representing stateful entities,
 45  
  * whose behaviors are defined via SCXML documents.
 46  
  *
 47  
  * SCXML documents (more generically, UML state chart diagrams) can be
 48  
  * used to define stateful behavior of objects, and Commons SCXML enables
 49  
  * developers to use this model directly into the corresponding code
 50  
  * artifacts. The resulting artifacts tend to be much simpler, embody
 51  
  * a useful separation of concerns and are easier to understand and
 52  
  * maintain. As the size of the modeled entity grows, these benefits
 53  
  * become more apparent.
 54  
  *
 55  
  * This approach functions by registering an SCXMLListener that gets
 56  
  * notified onentry, and calls the namesake method for each state that
 57  
  * has been entered.
 58  
  *
 59  
  * This class swallows all exceptions only to log them. Developers of
 60  
  * subclasses should think of themselves as "component developers"
 61  
  * catering to other end users, and therefore ensure that the subclasses
 62  
  * are free of <code>ModelException</code>s and the like. Most methods
 63  
  * are <code>protected</code> for ease of subclassing.
 64  
  *
 65  
  */
 66  
 public abstract class AbstractStateMachine {
 67  
 
 68  
     /**
 69  
      * The state machine that will drive the instances of this class.
 70  
      */
 71  
     private static SCXML stateMachine;
 72  
 
 73  
     /**
 74  
      * The instance specific SCXML engine.
 75  
      */
 76  
     private SCXMLExecutor engine;
 77  
 
 78  
     /**
 79  
      * The log.
 80  
      */
 81  
     private Log log;
 82  
 
 83  
     /**
 84  
      * The method signature for the activities corresponding to each
 85  
      * state in the SCXML document.
 86  
      */
 87  1
     private static final Class[] SIGNATURE = new Class[0];
 88  
 
 89  
     /**
 90  
      * The method parameters for the activities corresponding to each
 91  
      * state in the SCXML document.
 92  
      */
 93  1
     private static final Object[] PARAMETERS = new Object[0];
 94  
 
 95  
     /**
 96  
      * Convenience constructor.
 97  
      *
 98  
      * @param scxmlDocument The URL pointing to the SCXML document that
 99  
      *                      describes the &quot;lifecycle&quot; of the
 100  
      *                      instances of this class.
 101  
      */
 102  
     public AbstractStateMachine(final URL scxmlDocument) {
 103  
         // default is JEXL
 104  1
         this(scxmlDocument, new JexlContext(), new JexlEvaluator());
 105  1
     }
 106  
 
 107  
     /**
 108  
      * Primary constructor.
 109  
      *
 110  
      * @param scxmlDocument The URL pointing to the SCXML document that
 111  
      *                      describes the &quot;lifecycle&quot; of the
 112  
      *                      instances of this class.
 113  
      * @param rootCtx The root context for this instance.
 114  
      * @param evaluator The expression evaluator for this instance.
 115  
      *
 116  
      * @see Context
 117  
      * @see Evaluator
 118  
      */
 119  
     public AbstractStateMachine(final URL scxmlDocument,
 120  1
             final Context rootCtx, final Evaluator evaluator) {
 121  1
         log = LogFactory.getLog(this.getClass());
 122  1
         if (stateMachine == null) {
 123  
             // parse only once per subclass
 124  1
             ErrorHandler errHandler = new SimpleErrorHandler();
 125  
             try {
 126  1
                 stateMachine = SCXMLDigester.digest(scxmlDocument,
 127  
                     errHandler);
 128  0
             } catch (IOException ioe) {
 129  0
                 logError(ioe);
 130  0
             } catch (SAXException sae) {
 131  0
                 logError(sae);
 132  0
             } catch (ModelException me) {
 133  0
                 logError(me);
 134  1
             }
 135  
         }
 136  1
         engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(),
 137  
             new SimpleErrorReporter());
 138  1
         engine.setStateMachine(stateMachine);
 139  1
         engine.setSuperStep(true);
 140  1
         engine.setRootContext(rootCtx);
 141  1
         engine.addListener(stateMachine, new EntryListener());
 142  
         try {
 143  1
             engine.go();
 144  0
         } catch (ModelException me) {
 145  0
             logError(me);
 146  1
         }
 147  1
     }
 148  
 
 149  
     /**
 150  
      * Fire an event on the SCXML engine.
 151  
      *
 152  
      * @param event The event name.
 153  
      * @return Whether the state machine has reached a &quot;final&quot;
 154  
      *         configuration.
 155  
      */
 156  
     public boolean fireEvent(final String event) {
 157  5
         TriggerEvent[] evts = {new TriggerEvent(event,
 158  
                 TriggerEvent.SIGNAL_EVENT, null)};
 159  
         try {
 160  5
             engine.triggerEvents(evts);
 161  0
         } catch (ModelException me) {
 162  0
             logError(me);
 163  5
         }
 164  5
         return engine.getCurrentStatus().isFinal();
 165  
     }
 166  
 
 167  
     /**
 168  
      * Get the SCXML object representing this state machine.
 169  
      *
 170  
      * @return Returns the stateMachine.
 171  
      */
 172  
     public static SCXML getStateMachine() {
 173  0
         return stateMachine;
 174  
     }
 175  
 
 176  
     /**
 177  
      * Get the SCXML engine driving the &quot;lifecycle&quot; of the
 178  
      * instances of this class.
 179  
      *
 180  
      * @return Returns the engine.
 181  
      */
 182  
     public SCXMLExecutor getEngine() {
 183  6
         return engine;
 184  
     }
 185  
 
 186  
     /**
 187  
      * Get the log for this class.
 188  
      *
 189  
      * @return Returns the log.
 190  
      */
 191  
     public Log getLog() {
 192  0
         return log;
 193  
     }
 194  
 
 195  
     /**
 196  
      * Set the log for this class.
 197  
      *
 198  
      * @param log The log to set.
 199  
      */
 200  
     public void setLog(final Log log) {
 201  0
         this.log = log;
 202  0
     }
 203  
 
 204  
     /**
 205  
      * Invoke the no argument method with the following name.
 206  
      *
 207  
      * @param methodName The method to invoke.
 208  
      * @return Whether the invoke was successful.
 209  
      */
 210  
     public boolean invoke(final String methodName) {
 211  6
         Class clas = this.getClass();
 212  
         try {
 213  6
             Method method = clas.getDeclaredMethod(methodName, SIGNATURE);
 214  6
             method.invoke(this, PARAMETERS);
 215  0
         } catch (SecurityException se) {
 216  0
             logError(se);
 217  0
             return false;
 218  0
         } catch (NoSuchMethodException nsme) {
 219  0
             logError(nsme);
 220  0
             return false;
 221  0
         } catch (IllegalArgumentException iae) {
 222  0
             logError(iae);
 223  0
             return false;
 224  0
         } catch (IllegalAccessException iae) {
 225  0
             logError(iae);
 226  0
             return false;
 227  0
         } catch (InvocationTargetException ite) {
 228  0
             logError(ite);
 229  0
             return false;
 230  6
         }
 231  6
         return true;
 232  
     }
 233  
 
 234  
     /**
 235  
      * Reset the state machine.
 236  
      *
 237  
      * @return Whether the reset was successful.
 238  
      */
 239  
     public boolean resetMachine() {
 240  
         try {
 241  0
             engine.reset();
 242  0
         } catch (ModelException me) {
 243  0
             logError(me);
 244  0
             return false;
 245  0
         }
 246  0
         return true;
 247  
     }
 248  
 
 249  
     /**
 250  
      * Utility method for logging error.
 251  
      *
 252  
      * @param exception The exception leading to this error condition.
 253  
      */
 254  
     protected void logError(final Exception exception) {
 255  0
         if (log.isErrorEnabled()) {
 256  0
             log.error(exception.getMessage(), exception);
 257  
         }
 258  0
     }
 259  
 
 260  
     /**
 261  
      * A SCXMLListener that is only concerned about &quot;onentry&quot;
 262  
      * notifications.
 263  
      */
 264  1
     protected class EntryListener implements SCXMLListener {
 265  
 
 266  
         /**
 267  
          * {@inheritDoc}
 268  
          */
 269  
         public void onEntry(final TransitionTarget entered) {
 270  6
             invoke(entered.getId());
 271  6
         }
 272  
 
 273  
         /**
 274  
          * No-op.
 275  
          *
 276  
          * @param from The &quot;source&quot; transition target.
 277  
          * @param to The &quot;destination&quot; transition target.
 278  
          * @param transition The transition being followed.
 279  
          */
 280  
         public void onTransition(final TransitionTarget from,
 281  
                 final TransitionTarget to, final Transition transition) {
 282  
             // nothing to do
 283  5
         }
 284  
 
 285  
         /**
 286  
          * No-op.
 287  
          *
 288  
          * @param exited The transition target being exited.
 289  
          */
 290  
         public void onExit(final TransitionTarget exited) {
 291  
             // nothing to do
 292  5
         }
 293  
 
 294  
     }
 295  
 
 296  
 }
 297