1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml.model;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.StringTokenizer;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.scxml.Context;
28 import org.apache.commons.scxml.ErrorReporter;
29 import org.apache.commons.scxml.Evaluator;
30 import org.apache.commons.scxml.EventDispatcher;
31 import org.apache.commons.scxml.SCInstance;
32 import org.apache.commons.scxml.SCXMLExpressionException;
33 import org.apache.commons.scxml.SCXMLHelper;
34 import org.apache.commons.scxml.TriggerEvent;
35 import org.apache.commons.scxml.semantics.ErrorConstants;
36
37 /***
38 * The class in this SCXML object model that corresponds to the
39 * <send> SCXML element.
40 *
41 */
42 public class Send extends Action implements ExternalContent {
43
44 /***
45 * Serial version UID.
46 */
47 private static final long serialVersionUID = 1L;
48
49 /***
50 * The default targettype.
51 */
52 private static final String TARGETTYPE_SCXML = "scxml";
53
54 /***
55 * The spec mandated derived event when target cannot be reached
56 * for TARGETTYPE_SCXML.
57 */
58 private static final String EVENT_ERR_SEND_TARGETUNAVAILABLE =
59 "error.send.targetunavailable";
60
61 /***
62 * The ID of the send message.
63 */
64 private String sendid;
65
66 /***
67 * An expression returning the target location of the event.
68 */
69 private String target;
70
71 /***
72 * The type of the Event I/O Processor that the event.
73 * should be dispatched to
74 */
75 private String targettype;
76
77 /***
78 * The event is dispatched after the delay interval elapses.
79 */
80 private String delay;
81
82 /***
83 * The data containing information which may be used by the
84 * implementing platform to configure the event processor.
85 */
86 private String hints;
87
88 /***
89 * The namelist to the sent.
90 */
91 private String namelist;
92
93 /***
94 * The list of external nodes associated with this <send> element.
95 */
96 private List externalNodes;
97
98 /***
99 * The type of event being generated.
100 */
101 private String event;
102
103 /***
104 * OutputFormat used to serialize external nodes.
105 *
106 private static final OutputFormat format;
107 static {
108 format = new OutputFormat();
109 format.setOmitXMLDeclaration(true);
110 }
111 */
112
113 /***
114 * Constructor.
115 */
116 public Send() {
117 super();
118 this.externalNodes = new ArrayList();
119 }
120
121 /***
122 * Get the delay.
123 *
124 * @return Returns the delay.
125 */
126 public final String getDelay() {
127 return delay;
128 }
129
130 /***
131 * Set the delay.
132 *
133 * @param delay The delay to set.
134 */
135 public final void setDelay(final String delay) {
136 this.delay = delay;
137 }
138
139 /***
140 * Get the list of external namespaced child nodes.
141 *
142 * @return List Returns the list of externalnodes.
143 */
144 public final List getExternalNodes() {
145 return externalNodes;
146 }
147
148 /***
149 * Set the list of external namespaced child nodes.
150 *
151 * @param externalNodes The externalnode to set.
152 */
153 public final void setExternalNodes(final List externalNodes) {
154 this.externalNodes = externalNodes;
155 }
156
157 /***
158 * Get the hints for this <send> element.
159 *
160 * @return String Returns the hints.
161 */
162 public final String getHints() {
163 return hints;
164 }
165
166 /***
167 * Set the hints for this <send> element.
168 *
169 * @param hints The hints to set.
170 */
171 public final void setHints(final String hints) {
172 this.hints = hints;
173 }
174
175 /***
176 * Get the namelist.
177 *
178 * @return String Returns the namelist.
179 */
180 public final String getNamelist() {
181 return namelist;
182 }
183
184 /***
185 * Set the namelist.
186 *
187 * @param namelist The namelist to set.
188 */
189 public final void setNamelist(final String namelist) {
190 this.namelist = namelist;
191 }
192
193 /***
194 * Get the identifier for this <send> element.
195 *
196 * @return String Returns the sendid.
197 */
198 public final String getSendid() {
199 return sendid;
200 }
201
202 /***
203 * Set the identifier for this <send> element.
204 *
205 * @param sendid The sendid to set.
206 */
207 public final void setSendid(final String sendid) {
208 this.sendid = sendid;
209 }
210
211 /***
212 * Get the target for this <send> element.
213 *
214 * @return String Returns the target.
215 */
216 public final String getTarget() {
217 return target;
218 }
219
220 /***
221 * Set the target for this <send> element.
222 *
223 * @param target The target to set.
224 */
225 public final void setTarget(final String target) {
226 this.target = target;
227 }
228
229 /***
230 * Get the target type for this <send> element.
231 *
232 * @return String Returns the targettype.
233 */
234 public final String getTargettype() {
235 return targettype;
236 }
237
238 /***
239 * Set the target type for this <send> element.
240 *
241 * @param targettype The targettype to set.
242 */
243 public final void setTargettype(final String targettype) {
244 this.targettype = targettype;
245 }
246
247 /***
248 * Get the event to send.
249 *
250 * @param event The event to set.
251 */
252 public final void setEvent(final String event) {
253 this.event = event;
254 }
255
256 /***
257 * Set the event to send.
258 *
259 * @return String Returns the event.
260 */
261 public final String getEvent() {
262 return event;
263 }
264
265 /***
266 * {@inheritDoc}
267 */
268 public void execute(final EventDispatcher evtDispatcher,
269 final ErrorReporter errRep, final SCInstance scInstance,
270 final Log appLog, final Collection derivedEvents)
271 throws ModelException, SCXMLExpressionException {
272
273 State parentState = getParentState();
274 Context ctx = scInstance.getContext(parentState);
275 ctx.setLocal(getNamespacesKey(), getNamespaces());
276 Evaluator eval = scInstance.getEvaluator();
277
278
279 Object hintsValue = null;
280 if (!SCXMLHelper.isStringEmpty(hints)) {
281 hintsValue = eval.eval(ctx, hints);
282 }
283 String targetValue = target;
284 if (!SCXMLHelper.isStringEmpty(target)) {
285 targetValue = (String) eval.eval(ctx, target);
286 if (SCXMLHelper.isStringEmpty(targetValue)
287 && appLog.isWarnEnabled()) {
288 appLog.warn("<send>: target expression \"" + target
289 + "\" evaluated to null or empty String");
290 }
291 }
292 String targettypeValue = targettype;
293 if (!SCXMLHelper.isStringEmpty(targettype)) {
294 targettypeValue = (String) eval.eval(ctx, targettype);
295 if (SCXMLHelper.isStringEmpty(targettypeValue)
296 && appLog.isWarnEnabled()) {
297 appLog.warn("<send>: targettype expression \"" + targettype
298 + "\" evaluated to null or empty String");
299 }
300 } else {
301
302 targettypeValue = TARGETTYPE_SCXML;
303 }
304 Map params = null;
305 if (!SCXMLHelper.isStringEmpty(namelist)) {
306 StringTokenizer tkn = new StringTokenizer(namelist);
307 params = new HashMap(tkn.countTokens());
308 while (tkn.hasMoreTokens()) {
309 String varName = tkn.nextToken();
310 Object varObj = ctx.get(varName);
311 if (varObj == null) {
312
313 errRep.onError(ErrorConstants.UNDEFINED_VARIABLE,
314 varName + " = null", parentState);
315 }
316 params.put(varName, varObj);
317 }
318 }
319 long wait = parseDelay(appLog);
320
321 if (targettypeValue != null
322 && targettypeValue.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) {
323 if (SCXMLHelper.isStringEmpty(targetValue)) {
324
325 if (wait == 0L) {
326 if (appLog.isDebugEnabled()) {
327 appLog.debug("<send>: Enqueued event '" + event
328 + "' with no delay");
329 }
330 derivedEvents.add(new TriggerEvent(event,
331 TriggerEvent.SIGNAL_EVENT));
332 return;
333 }
334 } else {
335
336 if (appLog.isWarnEnabled()) {
337 appLog.warn("<send>: Unavailable target - "
338 + targetValue);
339 }
340 derivedEvents.add(new TriggerEvent(
341 EVENT_ERR_SEND_TARGETUNAVAILABLE,
342 TriggerEvent.ERROR_EVENT));
343
344 return;
345 }
346 }
347 ctx.setLocal(getNamespacesKey(), null);
348 if (appLog.isDebugEnabled()) {
349 appLog.debug("<send>: Dispatching event '" + event
350 + "' to target '" + targetValue + "' of target type '"
351 + targettypeValue + "' with suggested delay of " + wait
352 + "ms");
353 }
354
355 evtDispatcher.send(sendid, targetValue, targettypeValue, event,
356 params, hintsValue, wait, externalNodes);
357 }
358
359 /***
360 * Parse delay.
361 *
362 * @param appLog The application log
363 * @return The parsed delay in milliseconds
364 * @throws SCXMLExpressionException If the delay cannot be parsed
365 */
366 private long parseDelay(final Log appLog)
367 throws SCXMLExpressionException {
368
369 long wait = 0L;
370 long multiplier = 1L;
371
372 if (!SCXMLHelper.isStringEmpty(delay)) {
373
374 String trimDelay = delay.trim();
375 String numericDelay = trimDelay;
376 if (trimDelay.endsWith(MILLIS)) {
377 numericDelay = trimDelay.substring(0, trimDelay.length() - 2);
378 } else if (trimDelay.endsWith(SECONDS)) {
379 multiplier = MILLIS_IN_A_SECOND;
380 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
381 } else if (trimDelay.endsWith(MINUTES)) {
382 multiplier = MILLIS_IN_A_MINUTE;
383 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
384 }
385
386 try {
387 wait = Long.parseLong(numericDelay);
388 } catch (NumberFormatException nfe) {
389 appLog.error(nfe.getMessage(), nfe);
390 throw new SCXMLExpressionException(nfe.getMessage(), nfe);
391 }
392 wait *= multiplier;
393
394 }
395
396 return wait;
397
398 }
399
400 /*** The suffix in the delay string for milliseconds. */
401 private static final String MILLIS = "ms";
402
403 /*** The suffix in the delay string for seconds. */
404 private static final String SECONDS = "s";
405
406 /*** The suffix in the delay string for minutes. */
407 private static final String MINUTES = "m";
408
409 /*** The number of milliseconds in a second. */
410 private static final long MILLIS_IN_A_SECOND = 1000L;
411
412 /*** The number of milliseconds in a minute. */
413 private static final long MILLIS_IN_A_MINUTE = 60000L;
414
415 }
416