| 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 |