Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
SimpleScheduler |
|
| 2.875;2.875 |
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 | * <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 | 0 | public class SimpleScheduler implements EventDispatcher, Serializable { |
49 | ||
50 | /** Serial version UID. */ |
|
51 | private static final long serialVersionUID = 1L; |
|
52 | ||
53 | /** Log instance. */ |
|
54 | 0 | 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 | 0 | super(); |
74 | 0 | this.executor = executor; |
75 | 0 | this.timers = Collections.synchronizedMap(new HashMap()); |
76 | 0 | } |
77 | ||
78 | /** |
|
79 | * @see EventDispatcher#cancel(String) |
|
80 | */ |
|
81 | public void cancel(final String sendId) { |
|
82 | // Log callback |
|
83 | 0 | if (log.isInfoEnabled()) { |
84 | 0 | log.info("cancel( sendId: " + sendId + ")"); |
85 | } |
|
86 | 0 | if (!timers.containsKey(sendId)) { |
87 | 0 | return; // done, we don't track this one or its already expired |
88 | } |
|
89 | 0 | Timer timer = (Timer) timers.get(sendId); |
90 | 0 | if (timer != null) { |
91 | 0 | timer.cancel(); |
92 | 0 | if (log.isDebugEnabled()) { |
93 | 0 | log.debug("Cancelled event scheduled by <send> with id '" |
94 | + sendId + "'"); |
|
95 | } |
|
96 | } |
|
97 | 0 | timers.remove(sendId); |
98 | 0 | } |
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 | 0 | if (log.isInfoEnabled()) { |
108 | 0 | StringBuffer buf = new StringBuffer(); |
109 | 0 | buf.append("send ( sendId: ").append(sendId); |
110 | 0 | buf.append(", target: ").append(target); |
111 | 0 | buf.append(", targetType: ").append(targettype); |
112 | 0 | buf.append(", event: ").append(event); |
113 | 0 | buf.append(", params: ").append(String.valueOf(params)); |
114 | 0 | buf.append(", hints: ").append(String.valueOf(hints)); |
115 | 0 | buf.append(", delay: ").append(delay); |
116 | 0 | buf.append(')'); |
117 | 0 | log.info(buf.toString()); |
118 | } |
|
119 | ||
120 | // We only handle the "scxml" targettype (which is the default too) |
|
121 | 0 | if (SCXMLHelper.isStringEmpty(targettype) |
122 | || targettype.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) { |
|
123 | ||
124 | 0 | if (!SCXMLHelper.isStringEmpty(target)) { |
125 | // We know of no other target |
|
126 | 0 | if (log.isWarnEnabled()) { |
127 | 0 | log.warn("<send>: Unavailable target - " + target); |
128 | } |
|
129 | try { |
|
130 | 0 | this.executor.triggerEvent(new TriggerEvent( |
131 | EVENT_ERR_SEND_TARGETUNAVAILABLE, |
|
132 | TriggerEvent.ERROR_EVENT)); |
|
133 | 0 | } catch (ModelException me) { |
134 | 0 | log.error(me.getMessage(), me); |
135 | 0 | } |
136 | 0 | return; // done |
137 | } |
|
138 | ||
139 | 0 | if (delay > 0L) { |
140 | // Need to schedule this one |
|
141 | 0 | Timer timer = new Timer(true); |
142 | 0 | timer.schedule(new DelayedEventTask(sendId, event), delay); |
143 | 0 | timers.put(sendId, timer); |
144 | 0 | if (log.isDebugEnabled()) { |
145 | 0 | 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 | 0 | } |
156 | ||
157 | /** |
|
158 | * Get the log instance. |
|
159 | * |
|
160 | * @return The current log instance |
|
161 | */ |
|
162 | protected Log getLog() { |
|
163 | 0 | return log; |
164 | } |
|
165 | ||
166 | /** |
|
167 | * Get the current timers. |
|
168 | * |
|
169 | * @return The currently scheduled timers |
|
170 | */ |
|
171 | protected Map getTimers() { |
|
172 | 0 | 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 | 0 | 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 | 0 | DelayedEventTask(final String sendId, final String event) { |
206 | 0 | super(); |
207 | 0 | this.sendId = sendId; |
208 | 0 | this.event = event; |
209 | 0 | } |
210 | ||
211 | /** |
|
212 | * What to do when timer expires. |
|
213 | */ |
|
214 | public void run() { |
|
215 | try { |
|
216 | 0 | executor.triggerEvent(new TriggerEvent(event, |
217 | TriggerEvent.SIGNAL_EVENT)); |
|
218 | 0 | } catch (ModelException me) { |
219 | 0 | log.error(me.getMessage(), me); |
220 | 0 | } |
221 | 0 | timers.remove(sendId); |
222 | 0 | if (log.isDebugEnabled()) { |
223 | 0 | log.debug("Fired event '" + event + "' as scheduled by " |
224 | + "<send> with id '" + sendId + "'"); |
|
225 | } |
|
226 | 0 | } |
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 |