You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ra...@apache.org on 2006/10/17 19:07:56 UTC

svn commit: r464965 - in /jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml: env/SimpleScheduler.java model/Send.java test/StandaloneUtils.java

Author: rahul
Date: Tue Oct 17 10:07:55 2006
New Revision: 464965

URL: http://svn.apache.org/viewvc?view=rev&rev=464965
Log:
Provide a new EventDispatcher implementation (SimpleScheduler) that provides the ability to deal with delayed events of the "scxml" <send> targettype. The delay attribute now supports the following suffixes (ms-milliseconds, s-seconds, m-minutes; the default being milliseconds).

SCXML-21

Added:
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java   (with props)
Modified:
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/Send.java
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/test/StandaloneUtils.java

Added: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java?view=auto&rev=464965
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java (added)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java Tue Oct 17 10:07:55 2006
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.scxml.env;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.scxml.EventDispatcher;
+import org.apache.commons.scxml.SCXMLExecutor;
+import org.apache.commons.scxml.SCXMLHelper;
+import org.apache.commons.scxml.TriggerEvent;
+import org.apache.commons.scxml.model.ModelException;
+
+/**
+ * <p>EventDispatcher implementation that can schedule <code>delay</code>ed
+ * &lt;send&gt; events for the &quot;scxml&quot; <code>targettype</code>
+ * attribute value (which is also the default). This implementation uses
+ * J2SE <code>Timer</code>s.</p>
+ *
+ * <p>No other <code>targettype</code>s are processed. Subclasses may support
+ * additional <code>targettype</code>s by overriding the
+ * <code>send(...)</code> and <code>cancel(...)</code> methods and
+ * delegating to their <code>super</code> counterparts for the
+ * &quot;scxml&quot; <code>targettype</code>.</p>
+ *
+ */
+public class SimpleScheduler implements EventDispatcher, Serializable {
+
+    /** Log instance. */
+    private Log log = LogFactory.getLog(SimpleScheduler.class);
+
+    /**
+     * The <code>Map</code> of active <code>Timer</code>s, keyed by
+     * &lt;send&gt; element <code>id</code>s.
+     */
+    private Map timers;
+
+    /**
+     * The state chart execution instance we schedule events for.
+     */
+    private SCXMLExecutor executor;
+
+    /**
+     * Constructor.
+     *
+     * @param executor The owning {@link SCXMLExecutor} instance.
+     */
+    public SimpleScheduler(final SCXMLExecutor executor) {
+        super();
+        this.executor = executor;
+        this.timers = Collections.synchronizedMap(new HashMap());
+    }
+
+    /**
+     * @see EventDispatcher#cancel(String)
+     */
+    public void cancel(final String sendId) {
+        // Log callback
+        if (log.isInfoEnabled()) {
+            log.info("cancel( sendId: " + sendId + ")");
+        }
+        if (!timers.containsKey(sendId)) {
+            return; // done, we don't track this one or its already expired
+        }
+        Timer timer = (Timer) timers.get(sendId);
+        if (timer != null) {
+            timer.cancel();
+            if (log.isDebugEnabled()) {
+                log.debug("Cancelled event scheduled by <send> with id '"
+                    + sendId + "'");
+            }
+        }
+        timers.remove(sendId);
+    }
+
+    /**
+    @see EventDispatcher#send(String,String,String,String,Map,Object,long,List)
+     */
+    public void send(final String sendId, final String target,
+            final String targettype, final String event, final Map params,
+            final Object hints, final long delay, final List externalNodes) {
+        // Log callback
+        if (log.isInfoEnabled()) {
+            StringBuffer buf = new StringBuffer();
+            buf.append("send ( sendId: ").append(sendId);
+            buf.append(", target: ").append(target);
+            buf.append(", targetType: ").append(targettype);
+            buf.append(", event: ").append(event);
+            buf.append(", params: ").append(String.valueOf(params));
+            buf.append(", hints: ").append(String.valueOf(hints));
+            buf.append(", delay: ").append(delay);
+            buf.append(')');
+            log.info(buf.toString());
+        }
+
+        // We only handle the "scxml" targettype (which is the default too)
+        if (SCXMLHelper.isStringEmpty(targettype)
+                || targettype.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) {
+
+            if (!SCXMLHelper.isStringEmpty(target)) {
+                // We know of no other target
+                if (log.isWarnEnabled()) {
+                    log.warn("<send>: Unavailable target - " + target);
+                }
+                try {
+                    this.executor.triggerEvent(new TriggerEvent(
+                        EVENT_ERR_SEND_TARGETUNAVAILABLE,
+                        TriggerEvent.ERROR_EVENT));
+                } catch (ModelException me) {
+                    log.error(me.getMessage(), me);
+                }
+                return; // done
+            }
+
+            if (delay > 0L) {
+                // Need to schedule this one
+                Timer timer = new Timer(true);
+                timer.schedule(new DelayedEventTask(sendId, event), delay);
+                timers.put(sendId, timer);
+                if (log.isDebugEnabled()) {
+                    log.debug("Scheduled event '" + event + "' with delay "
+                        + delay + "ms, as specified by <send> with id '"
+                        + sendId + "'");
+                }
+            }
+            // else short-circuited by Send#execute()
+            // TODO: Pass through in v1.0
+
+        }
+
+    }
+
+    /**
+     * Get the log instance.
+     *
+     * @return The current log instance
+     */
+    protected Log getLog() {
+        return log;
+    }
+
+    /**
+     * Get the current timers.
+     *
+     * @return The currently scheduled timers
+     */
+    protected Map getTimers() {
+        return timers;
+    }
+
+    /**
+     * Get the executor we're attached to.
+     *
+     * @return The owning executor instance
+     */
+    protected SCXMLExecutor getExecutor() {
+        return executor;
+    }
+
+    /**
+     * TimerTask implementation.
+     */
+    class DelayedEventTask extends TimerTask {
+
+        /**
+         * The ID of the &lt;send&gt; element.
+         */
+        private String sendId;
+
+        /**
+         * The event name.
+         */
+        private String event;
+
+        /**
+         * Constructor.
+         *
+         * @param sendId The ID of the send element.
+         * @param event The name of the event to be triggered.
+         */
+        DelayedEventTask(final String sendId, final String event) {
+            super();
+            this.sendId = sendId;
+            this.event = event;
+        }
+
+        /**
+         * What to do when timer expires.
+         */
+        public void run() {
+            try {
+                executor.triggerEvent(new TriggerEvent(event,
+                    TriggerEvent.SIGNAL_EVENT));
+            } catch (ModelException me) {
+                log.error(me.getMessage(), me);
+            }
+            timers.remove(sendId);
+            if (log.isDebugEnabled()) {
+                log.debug("Fired event '" + event + "' as scheduled by "
+                    + "<send> with id '" + sendId + "'");
+            }
+        }
+
+    }
+
+    /**
+     * The default targettype.
+     */
+    private static final String TARGETTYPE_SCXML = "scxml";
+
+    /**
+     * The spec mandated derived event when target cannot be reached.
+     */
+    private static final String EVENT_ERR_SEND_TARGETUNAVAILABLE =
+        "error.send.targetunavailable";
+
+}
+

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/SimpleScheduler.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/Send.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/Send.java?view=diff&rev=464965&r1=464964&r2=464965
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/Send.java (original)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/Send.java Tue Oct 17 10:07:55 2006
@@ -265,23 +265,7 @@
             final ErrorReporter errRep, final SCInstance scInstance,
             final Log appLog, final Collection derivedEvents)
     throws ModelException, SCXMLExpressionException {
-        // Lets see if we should handle it ourselves
-        if (targettype != null && targettype.trim().toLowerCase().
-                equals(TARGETTYPE_SCXML)) {
-            if (SCXMLHelper.isStringEmpty(target)) {
-                derivedEvents.add(new TriggerEvent(event,
-                    TriggerEvent.SIGNAL_EVENT));
-            } else {
-                // We know of no other
-                appLog.warn("<send>: Unavailable target - " + target);
-                derivedEvents.add(new TriggerEvent(
-                    EVENT_ERR_SEND_TARGETUNAVAILABLE,
-                    TriggerEvent.ERROR_EVENT));
-            }
-            // short-circuit the EventDispatcher
-            return;
-        }
-        // Else, let the EventDispatcher take care of it
+        // Send attributes evaluation
         State parentState = getParentState();
         Context ctx = scInstance.getContext(parentState);
         Evaluator eval = scInstance.getEvaluator();
@@ -304,18 +288,87 @@
                 params.put(varName, varObj);
             }
         }
