1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 * <send> events for the "scxml" <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 * "scxml" <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 * <send> 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
83 if (log.isInfoEnabled()) {
84 log.info("cancel( sendId: " + sendId + ")");
85 }
86 if (!timers.containsKey(sendId)) {
87 return;
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
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
121 if (SCXMLHelper.isStringEmpty(targettype)
122 || targettype.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) {
123
124 if (!SCXMLHelper.isStringEmpty(target)) {
125
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;
137 }
138
139 if (delay > 0L) {
140
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
151
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 <send> 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