You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/11/27 08:10:27 UTC

svn commit: r349187 [2/6] - in /struts/flow/trunk: ./ src/examples/WEB-INF/ src/examples/WEB-INF/guess/ src/examples/WEB-INF/portlet/ src/examples/WEB-INF/remote/ src/examples/remote/ src/java/ src/java/org/apache/struts/flow/ src/java/org/apache/strut...

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/ContinuationsManagerImpl.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/ContinuationsManagerImpl.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/ContinuationsManagerImpl.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/ContinuationsManagerImpl.java Sat Nov 26 23:10:08 2005
@@ -1,21 +1,22 @@
 /*
- *  Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2004 The Apache Software Foundation.
  *
- *  Licensed 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
+ * Licensed 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
+ *      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.
+ * 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.struts.flow.core;
 
 import java.security.SecureRandom;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,79 +27,109 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.commons.chain.web.WebContext;
+import org.apache.commons.chain.web.servlet.ServletWebContext;
+
+//import org.apache.avalon.framework.configuration.Configurable;
+//import org.apache.avalon.framework.configuration.Configuration;
+//import org.apache.avalon.framework.context.Context;
+//import org.apache.avalon.framework.context.ContextException;
+//import org.apache.avalon.framework.context.Contextualizable;
+//import org.apache.avalon.framework.logger.AbstractLogEnabled;
+//import org.apache.avalon.framework.service.ServiceException;
+//import org.apache.avalon.framework.service.ServiceManager;
+//import org.apache.avalon.framework.service.Serviceable;
+//import org.apache.avalon.framework.thread.ThreadSafe;
+//import org.apache.cocoon.components.ContextHelper;
+//import org.apache.cocoon.components.thread.RunnableManager;
+//import org.apache.cocoon.environment.ObjectModelHelper;
+//import org.apache.cocoon.environment.Request;
+//import org.apache.cocoon.environment.Session;
+
+
 /**
- *  The default implementation of {@link ContinuationsManager}.
- *
- *@author     <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
- *@author     <a href="mailto:Michael.Melhem@managesoft.com">Michael Melhem</a>
- *@since      March 19, 2002
- *@see        ContinuationsManager
- *@version    CVS $Id: ContinuationsManagerImpl.java,v 1.3 2004/06/02 21:56:54
- *      mrdon Exp $
+ * The default implementation of {@link ContinuationsManager}. <br/>There are
+ * two modes of work: <br/>
+ * <ul>
+ * <li><b>standard mode </b>- continuations are stored in single holder. No
+ * security is applied to continuation lookup. Anyone can invoke a continuation
+ * only knowing the ID. Set "session-bound-continuations" configuration option
+ * to false to activate this mode.</li>
+ * <li><b>secure mode </b>- each session has it's own continuations holder. A
+ * continuation is only valid for the same session it was created for. Session
+ * invalidation causes all bound continuations to be invalidated as well. Use
+ * this setting for web applications. Set "session-bound-continuations"
+ * configuration option to true to activate this mode.</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu </a>
+ * @author <a href="mailto:Michael.Melhem@managesoft.com">Michael Melhem </a>
+ * @since March 19, 2002
+ * @see ContinuationsManager
+ * @version CVS $Id: ContinuationsManagerImpl.java 293111 2005-10-02 13:39:20Z reinhard $
  */