+        long wait = parseDelay(appLog);
+        // Lets see if we should handle it ourselves
+        if (SCXMLHelper.isStringEmpty(targettype)
+                || targettype.trim().equalsIgnoreCase(TARGETTYPE_SCXML)) {
+            if (SCXMLHelper.isStringEmpty(target)) {
+                // TODO: Remove both short-circuit passes in v1.0
+                if (wait == 0L) {
+                    derivedEvents.add(new TriggerEvent(event,
+                        TriggerEvent.SIGNAL_EVENT));
+                    return;
+                }
+            } else {
+                // We know of no other
+                appLog.warn("<send>: Unavailable target - " + target);
+                derivedEvents.add(new TriggerEvent(
+                    EVENT_ERR_SEND_TARGETUNAVAILABLE,
+                    TriggerEvent.ERROR_EVENT));
+                // short-circuit the EventDispatcher
+                return;
+            }
+        }
+        // Else, let the EventDispatcher take care of it
+        evtDispatcher.send(sendid, target, targettype, event, params,
+            hintsValue, wait, externalNodes);
+    }
+
+    /**
+     * Parse delay.
+     *
+     * @param appLog The application log
+     * @return The parsed delay in milliseconds
+     * @throws SCXMLExpressionException If the delay cannot be parsed
+     */
+    private long parseDelay(final Log appLog)
+    throws SCXMLExpressionException {
+
         long wait = 0L;
-        if (delay != null && delay.length() > 0) {
+        long multiplier = 1L;
+
+        if (!SCXMLHelper.isStringEmpty(delay)) {
+
+            String trimDelay = delay.trim();
+            String numericDelay = trimDelay;
+            if (trimDelay.endsWith(MILLIS)) {
+                numericDelay = trimDelay.substring(0, trimDelay.length() - 2);
+            } else if (trimDelay.endsWith(SECONDS)) {
+                multiplier = MILLIS_IN_A_SECOND;
+                numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
+            } else if (trimDelay.endsWith(MINUTES)) {
+                multiplier = MILLIS_IN_A_MINUTE;
+                numericDelay = trimDelay.substring(0, trimDelay.length() - 1);
+            }
+
             try {
-                wait = Long.parseLong(delay.trim());
+                wait = Long.parseLong(numericDelay);
             } catch (NumberFormatException nfe) {
-                appLog.warn("Could not parse delay for <send>, "
-                    + "it will be treated as immediate", nfe);
+                appLog.error(nfe.getMessage(), nfe);
+                throw new SCXMLExpressionException(nfe.getMessage(), nfe);
             }
+            wait *= multiplier;
+
         }
-        evtDispatcher.send(sendid, target, targettype, event, params,
-            hintsValue, wait, externalNodes);
+
+        return wait;
+
     }
+
+    /** The suffix in the delay string for milliseconds. */
+    private static final String MILLIS = "ms";
+
+    /** The suffix in the delay string for seconds. */
+    private static final String SECONDS = "s";
+
+    /** The suffix in the delay string for minutes. */
+    private static final String MINUTES = "m";
+
+    /** The number of milliseconds in a second. */
+    private static final long MILLIS_IN_A_SECOND = 1000L;
+
+    /** The number of milliseconds in a minute. */
+    private static final long MILLIS_IN_A_MINUTE = 60000L;
 
 }
 

