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