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