Modified: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/test/StandaloneUtils.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/test/StandaloneUtils.java?view=diff&rev=464965&r1=464964&r2=464965
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/test/StandaloneUtils.java (original)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/test/StandaloneUtils.java Tue Oct 17 10:07:55 2006
@@ -29,7 +29,7 @@
 import org.apache.commons.scxml.SCXMLExecutor;
 import org.apache.commons.scxml.SCXMLHelper;
 import org.apache.commons.scxml.TriggerEvent;
-import org.apache.commons.scxml.env.SimpleDispatcher;
+import org.apache.commons.scxml.env.SimpleScheduler;
 import org.apache.commons.scxml.env.Tracer;
 import org.apache.commons.scxml.invoke.SimpleSCXMLInvoker;
 import org.apache.commons.scxml.io.SCXMLDigester;
@@ -75,7 +75,6 @@
         try {
             String documentURI = getCanonicalURI(uri);
             Context rootCtx = evaluator.newContext(null);
-            EventDispatcher ed = new SimpleDispatcher();
             Tracer trc = new Tracer();
             SCXML doc = SCXMLDigester.digest(new URL(documentURI), trc);
             if (doc == null) {
@@ -84,11 +83,13 @@
                 System.exit(-1);
             }
             System.out.println(SCXMLSerializer.serialize(doc));
-            SCXMLExecutor exec = new SCXMLExecutor(evaluator, ed, trc);
+            SCXMLExecutor exec = new SCXMLExecutor(evaluator, null, trc);
+            EventDispatcher ed = new SimpleScheduler(exec);
+            exec.setEventdispatcher(ed);
+            exec.setStateMachine(doc);
             exec.addListener(doc, trc);
             exec.registerInvokerClass("scxml", SimpleSCXMLInvoker.class);
             exec.setRootContext(rootCtx);
-            exec.setStateMachine(doc);
             exec.go();
             BufferedReader br = new BufferedReader(new
                 InputStreamReader(System.in));



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org