View Javadoc

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      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      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         this(scxmlDocument, new JexlContext(), new JexlEvaluator());
105     }
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             final Context rootCtx, final Evaluator evaluator) {
121         log = LogFactory.getLog(this.getClass());
122         if (stateMachine == null) {
123             // parse only once per subclass
124             ErrorHandler errHandler = new SimpleErrorHandler();
125             try {
126                 stateMachine = SCXMLDigester.digest(scxmlDocument,
127                     errHandler);
128             } catch (IOException ioe) {
129                 logError(ioe);
130             } catch (SAXException sae) {
131                 logError(sae);
132             } catch (ModelException me) {
133                 logError(me);
134             }
135         }
136         engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(),
137             new SimpleErrorReporter());
138         engine.setStateMachine(stateMachine);
139         engine.setSuperStep(true);
140         engine.setRootContext(rootCtx);
141         engine.addListener(stateMachine, new EntryListener());
142         try {
143             engine.go();
144         } catch (ModelException me) {
145             logError(me);
146         }
147     }
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         TriggerEvent[] evts = {new TriggerEvent(event,
158                 TriggerEvent.SIGNAL_EVENT, null)};
159         try {
160             engine.triggerEvents(evts);
161         } catch (ModelException me) {
162             logError(me);
163         }
164         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         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         return engine;
184     }
185 
186     /***
187      * Get the log for this class.
188      *
189      * @return Returns the log.
190      */
191     public Log getLog() {
192         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         this.log = log;
202     }
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         Class clas = this.getClass();
212         try {
213             Method method = clas.getDeclaredMethod(methodName, SIGNATURE);
214             method.invoke(this, PARAMETERS);
215         } catch (SecurityException se) {
216             logError(se);
217             return false;
218         } catch (NoSuchMethodException nsme) {
219             logError(nsme);
220             return false;
221         } catch (IllegalArgumentException iae) {
222             logError(iae);
223             return false;
224         } catch (IllegalAccessException iae) {
225             logError(iae);
226             return false;
227         } catch (InvocationTargetException ite) {
228             logError(ite);
229             return false;
230         }
231         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             engine.reset();
242         } catch (ModelException me) {
243             logError(me);
244             return false;
245         }
246         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         if (log.isErrorEnabled()) {
256             log.error(exception.getMessage(), exception);
257         }
258     }
259 
260     /***
261      * A SCXMLListener that is only concerned about &quot;onentry&quot;
262      * notifications.
263      */
264     protected class EntryListener implements SCXMLListener {
265 
266         /***
267          * {@inheritDoc}
268          */
269         public void onEntry(final TransitionTarget entered) {
270             invoke(entered.getId());
271         }
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         }
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         }
293 
294     }
295 
296 }
297