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
39
40
41
42 public class Send extends Action implements ExternalContent {
43
44
45
46
47 private static final long serialVersionUID = 1L;
48
49
50
51
52 private static final String TYPE_SCXML = "scxml";
53
54
55
56
57
58 private static final String EVENT_ERR_SEND_TARGETUNAVAILABLE =
59 "error.send.targetunavailable";
60
61
62
63
64 private String sendid;
65
66
67
68
69 private String target;
70
71
72
73
74
75 private String type;
76
77
78
79
80 private String delay;
81
82
83
84
85
86 private String hints;
87
88
89
90
91 private String namelist;
92
93
94
95
96 private List externalNodes;
97
98
99
100
101 private String event;
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public Send() {
117 super();
118 this.externalNodes = new ArrayList();
119 }
120
121
122
123
124
125
126 public final String getDelay() {
127 return delay;
128 }
129
130
131
132
133
134
135 public final void setDelay(final String delay) {
136 this.delay = delay;
137 }
138
139
140
141
142
143
144 public final List getExternalNodes() {
145 return externalNodes;
146 }
147
148
149
150
151
152
153 public final void setExternalNodes(final List externalNodes) {
154 this.externalNodes = externalNodes;
155 }
156
157
158
159
160
161
162 public final String getHints() {
163 return hints;
164 }
165
166
167
168
169
170
171 public final void setHints(final String hints) {
172 this.hints = hints;
173 }
174
175
176
177
178
179
180 public final String getNamelist() {
181 return namelist;
182 }
183
184
185
186
187
188
189 public final void setNamelist(final String namelist) {
190 this.namelist = namelist;
191 }
192
193
194
195
196
197
198 public final String getSendid() {
199 return sendid;
200 }
201
202
203
204
205
206
207 public final void setSendid(final String sendid) {
208 this.sendid = sendid;
209 }
210
211
212
213
214
215
216 public final String getTarget() {
217 return target;
218 }
219
220
221
222
223
224
225 public final void setTarget(final String target) {
226 this.target = target;
227 }
228
229
230
231
232
233
234
235 public final String getTargettype() {
236 return type;
237 }
238
239
240
241
242
243
244
245 public final void setTargettype(final String targettype) {
246 this.type = targettype;
247 }
248
249
250
251
252
253
254 public final String getType() {
255 return type;
256 }
257
258
259
260
261
262
263 public final void setType(final String type) {
264 this.type = type;
265 }
266
267
268
269
270
271
272 public final void setEvent(final String event) {
273 this.event = event;
274 }
275
276
277
278
279
280
281 public final String getEvent() {
282 return event;
283 }
284
285
286
287
288 public void execute(final EventDispatcher evtDispatcher,
289 final ErrorReporter errRep, final SCInstance scInstance,
290 final Log appLog, final Collection derivedEvents)
291 throws ModelException, SCXMLExpressionException {
292
293 TransitionTarget parentTarget = getParentTransitionTarget();
294 Context ctx = scInstance.getContext(parentTarget);
295 ctx.setLocal(getNamespacesKey(), getNamespaces());
296 Evaluator eval = scInstance.getEvaluator();
297
298
299 Object hintsValue = null;
300 if (!SCXMLHelper.isStringEmpty(hints)) {
301 hintsValue = eval.eval(ctx, hints);
302 }
303 String targetValue = target;
304 if (!SCXMLHelper.isStringEmpty(target)) {
305 targetValue = (String) eval.eval(ctx, target);
306 if (SCXMLHelper.isStringEmpty(targetValue)
307 && appLog.isWarnEnabled()) {
308 appLog.warn("<send>: target expression \"" + target
309 + "\" evaluated to null or empty String");
310 }
311 }
312 String typeValue = type;
313 if (!SCXMLHelper.isStringEmpty(type)) {
314 typeValue = (String) eval.eval(ctx, type);
315 if (SCXMLHelper.isStringEmpty(typeValue)
316 && appLog.isWarnEnabled()) {
317 appLog.warn("<send>: type expression \"" + type
318 + "\" evaluated to null or empty String");
319 }
320 } else {
321
322 typeValue = TYPE_SCXML;
323 }
324 Map params = null;
325 if (!SCXMLHelper.isStringEmpty(namelist)) {
326 StringTokenizer tkn = new StringTokenizer(namelist);
327 params = new HashMap(tkn.countTokens());
328 while (tkn.hasMoreTokens()) {
329 String varName = tkn.nextToken();
330 Object varObj = ctx.get(varName);
331 if (varObj == null) {
332
333 errRep.onError(ErrorConstants.UNDEFINED_VARIABLE,
334 varName + " = null", parentTarget);
335 }
336 params.put(varName, varObj);
337 }
338 }
339 long wait = 0L;
340 if (!SCXMLHelper.isStringEmpty(delay)) {
341 Object delayValue = eval.eval(ctx, delay);
342 if (delayValue != null) {
343 String delayString = delayValue.toString();
344 wait = parseDelay(delayString, appLog);
345 }
346 }
347 String eventValue = event;
348 if (!SCXMLHelper.isStringEmpty(event)) {
349 eventValue = (String) eval.eval(ctx, event);
350 if (SCXMLHelper.isStringEmpty(eventValue)
351 && appLog.isWarnEnabled()) {
352 appLog.warn("<send>: event expression \"" + event
353 + "\" evaluated to null or empty String");
354 }
355 }
356
357 if (typeValue != null
358 && typeValue.trim().equalsIgnoreCase(TYPE_SCXML)) {
359 if (SCXMLHelper.isStringEmpty(targetValue)) {
360
361 if (wait == 0L) {
362 if (appLog.isDebugEnabled()) {
363 appLog.debug("<send>: Enqueued event '" + eventValue
364 + "' with no delay");
365 }
366 derivedEvents.add(new TriggerEvent(eventValue,
367 TriggerEvent.SIGNAL_EVENT, params));
368 return;
369 }
370 } else {
371
372 if (appLog.isWarnEnabled()) {
373 appLog.warn("<send>: Unavailable target - "
374 + targetValue);
375 }
376 derivedEvents.add(new TriggerEvent(
377 EVENT_ERR_SEND_TARGETUNAVAILABLE,
378 TriggerEvent.ERROR_EVENT));
379
380 return;
381 }
382 }
383 ctx.setLocal(getNamespacesKey(), null);
384 if (appLog.isDebugEnabled()) {
385 appLog.debug("<send>: Dispatching event '" + eventValue
386 + "' to target '" + targetValue + "' of target type '"
387 + typeValue + "' with suggested delay of " + wait
388 + "ms");
389 }
390
391 evtDispatcher.send(sendid, targetValue, typeValue, eventValue,
392 params, hintsValue, wait, externalNodes);
393 }
394
395
396
397
398
399
400
401
402
403 private long parseDelay(final String delayString, final Log appLog)
404 throws SCXMLExpressionException {
405
406 long wait = 0L;
407 long multiplier = 1L;
408
409 if (!SCXMLHelper.isStringEmpty(delayString)) {
410
411 String trimDelay = delayString.trim();
412 String numericDelay = trimDelay;
413 if (trimDelay.endsWith(MILLIS)) {
414 numericDelay = trimDelay.substring(0, trimDelay.length() - 2);
415 } else if (trimDelay.endsWith(SECONDS)) {
416 multiplier = MILLIS_IN_A_SECOND;
417 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
418 } else if (trimDelay.endsWith(MINUTES)) {
419 multiplier = MILLIS_IN_A_MINUTE;
420 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
421 }
422
423 try {
424 wait = Long.parseLong(numericDelay);
425 } catch (NumberFormatException nfe) {
426 appLog.error(nfe.getMessage(), nfe);
427 throw new SCXMLExpressionException(nfe.getMessage(), nfe);
428 }
429 wait *= multiplier;
430
431 }
432
433 return wait;
434
435 }
436
437
438 private static final String MILLIS = "ms";
439
440
441 private static final String SECONDS = "s";
442
443
444 private static final String MINUTES = "m";
445
446
447 private static final long MILLIS_IN_A_SECOND = 1000L;
448
449
450 private static final long MILLIS_IN_A_MINUTE = 60000L;
451
452 }