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.scxml.env;
18  
19  import java.io.Serializable;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Timer;
25  import java.util.TimerTask;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.commons.scxml.EventDispatcher;
30  import org.apache.commons.scxml.SCXMLExecutor;
31  import org.apache.commons.scxml.SCXMLHelper;
32  import org.apache.commons.scxml.TriggerEvent;
33  import org.apache.commons.scxml.model.ModelException;
34  
35  /***
36   * <p>EventDispatcher implementation that can schedule <code>delay</code>ed
37   * &lt;send&gt; events for the &quot;scxml&quot; <code>targettype</code>
38   * attribute value (which is also the default). This implementation uses
39   * J2SE <code>Timer</code>s.</p>
40   *
41   * <p>No other <code>targettype</code>s are processed. Subclasses may support
42   * additional <code>targettype</code>s by overriding the
43   * <code>send(...)</code> and <code>cancel(...)</code> methods and
44   * delegating to their <code>super</code> counterparts for the
45   * &quot;scxml&quot; <code>targettype</code>.</p>
46   *
47   */
48  public class SimpleScheduler implements EventDispatcher, Serializable {
49  
50      /*** Serial version UID. */
51      private static final long serialVersionUID = 1L;
52  
53      /*** Log instance. */
54      private Log log = LogFactory.getLog(SimpleScheduler.class);
55  
56      /***
57       * The <code>Map</code> of active <code>Timer</code>s, keyed by
58       * &lt;send&gt; element <code>id</code>s.
59       */
60      private Map timers;
61  
62      /***
63       * The state chart execution instance we schedule events for.
64       */
65      private SCXMLExecutor executor;
66  
67      /***
68       * Constructor.
69       *
70       * @param executor The owning {@link SCXMLExecutor} instance.
71       */
72      public SimpleScheduler(final SCXMLExecutor executor) {
73          super();
74          this.executor = executor;
75          this.timers = Collections.synchronizedMap(new HashMap());
76      }
77  
78      /***
79       * @see EventDispatcher#cancel(String)
80       */
81      public void cancel(final String sendId) {
82          // Log callback
83          if (log.isInfoEnabled()) {
84              log.info("cancel( sendId: " + sendId + ")");
85          }
86          if (!timers.containsKey(sendId)) {
87              return; // done, we don't track this one or its already expired
88          }
89          Timer timer = (Timer) timers.get(sendId);
90          if (timer != null) {
91              timer.cancel();
92              if (log.isDebugEnabled()) {
93                  log.debug("Cancelled event scheduled by <send> with id '"
94                      + sendId + "'");
95              }
96          }
97          timers.remove(sendId);
98      }
99  
100     /***
101     @see EventDispatcher#send(String,String,String,String,Map,Object,long,List)
102      */
103     public void send(final String sendId, final String target,
104             final String targettype, final String event, final Map params,
105             final Object hints, final long delay, final List externalNodes) {
106         // Log callback
107         if (log.isInfoEnabled()) {
108             StringBuffer buf = new StringBuffer();
109             buf.append("send ( sendId: ").append(sendId);
110             buf.append(", target: ").append(target);
111             buf.append(", targetType: ").append(targettype);
112             buf.append(", event: ").append(event);
113             buf.append(", params: ").append(String.valueOf(params));
114             buf.append(", hints: ").append(String.valueOf(hints));
115             buf.append(", delay: ").append(delay);
116             buf.append(')');
117             log.info(buf.toString());
118         }
119 
120         // We only handle the "scxml" targettype (which is the default too)
121         if (SCXMLHelper.isStringEmpty(targettype)
122                 || targettype.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) {
123 
124             if (!SCXMLHelper.isStringEmpty(target)) {
125                 // We know of no other target
126                 if (log.isWarnEnabled()) {
127                     log.warn("<send>: Unavailable target - " + target);
128                 }
129                 try {
130                     this.executor.triggerEvent(new TriggerEvent(
131                         EVENT_ERR_SEND_TARGETUNAVAILABLE,
132                         TriggerEvent.ERROR_EVENT));
133                 } catch (ModelException me) {
134                     log.error(me.getMessage(), me);
135                 }
136                 return; // done
137             }
138 
139             if (delay > 0L) {
140                 // Need to schedule this one
141                 Timer timer = new Timer(true);
142                 timer.schedule(new DelayedEventTask(sendId, event), delay);
143                 timers.put(sendId, timer);
144                 if (log.isDebugEnabled()) {
145                     log.debug("Scheduled event '" + event + "' with delay "
146                         + delay + "ms, as specified by <send> with id '"
147                         + sendId + "'");
148                 }
149             }
150             // else short-circuited by Send#execute()
151             // TODO: Pass through in v1.0
152 
153         }
154 
155     }
156 
157     /***
158      * Get the log instance.
159      *
160      * @return The current log instance
161      */
162     protected Log getLog() {
163         return log;
164     }
165 
166     /***
167      * Get the current timers.
168      *
169      * @return The currently scheduled timers
170      */
171     protected Map getTimers() {
172         return timers;
173     }
174 
175     /***
176      * Get the executor we're attached to.
177      *
178      * @return The owning executor instance
179      */
180     protected SCXMLExecutor getExecutor() {
181         return executor;
182     }
183 
184     /***
185      * TimerTask implementation.
186      */
187     class DelayedEventTask extends TimerTask {
188 
189         /***
190          * The ID of the &lt;send&gt; element.
191          */
192         private String sendId;
193 
194         /***
195          * The event name.
196          */
197         private String event;
198 
199         /***
200          * Constructor.
201          *
202          * @param sendId The ID of the send element.
203          * @param event The name of the event to be triggered.
204          */
205         DelayedEventTask(final String sendId, final String event) {
206             super();
207             this.sendId = sendId;
208             this.event = event;
209         }
210 
211         /***
212          * What to do when timer expires.
213          */
214         public void run() {
215             try {
216                 executor.triggerEvent(new TriggerEvent(event,
217                     TriggerEvent.SIGNAL_EVENT));
218             } catch (ModelException me) {
219                 log.error(me.getMessage(), me);
220             }
221             timers.remove(sendId);
222             if (log.isDebugEnabled()) {
223                 log.debug("Fired event '" + event + "' as scheduled by "
224                     + "<send> with id '" + sendId + "'");
225             }
226         }
227 
228     }
229 
230     /***
231      * The default targettype.
232      */
233     private static final String TARGETTYPE_SCXML = "scxml";
234 
235     /***
236      * The spec mandated derived event when target cannot be reached.
237      */
238     private static final String EVENT_ERR_SEND_TARGETUNAVAILABLE =
239         "error.send.targetunavailable";
240 
241 }
242