-public class ContinuationsManagerImpl
-         implements ContinuationsManager {
-
-    private final static int CONTINUATION_ID_LENGTH = 20;
+public class ContinuationsManagerImpl implements ContinuationsManager  {
 
-    /**  Random object for creating continuation ids */
-    protected SecureRandom random = null;
+    static final int CONTINUATION_ID_LENGTH = 20;
+    static final String EXPIRE_CONTINUATIONS = "expire-continuations";
 
     /**
-     *  How long a continuation exists in memory since the last access. The
-     *  time is in miliseconds, and the default is 1 hour.
+     * Random number generator used to create continuation ID
      */
-    protected int defaultTimeToLive = 1 * 60 * 60 * 1000;
-
-    /**  Maintains the forrest of <code>WebContinuation</code> trees. */
-    protected Set forrest = Collections.synchronizedSet(new HashSet());
+    protected SecureRandom random;
+    protected byte[] bytes;
 
     /**
-     *  Association between <code>WebContinuation</code> ids and the
-     *  corresponding <code>WebContinuation</code> object.
+     * How long does a continuation exist in memory since the last
+     * access? The time is in miliseconds, and the default is 1 hour.
      */
-    protected Map idToWebCont = Collections.synchronizedMap(new HashMap());
+    protected int defaultTimeToLive = 3600 * 1000;
 
     /**
-     *  Sorted set of <code>WebContinuation</code> instances, based on their
-     *  expiration time. This is used by the background thread to invalidate
-     *  continuations.
+     * Maintains the forest of <code>WebContinuation</code> trees.
+     * This set is used only for debugging puroses by
+     * {@link #displayAllContinuations()} method.
      */
-    protected SortedSet expirations = Collections.synchronizedSortedSet(new TreeSet());
-
-    private Thread expireThread;
+    protected Set forest = Collections.synchronizedSet(new HashSet());
 
     /**
-     *  Gets the logger 
-     *
-     *@return    The logger value
+     * Main continuations holder. Used unless continuations are stored in user
+     * session.
      */
-    public Logger getLogger() {
-        return Factory.getLogger();
-    }
-
-
+    protected WebContinuationsHolder continuationsHolder;
+    
     /**
-     *  Constructor for the ContinuationsManagerImpl object.  Initializes the random
-     *  seed for continuation ids and starts the expiration thread.
-     *
-     *@exception  Exception  If anything goes wrong
+     * Sorted set of <code>WebContinuation</code> instances, based on
+     * their expiration time. This is used by the background thread to
+     * invalidate continuations.
      */
+    protected SortedSet expirations = Collections.synchronizedSortedSet(new TreeSet());
+
+    protected boolean bindContinuationsToSession;
+
+    private Thread expireThread;
+    
+    private long expirePeriod = 180000;
+
     public ContinuationsManagerImpl() throws Exception {
         try {
             random = SecureRandom.getInstance("SHA1PRNG");
-        } catch (java.security.NoSuchAlgorithmException nsae) {
-            // maybe we are on IBM's SDK
+        } catch(java.security.NoSuchAlgorithmException nsae) {
+            // Maybe we are on IBM's SDK
             random = SecureRandom.getInstance("IBMSecureRandom");
         }
         random.setSeed(System.currentTimeMillis());
+        bytes = new byte[CONTINUATION_ID_LENGTH];
         expireThread = new Thread(
             new Runnable() {
                 public void run() {
                     boolean shouldKeepRunning = true;
                     while (shouldKeepRunning) {
                         try {
-                            Thread.sleep(60 * 1000);
+                            Thread.sleep(expirePeriod);
                         } catch (InterruptedException ex) {
                             getLogger().debug("Continuation expiration thread interrupted");
                             shouldKeepRunning = false;
@@ -114,9 +145,21 @@
         expireThread.setName("Flow continuations expiration thread");
         expireThread.setPriority(Thread.MIN_PRIORITY);
         expireThread.start();
+        
+        this.continuationsHolder = new WebContinuationsHolder();
     }
-
-
+    
+    
+    /**
+     *  Gets the logger 
+     *
+     *@return    The logger value
+     */
+    public Logger getLogger() {
+        return Factory.getLogger();
+    }
+    
+    
     /**
      *  Set the default time to live value
      *
@@ -125,149 +168,130 @@
     public void setDefaultTimeToLive(int ttl) {
         this.defaultTimeToLive = ttl;
     }
+    
+    public void setExpirationPeriod(long period) {
+        this.expirePeriod = period;
+    }
+    
+    public void setBindContinuationsToSession(boolean bind) {
+        this.bindContinuationsToSession = bind;
+        if (this.bindContinuationsToSession) {
+            this.continuationsHolder = null;
+        }
+    }
 
 
-    /**
-     *  Create a <code>WebContinuation</code> object given a native continuation
-     *  object and its parent. If the parent continuation is null, the <code>WebContinuation</code>
-     *  returned becomes the root of a tree in the forrest.
-     *
-     *@param  kont        an <code>Object</code> value
-     *@param  parent  a <code>WebContinuation</code> value
-     *@param  timeToLive  an <code>int</code> value indicating how long in
-     *      seconds this continuation will live in the server if not accessed
-     *@param  disposer    a <code>ContinuationsDisposer</code> instance to
-     *      called when the continuation gets cleaned up.
-     *@return             a <code>WebContinuation</code> value
-     *@see                WebContinuation
-     */
     public WebContinuation createWebContinuation(Object kont,
-            WebContinuation parent,
-            int timeToLive,
-            ContinuationsDisposer disposer) {
+                                                 WebContinuation parent,
+                                                 int timeToLive,
+                                                 String interpreterId, 
+                                                 ContinuationsDisposer disposer,
+                                                 WebContext webctx) {
         int ttl = (timeToLive == 0 ? defaultTimeToLive : timeToLive);
 
-        WebContinuation wk = generateContinuation(kont, parent, ttl, disposer);
+        WebContinuation wk = generateContinuation(kont, parent, ttl, interpreterId, disposer, webctx);
+        //wk.enableLogging(getLogger());
 
         if (parent == null) {
-            forrest.add(wk);
-        }
-
-        // REVISIT: This Places only the "leaf" nodes in the expirations Sorted Set.
-        // do we really want to do this?
-        if (parent != null) {
-            if (wk.getParentContinuation().getChildren().size() < 2) {
-                expirations.remove(wk.getParentContinuation());
-            }
+            forest.add(wk);
+        } else {
+            handleParentContinuationExpiration(parent);
         }
 
-        expirations.add(wk);
-
-        // No need to add the WebContinuation in idToWebCont as it was
-        // already done during its construction.
+        handleLeafContinuationExpiration(wk);
 
         if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK: Just Created New Continuation " + wk.getId());
+            getLogger().debug("WK: Created continuation " + wk.getId());
         }
 
         return wk;
     }
-
-
+    
     /**
-     *  Invalidates a <code>WebContinuation</code>. This effectively means that
-     *  the continuation object associated with it will no longer be accessible
-     *  from Web pages. Invalidating a <code>WebContinuation</code> invalidates
-     *  all the <code>WebContinuation</code>s which are children of it.
-     *
-     *@param  wk  a <code>WebContinuation</code> value
+     * When a new continuation is created in @link #createWebContinuation(Object, WebContinuation, int, String, ContinuationsDisposer),
+     * it is registered in the expiration set in order to be evaluated by the invalidation mechanism.
      */
-    public void invalidateWebContinuation(WebContinuation wk) {
-        WebContinuation parent = wk.getParentContinuation();
-        if (parent == null) {
-            forrest.remove(wk);
-        } else {
-            List parentKids = parent.getChildren();
-            parentKids.remove(wk);
-        }
-
-        _invalidate(wk);
+    protected void handleLeafContinuationExpiration(WebContinuation wk) {
+        expirations.add(wk);
     }
 
-
-    private void _invalidate(WebContinuation wk) {
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK: Manual Expire of Continuation " + wk.getId());
-        }
-        disposeContinuation(wk);
-        expirations.remove(wk);
-
-        // Invalidate all the children continuations as well
-        List children = wk.getChildren();
-        int size = children.size();
-        for (int i = 0; i < size; i++) {
-            _invalidate((WebContinuation) children.get(i));
+    /**
+     * When a new continuation is created in @link #createWebContinuation(Object, WebContinuation, int, String, ContinuationsDisposer),
+     * its parent continuation is removed from the expiration set. This way only leaf continuations are part of
+     * the expiration set.
+     */
+    protected void handleParentContinuationExpiration(WebContinuation parent) {
+        if (parent.getChildren().size() < 2) {
+            expirations.remove(parent);
         }
-    }
-
-
+    }    
+    
     /**
-     *  Makes the continuation inaccessible for lookup, and triggers possible
-     *  needed cleanup code through the ContinuationsDisposer interface.
-     *
-     *@param  wk  the continuation to dispose.
+     * Get a list of all web continuations (data only)
      */
-    private void disposeContinuation(WebContinuation wk) {
-        idToWebCont.remove(wk.getId());
-
-        // Call specific possible implementation-specific clean-up on this continuation.
-        ContinuationsDisposer disposer = wk.getDisposer();
-        if (disposer != null) {
-            disposer.disposeContinuation(wk);
+    public List getWebContinuationsDataBeanList() {
+        List beanList = new ArrayList();
+        for(Iterator it = this.forest.iterator(); it.hasNext();) {
+            beanList.add(new WebContinuationDataBean((WebContinuation) it.next()));
         }
+        return beanList;
     }
 
-
-    /**
-     *  Given a <code>WebContinuation</code> id, retrieve the associated <code>WebContinuation</code>
-     *  object.
-     *
-     *@param  id  a <code>String</code> value
-     *@return     a <code>WebContinuation</code> object, or null if no such
-     *      <code>WebContinuation</code> could be found.
-     */
-    public WebContinuation lookupWebContinuation(String id) {
-        // REVISIT: Is the folliwing check needed to avoid threading issues:
+    public WebContinuation lookupWebContinuation(String id, String interpreterId, WebContext webctx) {
+        // REVISIT: Is the following check needed to avoid threading issues:
         // return wk only if !(wk.hasExpired) ?
-        return (WebContinuation) idToWebCont.get(id);
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(false, webctx);
+        if (continuationsHolder == null)
+            return null;
+        
+        WebContinuation kont = continuationsHolder.get(id);
+        if (kont == null)
+            return null;
+            
+        if (!kont.interpreterMatches(interpreterId)) {
+            getLogger().error(
+                    "WK: Continuation (" + kont.getId()
+                            + ") lookup for wrong interpreter. Bound to: "
+                            + kont.getInterpreterId() + ", looked up for: "
+                            + interpreterId);
+            return null;
+        }
+        return kont;
     }
 
-
     /**
-     *  Create <code>WebContinuation</code> and generate unique identifier for
-     *  it. The identifier is generated using a cryptographically strong
-     *  algorithm to prevent people to generate their own identifiers. <p>
-     *
-     *  It has the side effect of interning the continuation object in the
-     *  <code>idToWebCont</code> hash table.
-     *
-     *@param  kont      an <code>Object</code> value representing continuation
-     *@param  parent    value representing parent <code>WebContinuation</code>
-     *@param  ttl       <code>WebContinuation</code> time to live
-     *@param  disposer  <code>ContinuationsDisposer</code> instance to use for
-     *      cleanup of the continuation.
-     *@return           the generated <code>WebContinuation</code> with unique
-     *      identifier
-     */
-    private WebContinuation generateContinuation(Object kont,
-            WebContinuation parent,
-            int ttl,
-            ContinuationsDisposer disposer) {
+     * Create <code>WebContinuation</code> and generate unique identifier for
+     * it. The identifier is generated using a cryptographically strong
+     * algorithm to prevent people to generate their own identifiers.
+     * 
+     * <p>
+     * It has the side effect of interning the continuation object in the
+     * <code>idToWebCont</code> hash table.
+     * 
+     * @param kont
+     *            an <code>Object</code> value representing continuation
+     * @param parent
+     *            value representing parent <code>WebContinuation</code>
+     * @param ttl
+     *            <code>WebContinuation</code> time to live
+     * @param interpreterId
+     *            id of interpreter invoking continuation creation
+     * @param disposer
+     *            <code>ContinuationsDisposer</code> instance to use for
+     *            cleanup of the continuation.
+     * @return the generated <code>WebContinuation</code> with unique
+     *         identifier
+     */
+    protected WebContinuation generateContinuation(Object kont,
+                                                 WebContinuation parent,
+                                                 int ttl,
+                                                 String interpreterId,
+                                                 ContinuationsDisposer disposer,
+                                                 WebContext webctx) {
 
-        byte[] bytes = new byte[CONTINUATION_ID_LENGTH];
         char[] result = new char[bytes.length * 2];
         WebContinuation wk = null;
-
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(true, webctx);
         while (true) {
             random.nextBytes(bytes);
 
@@ -277,11 +301,17 @@
                 result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f), 16);
             }
 
-            String id = new String(result);
-            synchronized (idToWebCont) {
-                if (!idToWebCont.containsKey(id)) {
-                    wk = new WebContinuation(id, kont, parent, ttl, disposer);
-                    idToWebCont.put(id, wk);
+            final String id = new String(result);
+            synchronized (continuationsHolder) {
+                if (!continuationsHolder.contains(id)) {
+                    if (this.bindContinuationsToSession)
+                        wk = new HolderAwareWebContinuation(id, kont, parent,
+                                ttl, interpreterId, disposer,
+                                continuationsHolder);
+                    else
+                        wk = new WebContinuation(id, kont, parent, ttl,
+                                interpreterId, disposer);
+                    continuationsHolder.addContinuation(wk);
                     break;
                 }
             }
@@ -290,49 +320,97 @@
         return wk;
     }
 
+    public void invalidateWebContinuation(WebContinuation wk, WebContext webctx) {
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(false, webctx);
+        if (!continuationsHolder.contains(wk)) {
+            //TODO this looks like a security breach - should we throw?
+            return;
+        }
+        _detach(wk);
+        _invalidate(continuationsHolder, wk);
+    }
+
+    private void _invalidate(WebContinuationsHolder continuationsHolder, WebContinuation wk) {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("WK: Manual expire of continuation " + wk.getId());
+        }
+        disposeContinuation(continuationsHolder, wk);
+        expirations.remove(wk);
+
+        // Invalidate all the children continuations as well
+        List children = wk.getChildren();
+        int size = children.size();
+        for (int i = 0; i < size; i++) {
+            _invalidate(continuationsHolder, (WebContinuation) children.get(i));
+        }
+    }
+
+    /**
+     * Detach this continuation from parent. This method removes
+     * continuation from {@link #forest} set, or, if it has parent,
+     * from parent's children collection.
+     * @param continuationsHolder
+     * @param wk Continuation to detach from parent.
+     */
+    protected void _detach(WebContinuation wk) {
+        WebContinuation parent = wk.getParentContinuation();
+        if (parent == null) {
+            forest.remove(wk);
+        } else 
+            wk.detachFromParent();
+    }
 
     /**
-     *  Removes an expired leaf <code>WebContinuation</code> node from its
-     *  continuation tree, and recursively removes its parent(s) if it they have
-     *  expired and have no (other) children.
+     * Makes the continuation inaccessible for lookup, and triggers possible needed
+     * cleanup code through the ContinuationsDisposer interface.
+     * @param continuationsHolder
      *
-     *@param  wk  <code>WebContinuation</code> node
+     * @param wk the continuation to dispose.
      */
-    private void removeContinuation(WebContinuation wk) {
+    protected void disposeContinuation(WebContinuationsHolder continuationsHolder, WebContinuation wk) {
+        continuationsHolder.removeContinuation(wk);
+        wk.dispose();
+    }
+
+    /**
+     * Removes an expired leaf <code>WebContinuation</code> node
+     * from its continuation tree, and recursively removes its
+     * parent(s) if it they have expired and have no (other) children.
+     * @param continuationsHolder
+     *
+     * @param wk <code>WebContinuation</code> node
+     */
+    protected void removeContinuation(WebContinuationsHolder continuationsHolder,
+            WebContinuation wk) {
         if (wk.getChildren().size() != 0) {
             return;
         }
 
         // remove access to this contination
-        disposeContinuation(wk);
-
-        WebContinuation parent = wk.getParentContinuation();
-        if (parent == null) {
-            forrest.remove(wk);
-        } else {
-            List parentKids = parent.getChildren();
-            parentKids.remove(wk);
-        }
+        disposeContinuation(continuationsHolder, wk);
+        _detach(wk);
 
         if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK: deleted this WK: " + wk.getId());
+            getLogger().debug("WK: Deleted continuation: " + wk.getId());
         }
 
         // now check if parent needs to be removed.
+        WebContinuation parent = wk.getParentContinuation();
         if (null != parent && parent.hasExpired()) {
-            removeContinuation(parent);
+            //parent must have the same continuations holder, lookup not needed
+            removeContinuation(continuationsHolder, parent);
         }
     }
 
-
     /**
-     *  Dump to Log file the current contents of the expirations <code>SortedSet</code>
+     * Dump to Log file the current contents of
+     * the expirations <code>SortedSet</code>
      */
-    private void displayExpireSet() {
-        Iterator iter = expirations.iterator();
-        StringBuffer wkSet = new StringBuffer("\nWK; Expire Set Size: " + expirations.size());
-        while (iter.hasNext()) {
-            final WebContinuation wk = (WebContinuation) iter.next();
+    protected void displayExpireSet() {
+        StringBuffer wkSet = new StringBuffer("\nWK; Expire set size: " + expirations.size());
+        Iterator i = expirations.iterator();
+        while (i.hasNext()) {
+            final WebContinuation wk = (WebContinuation) i.next();
             final long lat = wk.getLastAccessTime() + wk.getTimeToLive();
             wkSet.append("\nWK: ")
                     .append(wk.getId())
@@ -349,59 +427,194 @@
         getLogger().debug(wkSet.toString());
     }
 
-
     /**
-     *  Dump to Log file all <code>WebContinuation</code>s in the system
+     * Dump to Log file all <code>WebContinuation</code>s
+     * in the system
      */
     public void displayAllContinuations() {
-        Iterator iter = forrest.iterator();
-        while (iter.hasNext()) {
-            ((WebContinuation) iter.next()).display();
+        final Iterator i = forest.iterator();
+        while (i.hasNext()) {
+            ((WebContinuation) i.next()).display();
         }
     }
 
+    /**
+     * Remove all continuations which have already expired.
+     */
+    protected void expireContinuations() {
+        long now = 0;
+        if (getLogger().isDebugEnabled()) {
+            now = System.currentTimeMillis();
 
-    /**  Destroys all continuations and any other resident objects  */
-    public void destroy() {
-        expirations.clear();
-        Set clone = new HashSet(forrest);
-        for (Iterator i = clone.iterator(); i.hasNext(); ) {
-            removeContinuation((WebContinuation) i.next());
+            /* Continuations before clean up:
+            getLogger().debug("WK: Forest before cleanup: " + forest.size());
+            displayAllContinuations();
+            displayExpireSet();
+            */
         }
 
-        if (expireThread != null && expireThread.isAlive()) {
-            expireThread.interrupt();
+        // Clean up expired continuations
+        int count = 0;
+        WebContinuation wk;
+        Iterator i = expirations.iterator();
+        while (i.hasNext() && ((wk = (WebContinuation) i.next()).hasExpired())) {
+            i.remove();
+            WebContinuationsHolder continuationsHolder = null;
+            if ( wk instanceof HolderAwareWebContinuation )
+                continuationsHolder = ((HolderAwareWebContinuation) wk).getContinuationsHolder();
+            else
+                continuationsHolder = this.continuationsHolder;
+            removeContinuation(continuationsHolder, wk);
+            count++;
         }
-    }
 
-
-    /**  Remove all continuations which have already expired */
-    private void expireContinuations() {
-        // log state before continuations clean up
         if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK: Forrest size: " + forrest.size());
+            getLogger().debug("WK Cleaned up " + count + " continuations in " +
+                              (System.currentTimeMillis() - now));
+
+            /* Continuations after clean up:
+            getLogger().debug("WK: Forest after cleanup: " + forest.size());
             displayAllContinuations();
             displayExpireSet();
+            */
         }
+    }
 
-        // clean up
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK CurrentSystemTime[" + System.currentTimeMillis() +
-                    "]: Cleaning up expired Continuations....");
+    /**
+     * Method used by WebContinuationsHolder to notify the continuations manager
+     * about session invalidation. Invalidates all continuations held by passed
+     * continuationsHolder.
+     */
+    protected void invalidateContinuations(
+            WebContinuationsHolder continuationsHolder) {
+        // TODO: this avoids ConcurrentModificationException, still this is not
+        // the best solution and should be changed
+        Object[] continuationIds = continuationsHolder.getContinuationIds()
+                .toArray();
+        
+        for (int i = 0; i < continuationIds.length; i++) {
+            WebContinuation wk = continuationsHolder.get(continuationIds[i]);
+            if (wk != null) {
+                _detach(wk);
+                _invalidate(continuationsHolder, wk);
+            }
         }
-        WebContinuation wk;
-        Iterator iter = expirations.iterator();
-        while (iter.hasNext() && ((wk = (WebContinuation) iter.next()).hasExpired())) {
-            iter.remove();
-            this.removeContinuation(wk);
+    }
+
+    /**
+     * Lookup a proper web continuations holder. 
+     * @param createNew
+     *            should the manager create a continuations holder in session
+     *            when none found?
+     */
+    public WebContinuationsHolder lookupWebContinuationsHolder(boolean createNew, WebContext webctx) {
+        //there is only one holder if continuations are not bound to session
+        if (!this.bindContinuationsToSession)
+            return this.continuationsHolder;
+        
+        //if continuations bound to session lookup a proper holder in the session
+        if (!createNew && webctx instanceof ServletWebContext) {
+            if (((ServletWebContext) webctx).getRequest().getSession(false) == null) {
+                return null;
+            }
         }
 
-        // log state after continuations clean up
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug("WK: Forrest size: " + forrest.size());
-            displayAllContinuations();
-            displayExpireSet();
+        WebContinuationsHolder holder = 
+            (WebContinuationsHolder) webctx.getSessionScope().get(
+                    WebContinuationsHolder.CONTINUATIONS_HOLDER);
+        if (!createNew)
+            return holder;
+
+        if (holder != null)
+            return holder;
+
+        holder = new WebContinuationsHolder();
+        webctx.getSessionScope().put(WebContinuationsHolder.CONTINUATIONS_HOLDER,
+                holder);
+        return holder;
+    }
+
+    /**
+     * A holder for WebContinuations. When bound to session notifies the
+     * continuations manager of session invalidation.
+     */
+    public class WebContinuationsHolder implements HttpSessionBindingListener {
+        private final static String CONTINUATIONS_HOLDER = 
+                                       "o.a.c.c.f.SCMI.WebContinuationsHolder";
+
+        private Map holder = Collections.synchronizedMap(new HashMap());
+
+        public WebContinuation get(Object id) {
+            return (WebContinuation) this.holder.get(id);
+        }
+
+        public void addContinuation(WebContinuation wk) {
+            this.holder.put(wk.getId(), wk);
+        }
+
+        public void removeContinuation(WebContinuation wk) {
+            this.holder.remove(wk.getId());
+        }
+
+        public Set getContinuationIds() {
+            return holder.keySet();
+        }
+        
+        public boolean contains(String continuationId) {
+            return this.holder.containsKey(continuationId);
+        }
+        
+        public boolean contains(WebContinuation wk) {
+            return contains(wk.getId());
+        }
+
+        public void valueBound(HttpSessionBindingEvent event) {
+        }
+
+        public void valueUnbound(HttpSessionBindingEvent event) {
+            invalidateContinuations(this);
+        }
+    }
+
+    /**
+     * WebContinuation extension that holds also the information about the
+     * holder. This information is needed to cleanup a proper holder after
+     * continuation's expiration time.
+     */
+    protected class HolderAwareWebContinuation extends WebContinuation {
+        private WebContinuationsHolder continuationsHolder;
+
+        public HolderAwareWebContinuation(String id, Object continuation,
+                WebContinuation parentContinuation, int timeToLive,
+                String interpreterId, ContinuationsDisposer disposer,
+                WebContinuationsHolder continuationsHolder) {
+            super(id, continuation, parentContinuation, timeToLive,
+                    interpreterId, disposer);
+            this.continuationsHolder = continuationsHolder;
+        }
+
+        public WebContinuationsHolder getContinuationsHolder() {
+            return continuationsHolder;
+        }
+
+        //retain comparation logic from parent
+        public int compareTo(Object other) {
+            return super.compareTo(other);
+        }
+    }
+    
+    
+    /**  Destroys all continuations and any other resident objects  */
+    public void destroy() {
+        /*expirations.clear();
+        Set clone = new HashSet(forest);
+        for (Iterator i = clone.iterator(); i.hasNext(); ) {
+            removeContinuation((WebContinuation) i.next());
+        }
+        */
+        if (expireThread != null && expireThread.isAlive()) {
+            expireThread.interrupt();
         }
     }
-}
 
+}

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultCallVariableRegistrar.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultCallVariableRegistrar.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultCallVariableRegistrar.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultCallVariableRegistrar.java Sat Nov 26 23:10:08 2005
@@ -16,7 +16,7 @@
 package org.apache.struts.flow.core;
 
 import org.mozilla.javascript.Scriptable;
-import org.apache.commons.chain.Context;
+import org.apache.commons.chain.web.WebContext;
 import java.lang.reflect.Constructor;
 
 /**
@@ -64,11 +64,11 @@
      *      static variable
      *@return        The instance value
      */
-    public Object getInstance(Scriptable scope, Context ctx) {
+    public Object getInstance(Scriptable scope, WebContext ctx) {
         try {
             Constructor c = null;
             try {
-                c = variableClass.getConstructor(new Class[]{Context.class});
+                c = variableClass.getConstructor(new Class[]{WebContext.class});
             } catch (NoSuchMethodException ex) {
                 // ignored
             }

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultStaticVariableRegistrar.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultStaticVariableRegistrar.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultStaticVariableRegistrar.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/DefaultStaticVariableRegistrar.java Sat Nov 26 23:10:08 2005
@@ -16,7 +16,7 @@
 package org.apache.struts.flow.core;
 
 import org.mozilla.javascript.Scriptable;
-import org.apache.commons.chain.Context;
+import org.apache.commons.chain.web.WebContext;
 
 /**
  *  Defines a variable registrar used to define a static variable in the global
@@ -61,7 +61,7 @@
      *      static variable
      *@return        The instance value
      */
-    public Object getInstance(Scriptable scope, Context ctx) {
+    public Object getInstance(Scriptable scope, WebContext ctx) {
         return variable;
     }
 

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/Factory.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/Factory.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/Factory.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/Factory.java Sat Nov 26 23:10:08 2005
@@ -61,5 +61,6 @@
         }
         return continuationsManager;
     }
+    
 }
 

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowException.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowException.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowException.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowException.java Sat Nov 26 23:10:08 2005
@@ -1,122 +1,159 @@
 /*
- *  Copyright 1999-2004 The Apache Software Foundation.
- *
- *  Licensed 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.
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.struts.flow.core;
 
-import java.io.PrintStream;
-import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.struts.flow.core.location.LocatedException;
+import org.apache.struts.flow.core.location.LocatedRuntimeException;
+import org.apache.struts.flow.core.location.Location;
+import org.apache.struts.flow.core.location.MultiLocatable;
 
 /**
- *  This Exception wraps any exceptions thrown by Flow.
+ * This Exception is thrown every time there is a problem in processing
+ * a request.
+ *
+ * @author <a href="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
+ *         (Apache Software Foundation)
+ * @version CVS $Id: FlowException.java 280632 2005-09-13 19:35:46Z sylvain $
  */
-public class FlowException extends RuntimeException {
-
-    /**  The Throwable that caused this exception to be thrown.  */
-    private final Throwable throwable;
-
-
+public class FlowException extends LocatedRuntimeException implements MultiLocatable {
+    
     /**
-     *  Construct a new <code>FlowException</code> instance.
-     *
-     *@param  message  The detail message for this exception.
+     * Construct a new <code>FlowException</code> instance.
      */
-    public FlowException(final String message) {
-        this(message, null);
-    }
-
-
-    /**
-     *  Construct a new <code>FlowException</code> instance.
-     *
-     *@param  message    The detail message for this exception.
-     *@param  throwable  the root cause of the exception
-     */
-    public FlowException(final String message, final Throwable throwable) {
+    public FlowException(String message) {
         super(message);
-        this.throwable = throwable;
-    }
-
-
-    /**
-     *  Retrieve root cause of the exception.
-     *
-     *@return    the root cause
-     */
-    public final Throwable getCause() {
-        return throwable;
     }
-
-
+    
     /**
-     *  Creates a new <code>FlowException</code> instance.
+     * Creates a new <code>FlowException</code> instance.
      *
-     *@param  ex  an <code>Exception</code> value
+     * @param ex an <code>Exception</code> value
      */
     public FlowException(Exception ex) {
-        this(ex.getMessage(), ex);
+        super(ex.getMessage(), ex);
     }
-
-
+    
     /**
-     *  Gets a detailed log of the exception
-     *
-     *@return    The message
+     * Construct a new <code>FlowException</code> that references
+     * a parent Exception.
      */
-    public String toString() {
-        StringBuffer s = new StringBuffer();
-        s.append(super.toString());
-        final Throwable t = getCause();
-        if (t != null) {
-            s.append(t.toString());
+    public FlowException(String message, Throwable t) {
+        super(message, t);
+    }
+    
+    /**
+     * Construct a new <code>FlowException</code> that has an associated location.
+     */
+    public FlowException(String message, Location location) {
+        super(message, location);
+    }
+    
+    /**
+     * Construct a new <code>FlowException</code> that has a parent exception
+     * and an associated location.
+     * <p>
+     * This constructor is protected to enforce the use of {@link #throwLocated(String, Throwable, Location)}
+     * which limits exception nesting as far as possible.
+     */
+    protected FlowException(String message, Throwable t, Location location) {
+        super(message, t, location);
+    }
+    
+    /**
+     * Throw a located exception given an existing exception and the location where
+     * this exception was catched.
+     * <p>
+     * If the exception is already a <code>FlowException</code> or a {@link LocatedRuntimeException},
+     * the location is added to the original exception's location chain and the original exception
+     * is rethrown (<code>description</code> is ignored) to limit exception nesting. Otherwise, a new
+     * <code>FlowException</code> is thrown, wrapping the original exception.
+     * <p>
+     * Note: this method returns an exception as a convenience if you want to keep the <code>throw</code>
+     * semantics in the caller code, i.e. write<br>
+     * <code>&nbsp;&nbsp;throw FlowException.throwLocated(...);</code><br>
+     * instead of<br>
+     * <code>&nbsp;&nbsp;FlowException.throwLocated(...);</code><br>
+     * <code>&nbsp;&nbsp;return;</code>
+     * 
+     * @param message a message (can be <code>null</code>)
+     * @param thr the original exception (can be <code>null</code>)
+     * @param location the location (can be <code>null</code>)
+     * @return a (fake) located exception
+     * @throws FlowException or <code>LocatedRuntimeException</code>
+     */
+    public static FlowException throwLocated(String message, Throwable thr, Location location) throws FlowException {
+        if (thr instanceof FlowException) {
+            FlowException pe = (FlowException)thr;
+            pe.addLocation(location);
+            throw pe;
+
+        } else if (thr instanceof LocatedRuntimeException) {
+            LocatedRuntimeException re = (LocatedRuntimeException)thr;
+            re.addLocation(location);
+            // Rethrow
+            throw re;
         }
-        return s.toString();
+        
+        throw new FlowException(message, thr, location);
     }
-
-
-    /**  Prints the exception and its cause */
-    public void printStackTrace() {
-        super.printStackTrace();
-        if (getCause() != null) {
-            getCause().printStackTrace();
-        }
-    }
-
-
+    
     /**
-     *  Prints the exception and its cause
-     *
-     *@param  s  The stream to print to
-     */
-    public void printStackTrace(PrintStream s) {
-        super.printStackTrace(s);
-        if (getCause() != null) {
-            getCause().printStackTrace(s);
+     * Throw a located exception given an existing exception and the locations where
+     * this exception was catched.
+     * <p>
+     * If the exception is already a <code>FlowException</code> or a {@link LocatedRuntimeException},
+     * the locations are added to the original exception's location chain and the original exception
+     * is rethrown (<code>description</code> is ignored) to limit exception nesting. Otherwise, a new
+     * <code>FlowException</code> is thrown, wrapping the original exception.
+     * <p>
+     * Note: this method returns an exception as a convenience if you want to keep the <code>throw</code>
+     * semantics in the caller code, i.e. write<br>
+     * <code>&nbsp;&nbsp;throw FlowException.throwLocated(...);</code><br>
+     * instead of<br>
+     * <code>&nbsp;&nbsp;FlowException.throwLocated(...);</code><br>
+     * <code>&nbsp;&nbsp;return;</code>
+     * 
+     * @param message a message (can be <code>null</code>)
+     * @param thr the original exception (can be <code>null</code>)
+     * @param locations the locations (can be <code>null</code>)
+     * @return a (fake) located exception
+     * @throws FlowException or <code>LocatedRuntimeException</code>
+     */
+    public static FlowException throwLocated(String message, Throwable thr, List locations) throws FlowException {
+        MultiLocatable multiloc;
+        if (thr instanceof FlowException) {
+            multiloc = (FlowException)thr;
+        } else if (thr instanceof LocatedRuntimeException) {
+            multiloc = (LocatedRuntimeException)thr;
+        } else {
+            multiloc = new FlowException(message, thr);
         }
-    }
-
-
-    /**
-     *  Prints the exception and its cause
-     *
-     *@param  s  The writer to print to
-     */
-    public void printStackTrace(PrintWriter s) {
-        super.printStackTrace(s);
-        if (getCause() != null) {
-            getCause().printStackTrace(s);
+        
+        if (locations != null) {
+            for (int i = 0; i < locations.size(); i++) {
+                multiloc.addLocation((Location)locations.get(i));
+            }
+        }
+        
+        if (multiloc instanceof LocatedRuntimeException) {
+            throw (LocatedRuntimeException)multiloc;
+        } else {
+            throw (FlowException)multiloc;
         }
     }
 }
-

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowHelper.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowHelper.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowHelper.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowHelper.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.struts.flow.core;
+
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+
+import java.util.Map;
+
+/**
+ * Provides the interface between the flow controller layer and the 
+ * view layer. A view can obtain the context object sent by a flow
+ * script and the current web continuation, if any.
+ */
+public class FlowHelper {
+
+    // Constants defining keys in the object model used to store the various objects.
+    // These constants are private so that access to these objects only go through the
+    // accessors provided below.
+    //
+    // These objects are stored in the object model rather than as request attributes,
+    // as object model is cloned for subrequests (see EnvironmentWrapper), whereas
+    // request attributes are shared between the "real" request and all of its
+    // child requests.
+
+    /**
+     * Request attribute name used to store flow context.
+     */
+    private static final String CONTEXT_OBJECT = "cocoon.flow.context";
+
+    /**
+     * Request attribute name used to store flow continuation.
+     */
+    private static final String CONTINUATION_OBJECT = "cocoon.flow.continuation";
+
+    /**
+     * Get the flow context object associated with the current request
+     *
+     * @param objectModel The Cocoon Environment's object model
+     * @return The context object 
+     */
+    public final static Object getContextObject(Map objectModel) {
+        return objectModel.get(CONTEXT_OBJECT);
+    }
+
+    /**
+     * Get the web continuation associated with the current request
+     *
+     * @param objectModel The Cocoon Environment's object model
+     * @return The web continuation
+     */
+    public final static WebContinuation getWebContinuation(Map objectModel) {
+        return (WebContinuation)objectModel.get(CONTINUATION_OBJECT);
+    }
+
+    /**
+     * Set the web continuation associated with the current request
+     *
+     * @param objectModel The Cocoon Environment's object model
+     * @param kont The web continuation
+     */
+    public final static void setWebContinuation(Map objectModel,
+                                          WebContinuation kont) {
+        objectModel.put(CONTINUATION_OBJECT, kont);
+    }
+
+    /**
+     * Set the flow context object associated with the current request
+     *
+     * @param objectModel The Cocoon Environment's object model
+     * @param obj The context object 
+     */
+    public final static void setContextObject(Map objectModel, Object obj) {
+        objectModel.put(CONTEXT_OBJECT, obj);
+    }
+    
+    /**
+     * Unwrap a Rhino object (getting the raw java object) and convert undefined to null
+     */
+    public static Object unwrap(Object obj) {
+        if (obj instanceof Wrapper) {
+            obj = ((Wrapper)obj).unwrap();
+        } else if (obj == Undefined.instance) {
+            obj = null;
+        }
+        return obj;
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/FlowHelper.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,181 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.struts.flow.core;
+
+import java.util.List;
+
+import org.apache.commons.chain.web.WebContext;
+
+/**
+ * The interface to the flow scripting languages. This interface is
+ * for a component, which implements the appropriate language to be
+ * used for describing the flow. A system could have multiple
+ * components that implement this interface, each of them for a
+ * different scripting language.
+ *
+ * <p>A flow script defines what is the page flow in an interactive
+ * Web application. Usually the flow is defined in a high level
+ * programming language which provides the notion of continuations,
+ * which allows for the flow of the application to be described as a
+ * simple procedural program, without having to think about the
+ * application as a finite state machine which changes its internal
+ * state on each HTTP request from the client browser.
+ *
+ * <p>However an implementation may choose to use its own
+ * representation of an application, which may include XML
+ * representations of finite state machines. Note: this API has no
+ * provision for such implementations.
+ *
+ * <p>The component represented by this interface is called in three
+ * situations:
+ *
+ * <ul>
+ *  <li>
+ *    <p>From the sitemap, to invoke a top level function defined in a
+ *    * given implementation language of the flow. This is done from
+ *    the * sitemap using the construction:
+ *
+ *    <pre>
+ *      &lt;map:call function="..." language="..."/&gt;
+ *    </pre>
+ *
+ *    <p>The <code>language</code> attribute can be ignored if the *
+ *    default language is used.
+ *
+ *  <li>
+ *    <p>From the sitemap, to continue a previously started
+ *    computation. A previously started computation is saved in the
+ *    form of a continuation inside the flow implementation language.
+ *
+ *    <p>This case is similar with the above one, but the function
+ *    invoked has a special name, specific to each language
+ *    implementation. See the language implementation for more
+ *    information on the function name and the arguments it receives.
+ *
+ *  <li>
+ *    <p>From a program in the flow layer. This is done to invoke a
+ *    pipeline defined in the sitemap, to generate the response of the
+ *    request.
+ * </ul>
+ *
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since March 11, 2002
+ * @version CVS $Id: Interpreter.java 106091 2004-11-21 14:20:13Z lgawron $
+ */
+public interface Interpreter {
+
+    public static class Argument {
+        public String name;
+        public String value;
+
+        public Argument(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        public String toString() {
+            return name + ": " + value;
+        }
+    }
+
+	/**
+	 * @return the unique ID for this interpreter.
+	 */
+	String getInterpreterID();
+	
+	/**
+     * Set the unique ID for this interpreter.
+     */
+    void setInterpreterID(String interpreterID);
+
+    /**
+     * This method is called from the sitemap, using the syntax
+     *
+     * <pre>
+     *   &lt;map:call function="..."/&gt;
+     * </pre>
+     *
+     * The method will execute the named function, which must be defined
+     * in the given language. There is no assumption made on how various
+     * arguments are passed to the function.
+     *
+     * <p>The <code>params</code> argument is a <code>List</code> object
+     * that contains <code>Interpreter.Argument</code> instances,
+     * representing the parameters to be passed to the called
+     * function. An <code>Argument</code> instance is a key-value pair,
+     * where the key is the name of the parameter, and the value is its
+     * desired value. Most languages will ignore the name value and
+     * simply pass to the function, in a positional order, the values of
+     * the argument. Some languages however can pass the arguments in a
+     * different order than the original prototype of the function. For
+     * these languages the ability to associate the actual argument with
+     * a formal parameter using its name is essential.
+     *
+     * <p>A particular language implementation may decide to put the
+     * environment, request, response etc. objects in the dynamic scope
+     * available to the function at the time of the call. Other
+     * implementations may decide to pass these as arguments to the
+     * called function.
+     *
+     * <p>The current implementation assumes the sitemap implementation
+     * is TreeProcessor.
+     *
+     * @param funName a <code>String</code> value, the name of the
+     * function to call
+     * @param params a <code>List</code> object whose components are
+     * CallFunctionNode.Argument instances. The interpretation of the
+     * parameters is left to the actual implementation of the
+     * interpreter.
+     * @param redirector a <code>Redirector</code> used to call views
+     */
+    Object callFunction(String funName, List params, WebContext chainCtx)
+    throws Exception;
+
+    /**
+     * Forward the request to a Cocoon pipeline.
+     *
+     * @param uri a <code>String</code>, the URI of the forwarded request
+     * @param bizData an <code>Object</code>, the business data object
+     * to be made available to the forwarded pipeline
+     * @param continuation a <code>WebContinuation</code>, the
+     * continuation to be called to resume the processing
+     * @param redirector a <code>Redirector</code> used to call views
+     * @exception Exception if an error occurs
+     */
+    //void forwardTo(String uri, Object bizData, WebContinuation continuation,
+    //               Redirector redirector)
+    //throws Exception;
+
+    /**
+     * Continues a previously started processing. The continuation
+     * object where the processing should start from is indicated by the
+     * <code>continuationId</code> string.
+     *
+     * @param continuationId a <code>String</code> value
+     *
+     * @param params a <code>List</code> value, containing the
+     * parameters to be passed when invoking the continuation. As
+     * opposed to the parameters passed by <code>callFunction</code>,
+     * these parameters will only become available in the language's
+     * environment, if at all.
+     *
+     * @param redirector a <code>Redirector</code> used to call views
+     * @exception Exception if an error occurs
+     */
+    void handleContinuation(String continuationId, List params,
+                            WebContext chainCtx)
+    throws Exception;
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/InvalidContinuationException.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/InvalidContinuationException.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/InvalidContinuationException.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/InvalidContinuationException.java Sat Nov 26 23:10:08 2005
@@ -1,46 +1,40 @@
 /*
- *  Copyright 1999-2004 The Apache Software Foundation.
- *
- *  Licensed 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.
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.struts.flow.core;
 
 /**
- *  This Exception is thrown whenever an invalid continuation is given.
+ * This Exception is thrown whenever an invalid continuation is given.
  *
- *@author    <a href="mailto:tcollen@neuagency.com">Tony Collen</a>
+ * @author <a href="mailto:tcollen@neuagency.com">Tony Collen</a>
+ * @version CVS $Id: InvalidContinuationException.java 233343 2005-08-18 18:06:44Z sylvain $
  */
 public class InvalidContinuationException extends FlowException {
 
     /**
-     *  Construct a new <code>InvalidContinuationException</code> instance.
-     *
-     *@param  message  The message of the exception 
+     * Construct a new <code>InvalidContinuationException</code> instance.
      */
     public InvalidContinuationException(String message) {
-        super(message, null);
+        super(message);
     }
 
-
     /**
-     *  Construct a new <code>InvalidContinuationException</code> that
-     *  references a parent Exception.
-     *
-     *@param  message  The message
-     *@param  t        The throwable to wrap
+     * Construct a new <code>InvalidContinuationException</code> that references
+     * a parent Exception.
      */
     public InvalidContinuationException(String message, Throwable t) {
         super(message, t);
     }
 }
-

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/Logger.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/Logger.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/Logger.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/Logger.java Sat Nov 26 23:10:08 2005
@@ -59,6 +59,16 @@
     public void debug(String msg) {
         System.out.println("JS-DEBUG: " + msg);
     }
+    
+    /**
+     *  Logs a debugging message
+     *
+     *@param  msg  The message
+     */
+    public void debug(String msg, Throwable t) {
+        System.out.println("JS-DEBUG: " + msg);
+        t.printStackTrace();
+    }
 
 
     /**

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/VariableRegistrar.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/VariableRegistrar.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/VariableRegistrar.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/VariableRegistrar.java Sat Nov 26 23:10:08 2005
@@ -16,7 +16,7 @@
 package org.apache.struts.flow.core;
 
 import org.mozilla.javascript.Scriptable;
-import org.apache.commons.chain.Context;
+import org.apache.commons.chain.web.WebContext;
 
 /**
  *  Defines a variable registrar used to define either a static or 
@@ -43,7 +43,7 @@
      * @param ctx The commons chain context for the call, null if defining
      *            a static variable
      */
-    public Object getInstance(Scriptable scope, Context ctx);
+    public Object getInstance(Scriptable scope, WebContext ctx);
 
     /**
      *  Gets the variable name

Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuation.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuation.java?rev=349187&r1=349186&r2=349187&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuation.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuation.java Sat Nov 26 23:10:08 2005
@@ -1,114 +1,137 @@
 /*
- *  Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2004 The Apache Software Foundation.
  *
- *  Licensed 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
+ * Licensed 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
+ *      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.
+ * 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.struts.flow.core;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
 
 /**
- *  Representation of continuations in a Web environment. <p>
+ * Representation of continuations in a Web environment.
  *
- *  Because a user may click on the back button of the browser and restart a
- *  saved computation in a continuation, each <code>WebContinuation</code>
- *  becomes the parent of a subtree of continuations. <p>
+ * <p>Because a user may click on the back button of the browser and
+ * restart a saved computation in a continuation, each
+ * <code>WebContinuation</code> becomes the parent of a subtree of
+ * continuations.
  *
- *  If there is no parent <code>WebContinuation</code>, the created continuation
- *  becomes the root of a tree of <code>WebContinuation</code>s.
+ * <p>If there is no parent <code>WebContinuation</code>, the created
+ * continuation becomes the root of a tree of
+ * <code>WebContinuation</code>s.
  *
- *@author     <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
- *@since      March 19, 2002
- *@version    CVS $Id: WebContinuation.java,v 1.2 2004/06/02 21:56:54 mrdon Exp
- *      $
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since March 19, 2002
+ * @version CVS $Id: WebContinuation.java 292158 2005-09-28 10:24:51Z sylvain $
  */
 public class WebContinuation implements Comparable {
 
-    /**  The continuation this object represents.  */
+    /**
+     * The continuation this object represents.
+     */
     protected Object continuation;
 
     /**
-     *  The parent <code>WebContinuation</code> from which processing last
-     *  started. If null, there is no parent continuation associated, and this
-     *  is the first one to be created in a processing. In this case this <code>WebContinuation</code>
-     *  instance becomes the root of the tree maintained by the <code>ContinuationsManager</code>
-     *  .
+     * The parent <code>WebContinuation</code> from which processing
+     * last started. If null, there is no parent continuation
+     * associated, and this is the first one to be created in a
+     * processing. In this case this <code>WebContinuation</code>
+     * instance becomes the root of the tree maintained by the
+     * <code>ContinuationsManager</code>.
      *
-     *@see    ContinuationsManager
+     * @see ContinuationsManager
      */
     protected WebContinuation parentContinuation;
 
     /**
-     *  The children continuations. These are continuations created by resuming
-     *  the processing from the point stored by <code>continuation</code>.
+     * The children continuations. These are continuations created by
+     * resuming the processing from the point stored by
+     * <code>continuation</code>.
      */
     protected List children = new ArrayList();
 
     /**
-     *  The continuation id used to represent this instance in Web pages.
+     * The continuation id used to represent this instance in Web pages.
      */
     protected String id;
+    
+    /**
+     * Interpreter id that this continuation is bound to
+     */
+    protected String interpreterId;
 
     /**
-     *  A user definable object. This is present for convenience, to store any
-     *  information associated with this <code>WebContinuation</code> a
-     *  particular implementation might need.
+     * A user definable object. This is present for convenience, to
+     * store any information associated with this
+     * <code>WebContinuation</code> a particular implementation might
+     * need.
      */
     protected Object userObject;
 
     /**
-     *  When was this continuation accessed last time. Each time the
-     *  continuation is accessed, this time is set to the time of the access.
+     * When was this continuation accessed last time. Each time the
+     * continuation is accessed, this time is set to the time of the
+     * access.
      */
     protected long lastAccessTime;
 
     /**
-     *  Indicates how long does this continuation will live (in seconds). The
-     *  continuation will be removed once the current time is bigger than <code>lastAccessTime + timeToLive</code>
-     *  .
+     * Indicates how long does this continuation will live (in
+     * seconds). The continuation will be removed once the current time
+     * is bigger than <code>lastAccessTime + timeToLive</code>.
      */
     protected int timeToLive;
 
     /**
-     *  Holds the <code>ContinuationsDisposer</code> to call when this
-     *  continuation gets invalidated.
+     * Holds the <code>ContinuationsDisposer</code> to call when this continuation
+     * gets invalidated.
      */
     protected ContinuationsDisposer disposer;
 
+    /**
+     * The attributes of this continuation
+     */
+    private Map attributes;
 
     /**
-     *  Create a <code>WebContinuation</code> object. Saves the object in the
-     *  hash table of continuations maintained by <code>manager</code> (this is
-     *  done as a side effect of obtaining and identifier from it).
-     *
-     *@param  continuation        an <code>Object</code> value
-     *@param  parentContinuation  a <code>WebContinuation</code> value
-     *@param  timeToLive          time this continuation should live
-     *@param  disposer            a <code>ContinuationsDisposer</code> to call
-     *      when this continuation gets invalidated.
-     *@param  id                  Description of the Parameter
+     * Create a <code>WebContinuation</code> object. Saves the object in
+     * the hash table of continuations maintained by
+     * <code>manager</code> (this is done as a side effect of obtaining
+     * and identifier from it).
+     *
+     * @param continuation an <code>Object</code> value
+     * @param parentContinuation a <code>WebContinuation</code> value
+     * @param timeToLive time this continuation should live
+     * @param disposer a <code>ContinuationsDisposer</code> to call when this
+     * continuation gets invalidated.
      */
     WebContinuation(String id,
-            Object continuation,
-            WebContinuation parentContinuation,
-            int timeToLive,
-            ContinuationsDisposer disposer) {
+                    Object continuation,
+                    WebContinuation parentContinuation,
+                    int timeToLive,
+                    String interpreterId,
+                    ContinuationsDisposer disposer) {
         this.id = id;
         this.continuation = continuation;
         this.parentContinuation = parentContinuation;
         this.updateLastAccessTime();
         this.timeToLive = timeToLive;
+        this.interpreterId = interpreterId;
         this.disposer = disposer;
 
         if (parentContinuation != null) {
@@ -116,27 +139,77 @@
         }
     }
 
+    /**
+     * Get an attribute of this continuation
+     * 
+     * @param name the attribute name.
+     */
+    public Object getAttribute(String name) {
+        if (this.attributes == null)
+            return null;
+        
+        return this.attributes.get(name);
+    }
+    
+    /**
+     * Set an attribute of this continuation
+     * 
+     * @param name the attribute name
+     * @param value its value
+     */
+    public void setAttribute(String name, Object value) {
+        if (this.attributes == null) {
+            this.attributes = Collections.synchronizedMap(new HashMap());
+        }
+        
+        this.attributes.put(name, value);
+    }
+    
+    /**
+     * Remove an attribute of this continuation
+     * 
+     * @param name the attribute name
+     */
+    public void removeAttribute(String name) {
+        if (this.attributes == null)
+            return;
+        
+        this.attributes.remove(name);
+    }
+    
+    /**
+     * Enumerate the attributes of this continuation.
+     * 
+     * @return an enumeration of strings
+     */
+    public Iterator getAttributeNames() {
+        if (this.attributes == null)
+            return Collections.EMPTY_LIST.iterator();
+        
+        ArrayList keys = new ArrayList(this.attributes.keySet());
+        return keys.iterator();
+    }
 
     /**
-     *  Return the continuation object.
+     * Return the continuation object.
      *
-     *@return    an <code>Object</code> value
+     * @return an <code>Object</code> value
      */
     public Object getContinuation() {
         updateLastAccessTime();
         return continuation;
     }
 
-
     /**
-     *  Return the ancestor continuation situated <code>level</code>s above the
-     *  current continuation. The current instance is considered to be at level
-     *  0. The parent continuation of the receiving instance at level 1, its
-     *  parent is at level 2 relative to the receiving instance. If <code>level</code>
-     *  is bigger than the depth of the tree, the root of the tree is returned.
+     * Return the ancestor continuation situated <code>level</code>s
+     * above the current continuation. The current instance is
+     * considered to be at level 0. The parent continuation of the
+     * receiving instance at level 1, its parent is at level 2 relative
+     * to the receiving instance. If <code>level</code> is bigger than
+     * the depth of the tree, the root of the tree is returned.
      *
-     *@param  level  an <code>int</code> value
-     *@return        a <code>WebContinuation</code> value
+     * @param level an <code>int</code> value
+     * @return a <code>WebContinuation</code> value
      */
     public WebContinuation getContinuation(int level) {
         if (level <= 0) {
@@ -149,107 +222,110 @@
         }
     }
 
-
     /**
-     *  Return the parent <code>WebContinuation</code>. Equivalent with <code>getContinuation(1)</code>
-     *  .
+     * Return the parent <code>WebContinuation</code>. Equivalent with
+     * <code>getContinuation(1)</code>.
      *
-     *@return    a <code>WebContinuation</code> value
+     * @return a <code>WebContinuation</code> value
      */
     public WebContinuation getParentContinuation() {
         return parentContinuation;
     }
 
-
     /**
-     *  Return the children <code>WebContinuation</code> which were created as a
-     *  result of resuming the processing from the current <code>continuation</code>
-     *  .
+     * Return the children <code>WebContinuation</code> which were
+     * created as a result of resuming the processing from the current
+     * <code>continuation</code>.
      *
-     *@return    a <code>List</code> value
+     * @return a <code>List</code> value
      */
     public List getChildren() {
         return children;
     }
 
-
     /**
-     *  Returns the string identifier of this <code>WebContinuation</code>.
+     * Returns the string identifier of this
+     * <code>WebContinuation</code>.
      *
-     *@return    a <code>String</code> value
+     * @return a <code>String</code> value
      */
     public String getId() {
         return id;
     }
 
+    /**
+     * Returns the string identifier of the interpreter to which
+     * this <code>WebContinuation</code> is bound.
+     *
+     * @return a <code>String</code> value
+     */
+    public String getInterpreterId() {
+        return interpreterId;
+    }
 
     /**
-     *  Returns the last time this <code>WebContinuation</code> was accessed.
+     * Returns the last time this
+     * <code>WebContinuation</code> was accessed.
      *
-     *@return    a <code>long</code> value
+     * @return a <code>long</code> value
      */
     public long getLastAccessTime() {
         return lastAccessTime;
     }
 
-
     /**
-     *  Returns the the timetolive for this <code>WebContinuation</code>.
+     * Returns the the timetolive for this
+     * <code>WebContinuation</code>.
      *
-     *@return    a <code>long</code> value
+     * @return a <code>long</code> value
      */
     public long getTimeToLive() {
         return this.timeToLive;
     }
 
-
     /**
-     *  Sets the user object associated with this instance.
+     * Sets the user object associated with this instance.
      *
-     *@param  obj  an <code>Object</code> value
+     * @param obj an <code>Object</code> value
      */
     public void setUserObject(Object obj) {
         this.userObject = obj;
     }
 
-
     /**
-     *  Obtains the user object associated with this instance.
+     * Obtains the user object associated with this instance.
      *
-     *@return    an <code>Object</code> value
+     * @return an <code>Object</code> value
      */
     public Object getUserObject() {
         return userObject;
     }
 
-
     /**
-     *  Obtains the <code>ContinuationsDisposer</code> to call when this
-     *  continuation is invalidated.
+     * Obtains the <code>ContinuationsDisposer</code> to call when this continuation
+     * is invalidated.
      *
-     *@return    a <code>ContinuationsDisposer</code> instance or null if there
-     *      are no specific clean-up actions required.
+     * @return a <code>ContinuationsDisposer</code> instance or null if there are
+     * no specific clean-up actions required.
      */
     ContinuationsDisposer getDisposer() {
         return this.disposer;
     }
 
-
     /**
-     *  Returns the hash code of the associated identifier.
+     * Returns the hash code of the associated identifier.
      *
-     *@return    an <code>int</code> value
+     * @return an <code>int</code> value
      */
     public int hashCode() {
         return id.hashCode();
     }
 
-
     /**
-     *  True if the identifiers are the same, false otherwise.
+     * True if the identifiers are the same, false otherwise.
      *
-     *@param  another  an <code>Object</code> value
-     *@return          a <code>boolean</code> value
+     * @param another an <code>Object</code> value
+     * @return a <code>boolean</code> value
      */
     public boolean equals(Object another) {
         if (another instanceof WebContinuation) {
@@ -258,44 +334,41 @@
         return false;
     }
 
-
     /**
-     *  Compares the expiration time of this instance with that of the
-     *  WebContinuation passed as argument. <p>
+     * Compares the expiration time of this instance with that of the
+     * WebContinuation passed as argument.
      *
-     *  <b>Note:</b> this class has a natural ordering that is inconsistent with
-     *  <code>equals</code>.</p> .
+     * <p><b>Note:</b> this class has a natural ordering that is
+     * inconsistent with <code>equals</code>.</p>.
      *
-     *@param  other  an <code>Object</code> value, which should be a <code>WebContinuation</code>
-     *      instance
-     *@return        an <code>int</code> value
+     * @param other an <code>Object</code> value, which should be a
+     * <code>WebContinuation</code> instance
+     * @return an <code>int</code> value
      */
     public int compareTo(Object other) {
         WebContinuation wk = (WebContinuation) other;
         return (int) ((lastAccessTime + timeToLive)
-                 - (wk.lastAccessTime + wk.timeToLive));
+                - (wk.lastAccessTime + wk.timeToLive));
     }
 
-
     /**
-     *  Debugging method. <p>
+     * Debugging method.
      *
-     *  Assumes the receiving instance as the root of a tree and displays the
-     *  tree of continuations.
+     * <p>Assumes the receiving instance as the root of a tree and
+     * displays the tree of continuations.
      */
     public void display() {
-        getLogger().debug("\nWK: Tree" + display(0));
+        Factory.getLogger().debug("\nWK: Tree" + display(0));
     }
 
-
     /**
-     *  Debugging method. <p>
+     * Debugging method.
      *
-     *  Displays the receiving instance as if it is at the <code>indent</code>
-     *  depth in the tree of continuations. Each level is indented 2 spaces.
+     * <p>Displays the receiving instance as if it is at the
+     * <code>indent</code> depth in the tree of continuations. Each
+     * level is indented 2 spaces.
      *
-     *@param  depth  an <code>int</code> value
-     *@return        Description of the Return Value
+     * @param depth an <code>int</code> value
      */
     protected String display(int depth) {
         StringBuffer tree = new StringBuffer("\n");
@@ -328,26 +401,17 @@
         return tree.toString();
     }
 
-
-    /**  Update the continuation in the  */
-    protected void updateLastAccessTime() {
-        lastAccessTime = System.currentTimeMillis();
-    }
-    
     /**
-     *  Gets the logger 
-     *
-     *@return    The logger value
+     * Update the continuation in the
      */
-    public Logger getLogger() {
-        return Factory.getLogger();
+    protected void updateLastAccessTime() {
+        lastAccessTime = System.currentTimeMillis();
     }
 
-
     /**
-     *  Determines whether this continuation has expired
+     * Determines whether this continuation has expired
      *
-     *@return    a <code>boolean</code> value
+     * @return a <code>boolean</code> value
      */
     public boolean hasExpired() {
         long currentTime = System.currentTimeMillis();
@@ -355,5 +419,32 @@
 
         return (currentTime > expireTime);
     }
-}
 
+    /**
+     * Dispose this continuation. Should be called on invalidation.
+     */
+    public void dispose() {
+        // Call possible implementation-specific clean-up on this continuation.
+        if (this.disposer != null) {
+            this.disposer.disposeContinuation(this);
+        }
+        // Remove continuation object - will also serve as "disposed" flag
+        this.continuation = null;
+    }
+
+    /**
+     * Return true if this continuation was disposed of
+     */
+    public boolean disposed() {
+        return this.continuation == null;
+    }
+    
+    public boolean interpreterMatches( String interpreterId ) {
+        return (interpreterId == null ? false : interpreterId.equals(this.interpreterId));
+    }
+
+    public void detachFromParent() {
+        if (getParentContinuation() != null)
+            getParentContinuation().getChildren().remove(this);
+    }
+}

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuationDataBean.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuationDataBean.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuationDataBean.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuationDataBean.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.struts.flow.core;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Access to continuation data for monitoring applications
+ */
+public class WebContinuationDataBean {
+
+    private static final String TYPE_JAVAFLOW = "javaflow";
+    private static final String TYPE_FLOWSCRIPT = "flowscript";
+    private static final String HAS_EXPIRED_NO = "no";
+    private static final String HAS_EXPIRED_YES = "yes";
+
+    private WebContinuation wc;
+    private SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
+    private List _children = new ArrayList();
+
+    public WebContinuationDataBean(WebContinuation wc) {
+        this.wc = wc;
+        for (Iterator it = wc.getChildren().iterator(); it.hasNext();) {
+            WebContinuationDataBean child = new WebContinuationDataBean(
+                    (WebContinuation) it.next());
+            this._children.add(child);
+        }
+    }
+
+    public String getId() {
+        return wc.getId();
+    }
+
+    public String getLastAccessTime() {
+        return formatter.format(new Date(wc.getLastAccessTime()));
+    }
+
+    public String getInterpreterId() {
+        return wc.getInterpreterId();
+    }
+
+    public String getTimeToLiveInMinutes() {
+        return Long.toString(wc.getTimeToLive() / 1000 / 60);
+    }
+
+    public String getTimeToLive() {
+        return Long.toString(wc.getTimeToLive());
+    }
+
+    public String getExpireTime() {
+        return formatter.format(new Date(wc.getLastAccessTime()
+                + wc.getTimeToLive()));
+    }
+
+    public String hasExpired() {
+        if ((wc.getLastAccessTime() + wc.getTimeToLive()) < System
+                .currentTimeMillis()) {
+            return HAS_EXPIRED_YES;
+        }
+        return HAS_EXPIRED_NO;
+
+    }
+
+    public String getType() {
+        if (wc.getUserObject().getClass().getName().indexOf(
+                "FOM_WebContinuation") > 0) {
+            return TYPE_FLOWSCRIPT;
+        }
+        return TYPE_JAVAFLOW;
+    }
+
+    public List get_children() {
+        return this._children;
+    }
+
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/WebContinuationDataBean.java
------------------------------------------------------------------------------
    svn:executable = *



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