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

Classes in this File | Line Coverage | Branch Coverage | Complexity
AbstractStateMachine |
46% |
50% |
| 2.4615384615384617;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 &quot;component developers&quot;
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 |  |