View Javadoc
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.scxml2.model;
18  
19  import java.util.LinkedHashMap;
20  import java.util.Map;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.scxml2.ActionExecutionContext;
24  import org.apache.commons.scxml2.Context;
25  import org.apache.commons.scxml2.Evaluator;
26  import org.apache.commons.scxml2.SCXMLExpressionException;
27  import org.apache.commons.scxml2.SCXMLIOProcessor;
28  import org.apache.commons.scxml2.SCXMLSystemContext;
29  
30  /**
31   * The class in this SCXML object model that corresponds to the
32   * <send> SCXML element.
33   *
34   */
35  public class Send extends NamelistHolder implements ContentContainer {
36  
37      /**
38       * Serial version UID.
39       */
40      private static final long serialVersionUID = 1L;
41  
42      /**
43       * The suffix in the delay string for milliseconds.
44       */
45      private static final String MILLIS = "ms";
46  
47      /**
48       * The suffix in the delay string for seconds.
49       */
50      private static final String SECONDS = "s";
51  
52      /**
53       * The suffix in the delay string for minutes.
54       */
55      private static final String MINUTES = "m";
56  
57      /**
58       * The number of milliseconds in a second.
59       */
60      private static final long MILLIS_IN_A_SECOND = 1000L;
61  
62      /**
63       * The number of milliseconds in a minute.
64       */
65      private static final long MILLIS_IN_A_MINUTE = 60000L;
66  
67      /**
68       * The ID of the send message.
69       */
70      private String id;
71  
72      /**
73       * Path expression evaluating to a location within a previously defined XML data tree.
74       */
75      private String idlocation;
76  
77      /**
78       * The target location of the event.
79       */
80      private String target;
81  
82  
83      /**
84       * An expression specifying the target location of the event.
85       */
86      private String targetexpr;
87  
88      /**
89       * The type of the Event I/O Processor that the event should be dispatched to.
90       */
91      private String type;
92  
93      /**
94       * An expression defining the type of the Event I/O Processor that the event should be dispatched to.
95       */
96      private String typeexpr;
97  
98      /**
99       * The delay the event is dispatched after.
100      */
101     private String delay;
102 
103     /**
104      * An expression defining the delay the event is dispatched after.
105      */
106     private String delayexpr;
107 
108     /**
109      * The data containing information which may be used by the implementing platform to configure the event processor.
110      */
111     private String hints;
112 
113     /**
114      * The type of event being generated.
115      */
116     private String event;
117 
118     /**
119      * An expression defining the type of event being generated.
120      */
121     private String eventexpr;
122 
123     /**
124      * The <content/> of this send
125      */
126     private Content content;
127 
128     /**
129      * Constructor.
130      */
131     public Send() {
132         super();
133     }
134 
135     /**
136      * @return the idlocation
137      */
138     public String getIdlocation() {
139         return idlocation;
140     }
141 
142     /**
143      * Set the idlocation expression
144      *
145      * @param idlocation The idlocation expression
146      */
147     public void setIdlocation(final String idlocation) {
148         this.idlocation = idlocation;
149     }
150 
151     /**
152      * Get the delay.
153      *
154      * @return Returns the delay.
155      */
156     public final String getDelay() {
157         return delay;
158     }
159 
160     /**
161      * Set the delay.
162      *
163      * @param delay The delay to set.
164      */
165     public final void setDelay(final String delay) {
166         this.delay = delay;
167     }
168 
169     /**
170      * @return The delay expression
171      */
172     public String getDelayexpr() {
173         return delayexpr;
174     }
175 
176     /**
177      * Set the delay expression
178      *
179      * @param delayexpr The delay expression to set
180      */
181     public void setDelayexpr(final String delayexpr) {
182         this.delayexpr = delayexpr;
183     }
184 
185     /**
186      * Get the hints for this <send> element.
187      *
188      * @return String Returns the hints.
189      */
190     public final String getHints() {
191         return hints;
192     }
193 
194     /**
195      * Set the hints for this <send> element.
196      *
197      * @param hints The hints to set.
198      */
199     public final void setHints(final String hints) {
200         this.hints = hints;
201     }
202 
203     /**
204      * Get the identifier for this <send> element.
205      *
206      * @return String Returns the id.
207      */
208     public final String getId() {
209         return id;
210     }
211 
212     /**
213      * Set the identifier for this <send> element.
214      *
215      * @param id The id to set.
216      */
217     public final void setId(final String id) {
218         this.id = id;
219     }
220 
221     /**
222      * Get the target for this <send> element.
223      *
224      * @return String Returns the target.
225      */
226     public final String getTarget() {
227         return target;
228     }
229 
230     /**
231      * Set the target for this <send> element.
232      *
233      * @param target The target to set.
234      */
235     public final void setTarget(final String target) {
236         this.target = target;
237     }
238 
239     /**
240      * @return The target expression
241      */
242     public String getTargetexpr() {
243         return targetexpr;
244     }
245 
246     /**
247      * Set the target expression
248      *
249      * @param targetexpr The target expression to set
250      */
251     public void setTargetexpr(final String targetexpr) {
252         this.targetexpr = targetexpr;
253     }
254 
255     /**
256      * Get the type for this <send> element.
257      *
258      * @return String Returns the type.
259      */
260     public final String getType() {
261         return type;
262     }
263 
264     /**
265      * Set the type for this <send> element.
266      *
267      * @param type The type to set.
268      */
269     public final void setType(final String type) {
270         this.type = type;
271     }
272 
273     /**
274      * @return The type expression
275      */
276     public String getTypeexpr() {
277         return typeexpr;
278     }
279 
280     /**
281      * Sets the type expression
282      *
283      * @param typeexpr The type expression to set
284      */
285     public void setTypeexpr(final String typeexpr) {
286         this.typeexpr = typeexpr;
287     }
288 
289     /**
290      * Get the event to send.
291      *
292      * @param event The event to set.
293      */
294     public final void setEvent(final String event) {
295         this.event = event;
296     }
297 
298     /**
299      * Set the event to send.
300      *
301      * @return String Returns the event.
302      */
303     public final String getEvent() {
304         return event;
305     }
306 
307     /**
308      * @return The event expression
309      */
310     public String getEventexpr() {
311         return eventexpr;
312     }
313 
314     /**
315      * Sets the event expression
316      *
317      * @param eventexpr The event expression to set
318      */
319     public void setEventexpr(final String eventexpr) {
320         this.eventexpr = eventexpr;
321     }
322 
323     /**
324      * Returns the content
325      *
326      * @return the content
327      */
328     public Content getContent() {
329         return content;
330     }
331 
332     /**
333      * Sets the content
334      *
335      * @param content the content to set
336      */
337     public void setContent(final Content content) {
338         this.content = content;
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     @SuppressWarnings("unchecked")
345     @Override
346     public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException {
347         // Send attributes evaluation
348         EnterableState parentState = getParentEnterableState();
349         Context ctx = exctx.getContext(parentState);
350         ctx.setLocal(getNamespacesKey(), getNamespaces());
351         Evaluator eval = exctx.getEvaluator();
352         // Most attributes of <send> are expressions so need to be
353         // evaluated before the EventDispatcher callback
354         Object hintsValue = null;
355         if (hints != null) {
356             hintsValue = eval.eval(ctx, hints);
357         }
358         if (id == null) {
359             id = ctx.getSystemContext().generateSessionId();
360             if (idlocation != null) {
361                 eval.evalAssign(ctx, idlocation, id, Evaluator.AssignType.REPLACE_CHILDREN, null);
362             }
363         }
364         String targetValue = target;
365         if (targetValue == null && targetexpr != null) {
366             targetValue = (String) getTextContentIfNodeResult(eval.eval(ctx, targetexpr));
367             if ((targetValue == null || targetValue.trim().length() == 0)
368                     && exctx.getAppLog().isWarnEnabled()) {
369                 exctx.getAppLog().warn("<send>: target expression \"" + targetexpr
370                         + "\" evaluated to null or empty String");
371             }
372         }
373         String typeValue = type;
374         if (typeValue == null && typeexpr != null) {
375             typeValue = (String) getTextContentIfNodeResult(eval.eval(ctx, typeexpr));
376             if ((typeValue == null || typeValue.trim().length() == 0)
377                     && exctx.getAppLog().isWarnEnabled()) {
378                 exctx.getAppLog().warn("<send>: type expression \"" + typeexpr
379                         + "\" evaluated to null or empty String");
380             }
381         }
382         if (typeValue == null) {
383             // must default to 'scxml' when unspecified
384             typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR;
385         } else if (!SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR.equals(typeValue) && typeValue.trim().equalsIgnoreCase(SCXMLIOProcessor.SCXML_EVENT_PROCESSOR)) {
386             typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR;
387         }
388         Object payload = null;
389         Map<String, Object> payloadDataMap = new LinkedHashMap<String, Object>();
390         addNamelistDataToPayload(exctx, payloadDataMap);
391         addParamsToPayload(exctx, payloadDataMap);
392         if (!payloadDataMap.isEmpty()) {
393             payload = makeEventPayload(eval, payloadDataMap);
394         }
395         else if (content != null) {
396             if (content.getExpr() != null) {
397                 payload = clonePayloadValue(eval.eval(ctx, content.getExpr()));
398             } else {
399                 payload = clonePayloadValue(content.getBody());
400             }
401         }
402         long wait = 0L;
403         String delayString = delay;
404         if (delayString == null && delayexpr != null) {
405             Object delayValue = getTextContentIfNodeResult(eval.eval(ctx, delayexpr));
406             if (delayValue != null) {
407                 delayString = delayValue.toString();
408             }
409         }
410         if (delayString != null) {
411             wait = parseDelay(delayString, exctx.getAppLog());
412         }
413         String eventValue = event;
414         if (eventValue == null && eventexpr != null) {
415             eventValue = (String) getTextContentIfNodeResult(eval.eval(ctx, eventexpr));
416             if ((eventValue == null)) {
417                 throw new SCXMLExpressionException("<send>: event expression \"" + eventexpr
418                         + "\" evaluated to null");
419             }
420         }
421         Map<String, SCXMLIOProcessor> ioProcessors = (Map<String, SCXMLIOProcessor>) ctx.get(SCXMLSystemContext.IOPROCESSORS_KEY);
422         ctx.setLocal(getNamespacesKey(), null);
423         if (exctx.getAppLog().isDebugEnabled()) {
424             exctx.getAppLog().debug("<send>: Dispatching event '" + eventValue
425                     + "' to target '" + targetValue + "' of target type '"
426                     + typeValue + "' with suggested delay of " + wait
427                     + "ms");
428         }
429         exctx.getEventDispatcher().send(ioProcessors, id, targetValue, typeValue, eventValue,
430                 payload, hintsValue, wait);
431     }
432 
433     /**
434      * Parse delay.
435      *
436      * @param delayString The String value of the delay, in CSS2 format
437      * @param appLog      The application log
438      * @return The parsed delay in milliseconds
439      * @throws SCXMLExpressionException If the delay cannot be parsed
440      */
441     private long parseDelay(final String delayString, final Log appLog)
442             throws SCXMLExpressionException {
443 
444         long wait = 0L;
445         long multiplier = 1L;
446 
447         if (delayString != null && delayString.trim().length() > 0) {
448 
449             String trimDelay = delayString.trim();
450             String numericDelay = trimDelay;
451             if (trimDelay.endsWith(MILLIS)) {
452                 numericDelay = trimDelay.substring(0, trimDelay.length() - 2);
453             } else if (trimDelay.endsWith(SECONDS)) {
454                 multiplier = MILLIS_IN_A_SECOND;
455                 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
456             } else if (trimDelay.endsWith(MINUTES)) { // Not CSS2
457                 multiplier = MILLIS_IN_A_MINUTE;
458                 numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
459             }
460 
461             try {
462                 wait = Long.parseLong(numericDelay);
463             } catch (NumberFormatException nfe) {
464                 appLog.error(nfe.getMessage(), nfe);
465                 throw new SCXMLExpressionException(nfe.getMessage(), nfe);
466             }
467             wait *= multiplier;
468 
469         }
470         return wait;
471     }
472 }
473