cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jheym...@apache.org
Subject svn commit: r330548 [14/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/...
Date Thu, 03 Nov 2005 14:00:48 GMT
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/SimpleCocoonCrawlerImpl.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/SimpleCocoonCrawlerImpl.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/SimpleCocoonCrawlerImpl.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/SimpleCocoonCrawlerImpl.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,650 @@
+/*
+ * 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.cocoon.components.crawler;
+
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.cocoon.Constants;
+import org.apache.commons.lang.StringUtils;
+import org.apache.regexp.RE;
+import org.apache.regexp.RESyntaxException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A simple cocoon crawler.
+ *
+ * @author     <a href="mailto:berni_huber@a1.net">Bernhard Huber</a>
+ * @version CVS $Id: SimpleCocoonCrawlerImpl.java 30941 2004-07-29 19:56:58Z vgritsenko $
+ */
+public class SimpleCocoonCrawlerImpl extends AbstractLogEnabled
+implements CocoonCrawler, Configurable, Disposable, Recyclable {
+    
+    /**
+     * Config element name specifying expected link content-typ.
+     * <p>
+     *   Its value is <code>link-content-type</code>.
+     * </p>
+     */
+    public final static String LINK_CONTENT_TYPE_CONFIG = "link-content-type";
+    
+    /**
+     * Default value of <code>link-content-type</code> configuration value.
+     * <p>
+     *   Its value is <code>application/x-cocoon-links</code>.
+     * </p>
+     */
+    public final String LINK_CONTENT_TYPE_DEFAULT = Constants.LINK_CONTENT_TYPE;
+    
+    /**
+     * Config element name specifying query-string appendend for requesting links
+     * of an URL.
+     * <p>
+     *  Its value is <code>link-view-query</code>.
+     * </p>
+     */
+    public final static String LINK_VIEW_QUERY_CONFIG = "link-view-query";
+    
+    /**
+     * Default value of <code>link-view-query</code> configuration option.
+     * <p>
+     *   Its value is <code>?cocoon-view=links</code>.
+     * </p>
+     */
+    public final static String LINK_VIEW_QUERY_DEFAULT = "cocoon-view=links";
+    
+    /**
+     * Config element name specifying excluding regular expression pattern.
+     * <p>
+     *  Its value is <code>exclude</code>.
+     * </p>
+     */
+    public final static String EXCLUDE_CONFIG = "exclude";
+    
+    /**
+     * Config element name specifying including regular expression pattern.
+     * <p>
+     *  Its value is <code>include</code>.
+     * </p>
+     */
+    public final static String INCLUDE_CONFIG = "include";
+    
+    /**
+     * Config element name specifying http header value for user-Agent.
+     * <p>
+     *  Its value is <code>user-agent</code>.
+     * </p>
+     */
+    public final static String USER_AGENT_CONFIG = "user-agent";
+    
+    /**
+     * Default value of <code>user-agent</code> configuration option.
+     * @see Constants#COMPLETE_NAME
+     */
+    public final static String USER_AGENT_DEFAULT = Constants.COMPLETE_NAME;
+    
+    /**
+     * Config element name specifying http header value for accept.
+     * <p>
+     *  Its value is <code>accept</code>.
+     * </p>
+     */
+    public final static String ACCEPT_CONFIG = "accept";
+    
+    /**
+     * Default value of <code>accept</code> configuration option.
+     * <p>
+     *   Its value is <code>* / *</code>
+     * </p>
+     */
+    public final static String ACCEPT_DEFAULT = "*/*";
+    
+    
+    private String linkViewQuery = LINK_VIEW_QUERY_DEFAULT;
+    private String linkContentType = LINK_CONTENT_TYPE_DEFAULT;
+    private HashSet excludeCrawlingURL;
+    private HashSet includeCrawlingURL;
+    private String userAgent = USER_AGENT_DEFAULT;
+    private String accept = ACCEPT_DEFAULT;
+    
+    private int depth;
+    
+    private HashSet crawled;
+    private HashSet urlsToProcess;
+    private HashSet urlsNextDepth;
+    
+    
+    
+    /**
+     * Constructor for the SimpleCocoonCrawlerImpl object
+     */
+    public SimpleCocoonCrawlerImpl() {
+        // by default include everything
+        includeCrawlingURL = null;
+        // by default exclude common image patterns
+        excludeCrawlingURL = null;
+    }
+    
+    
+    /**
+     * Configure the crawler component.
+     * <p>
+     *  Configure can specify which URI to include, and which URI to exclude
+     *  from crawling. You specify the patterns as regular expressions.
+     * </p>
+     * <p>
+     *  Morover you can configure
+     *  the required content-type of crawling request, and the
+     *  query-string appended to each crawling request.
+     * </p>
+     * <pre><tt>
+     * &lt;include&gt;.*\.html?&lt;/exclude&gt; or &lt;exclude&gt;.*\.html?, .*\.xsp&lt;/exclude&gt;
+     * &lt;exclude&gt;.*\.gif&lt;/exclude&gt; or &lt;exclude&gt;.*\.gif, .*\.jpe?g&lt;/exclude&gt;
+     * &lt;link-content-type&gt; application/x-cocoon-links &lt;/link-content-type&gt;
+     * &lt;link-view-query&gt; ?cocoon-view=links &lt;/link-view-query&gt;
+     * </tt></pre>
+     *
+     * @param  configuration               XML configuration of this avalon component.
+     * @exception  ConfigurationException  is throwing if configuration is invalid.
+     */
+    public void configure(Configuration configuration)
+    throws ConfigurationException {
+        
+        Configuration[] children;
+        children = configuration.getChildren(INCLUDE_CONFIG);
+        if (children.length > 0) {
+            includeCrawlingURL = new HashSet();
+            for (int i = 0; i < children.length; i++) {
+                String pattern = children[i].getValue();
+                try {
+                    String params[] = StringUtils.split(pattern, ", ");
+                    for (int index = 0; index < params.length; index++) {
+                        String tokenized_pattern = params[index];
+                        this.includeCrawlingURL.add(new RE(tokenized_pattern));
+                    }
+                } catch (RESyntaxException rese) {
+                    getLogger().error("Cannot create including regular-expression for " +
+                    pattern, rese);
+                }
+            }
+        } else {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Include all URLs");
+            }
+        }
+        
+        children = configuration.getChildren(EXCLUDE_CONFIG);
+        if (children.length > 0) {
+            excludeCrawlingURL = new HashSet();
+            for (int i = 0; i < children.length; i++) {
+                String pattern = children[i].getValue();
+                try {
+                    String params[] = StringUtils.split(pattern, ", ");
+                    for (int index = 0; index < params.length; index++) {
+                        String tokenized_pattern = params[index];
+                        this.excludeCrawlingURL.add(new RE(tokenized_pattern));
+                    }
+                } catch (RESyntaxException rese) {
+                    getLogger().error("Cannot create excluding regular-expression for " +
+                    pattern, rese);
+                }
+            }
+        } else {
+            excludeCrawlingURL = new HashSet();
+            setDefaultExcludeFromCrawling();
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Exclude default URLs only");
+            }
+        }
+        
+        Configuration child;
+        String value;
+        child = configuration.getChild(LINK_CONTENT_TYPE_CONFIG, false);
+        if (child != null) {
+            value = child.getValue();
+            if (value != null && value.length() > 0) {
+                this.linkContentType = value.trim();
+            }
+        }
+        child = configuration.getChild(LINK_VIEW_QUERY_CONFIG, false);
+        if (child != null) {
+            value = child.getValue();
+            if (value != null && value.length() > 0) {
+                this.linkViewQuery = value.trim();
+            }
+        }
+        
+        child = configuration.getChild(USER_AGENT_CONFIG, false);
+        if (child != null) {
+            value = child.getValue();
+            if (value != null && value.length() > 0) {
+                this.userAgent = value;
+            }
+        }
+        
+        child = configuration.getChild(ACCEPT_CONFIG, false);
+        if (child != null) {
+            value = child.getValue();
+            if (value != null && value.length() > 0) {
+                this.accept = value;
+            }
+        }
+        
+    }
+    
+    
+    /**
+     * dispose at end of life cycle, releasing all resources.
+     */
+    public void dispose() {
+        crawled = null;
+        urlsToProcess = null;
+        urlsNextDepth = null;
+        excludeCrawlingURL = null;
+        includeCrawlingURL = null;
+    }
+    
+    
+    /**
+     * recylcle this object, relasing resources
+     */
+    public void recycle() {
+        crawled = null;
+        urlsToProcess = null;
+        urlsNextDepth = null;
+        depth = -1;
+    }
+    
+    
+    /**
+     * The same as calling crawl(url,-1);
+     *
+     * @param  url  Crawl this URL, getting all links from this URL.
+     */
+    public void crawl(URL url) {
+        crawl(url, -1);
+    }
+    
+    /**
+     * Start crawling a URL.
+     *
+     * <p>
+     *   Use this method to start crawling.
+     *   Get the this url, and all its children  by using <code>iterator()</code>.
+     *   The Iterator object will return URL objects.
+     * </p>
+     * <p>
+     *  You may use the crawl(), and iterator() methods the following way:
+     * </p>
+     * <pre><tt>
+     *   SimpleCocoonCrawlerImpl scci = ....;
+     *   scci.crawl( "http://foo/bar" );
+     *   Iterator i = scci.iterator();
+     *   while (i.hasNext()) {
+     *     URL url = (URL)i.next();
+     *     ...
+     *   }
+     * </tt></pre>
+     * <p>
+     *   The i.next() method returns a URL, and calculates the links of the
+     *   URL before return it.
+     * </p>
+     *
+     * @param  url  Crawl this URL, getting all links from this URL.
+     * @param  maxDepth  maximum depth to crawl to. -1 for no maximum.
+     */
+    public void crawl(URL url, int maxDepth) {
+        crawled = new HashSet();
+        urlsToProcess = new HashSet();
+        urlsNextDepth = new HashSet();
+        depth = maxDepth;
+        
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("crawl URL " + url + " to depth " + maxDepth);
+        }
+        
+        urlsToProcess.add(url);
+    }
+    
+    
+    /**
+     * Return iterator, iterating over all links of the currently crawled URL.
+     * <p>
+     *   The Iterator object will return URL objects at its <code>next()</code>
+     *   method.
+     * </p>
+     *
+     * @return    Iterator iterator of all links from the crawl URL.
+     * @since
+     */
+    public Iterator iterator() {
+        return new CocoonCrawlerIterator(this);
+    }
+    
+    
+    /**
+     * Default exclude patterns.
+     * <p>
+     *   By default URLs matching following patterns are excluded:
+     * </p>
+     * <ul>
+     *   <li>.*\\.gif(\\?.*)?$ - exclude gif images</li>
+     *   <li>.*\\.png(\\?.*)?$ - exclude png images</li>
+     *   <li>.*\\.jpe?g(\\?.*)?$ - exclude jpeg images</li>
+     *   <li>.*\\.js(\\?.*)?$ - exclude javascript </li>
+     *   <li>.*\\.css(\\?.*)?$ - exclude cascaded stylesheets</li>
+     * </ul>
+     *
+     * @since
+     */
+    private void setDefaultExcludeFromCrawling() {
+        String[] EXCLUDE_FROM_CRAWLING_DEFAULT = {
+            ".*\\.gif(\\?.*)?$",
+            ".*\\.png(\\?.*)?$",
+            ".*\\.jpe?g(\\?.*)?$",
+            ".*\\.js(\\?.*)?$",
+            ".*\\.css(\\?.*)?$"
+        };
+        
+        for (int i = 0; i < EXCLUDE_FROM_CRAWLING_DEFAULT.length; i++) {
+            String pattern = EXCLUDE_FROM_CRAWLING_DEFAULT[i];
+            try {
+                excludeCrawlingURL.add(new RE(pattern));
+            } catch (RESyntaxException rese) {
+                getLogger().error("Cannot create excluding regular-expression for " +
+                pattern, rese);
+            }
+        }
+    }
+    
+    
+    /**
+     * Compute list of links from the url.
+     * <p>
+     *   Check for include, exclude pattern, content-type, and if url
+     *   has been craweled already.
+     * </p>
+     *
+     * @param  url  Crawl this URL
+     * @return      List of URLs, which are links from url, asserting the conditions.
+     * @since
+     */
+    private List getLinks(URL url) {
+        ArrayList url_links = null;
+        String sURL = url.toString();
+        
+        if (!isIncludedURL(sURL) || isExcludedURL(sURL)) {
+            return null;
+        }
+        
+        // don't try to get links for url which has been crawled already
+        if (crawled.contains(sURL)) {
+            return null;
+        }
+        
+        // mark it as crawled
+        crawled.add(sURL);
+        
+        // get links of url
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Getting links of URL " + sURL);
+        }
+        BufferedReader br = null;
+        try {
+            sURL = url.getFile();
+            URL links = new URL(url, sURL
+            + ((sURL.indexOf("?") == -1) ? "?" : "&")
+            + linkViewQuery);
+            URLConnection links_url_connection = links.openConnection();
+            links_url_connection.setRequestProperty("Accept", accept);
+            links_url_connection.setRequestProperty("User-Agent", userAgent);
+            links_url_connection.connect();
+            InputStream is = links_url_connection.getInputStream();
+            br = new BufferedReader(new InputStreamReader(is));
+            
+            String contentType = links_url_connection.getContentType();
+            if (contentType == null) {
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Ignoring " + sURL + " (no content type)");
+                }
+                // there is a check on null in the calling method
+                return null;
+            }
+            
+            int index = contentType.indexOf(';');
+            if (index != -1) {
+                contentType = contentType.substring(0, index);
+            }
+            
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Content-type: " + contentType);
+            }
+            
+            if (contentType.equals(linkContentType)) {
+                url_links = new ArrayList();
+                
+                // content is supposed to be a list of links,
+                // relative to current URL
+                String line;
+                while ((line = br.readLine()) != null) {
+                    final URL newUrl = new URL(url, line);
+                    final String sNewUrl = newUrl.toString();
+                    
+                    boolean add_url = true;
+                    // don't add new_url twice
+                    if (add_url) {
+                        add_url &= !url_links.contains(sNewUrl);
+                    }
+                    
+                    // don't add new_url if it has been crawled already
+                    if (add_url) {
+                        add_url &= !crawled.contains(sNewUrl);
+                    }
+                    
+                    // don't add if is not matched by existing include definition
+                    if (add_url) {
+                        add_url &= isIncludedURL(sNewUrl);
+                    }
+                    
+                    // don't add if is matched by existing exclude definition
+                    if (add_url) {
+                        add_url &= !isExcludedURL(sNewUrl);
+                    }
+                    if (add_url) {
+                        if (getLogger().isDebugEnabled()) {
+                            getLogger().debug("Add URL: " + sNewUrl);
+                        }
+                        url_links.add(newUrl);
+                    }
+                }
+                // now we have a list of URL which should be examined
+            }
+        } catch (IOException ioe) {
+            getLogger().warn("Problems get links of " + url, ioe);
+        } finally {
+            if (br != null) {
+                try {
+                    br.close();
+                    br = null;
+                } catch (IOException ignored) {
+                }
+            }
+        }
+        return url_links;
+    }
+    
+    
+    /**
+     * check if URL is a candidate for indexing
+     *
+     * @param  url  the URL to check
+     * @return      The excludedURL value
+     */
+    private boolean isExcludedURL(String url) {
+        // by default do not exclude URL for crawling
+        if (excludeCrawlingURL == null) {
+            return false;
+        }
+        
+        final String s = url.toString();
+        Iterator i = excludeCrawlingURL.iterator();
+        while (i.hasNext()) {
+            RE pattern = (RE) i.next();
+            if (pattern.match(s)) {
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Excluded URL " + url);
+                }
+                return true;
+            }
+        }
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Not excluded URL " + url);
+        }
+        return false;
+    }
+    
+    
+    /**
+     * check if URL is a candidate for indexing
+     *
+     * @param  url  Description of Parameter
+     * @return      The includedURL value
+     */
+    private boolean isIncludedURL(String url) {
+        // by default include URL for crawling
+        if (includeCrawlingURL == null) {
+            return true;
+        }
+        
+        final String s = url.toString();
+        Iterator i = includeCrawlingURL.iterator();
+        while (i.hasNext()) {
+            RE pattern = (RE) i.next();
+            if (pattern.match(s)) {
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Included URL " + url);
+                }
+                return true;
+            }
+        }
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Not included URL " + url);
+        }
+        return false;
+    }
+    
+    
+    /**
+     * Helper class implementing an Iterator
+     * <p>
+     *   This Iterator implementation calculates the links of an URL
+     *   before returning in the next() method.
+     * </p>
+     *
+     * @author     <a href="mailto:berni_huber@a1.net">Bernhard Huber</a>
+     * @version    $Id: SimpleCocoonCrawlerImpl.java 30941 2004-07-29 19:56:58Z vgritsenko $
+     */
+    public static class CocoonCrawlerIterator implements Iterator {
+        private SimpleCocoonCrawlerImpl cocoonCrawler;
+        
+        
+        /**
+         * Constructor for the CocoonCrawlerIterator object
+         *
+         * @param  cocoonCrawler  the containing CocoonCrawler instance.
+         */
+        CocoonCrawlerIterator(SimpleCocoonCrawlerImpl cocoonCrawler) {
+            this.cocoonCrawler = cocoonCrawler;
+        }
+        
+        
+        /**
+         * check if crawling is finished.
+         *
+         * @return    <code>true</code> if crawling has finished,
+         * else <code>false</code>.
+         */
+        public boolean hasNext() {
+            return cocoonCrawler.urlsToProcess.size() > 0
+            || cocoonCrawler.urlsNextDepth.size() > 0;
+        }
+        
+        
+        /**
+         * @return    the next URL
+         */
+        public Object next() {
+            if (cocoonCrawler.urlsToProcess.size() == 0
+            && cocoonCrawler.urlsNextDepth.size() > 0) {
+                // process queued urls belonging to the next depth level
+                cocoonCrawler.urlsToProcess = cocoonCrawler.urlsNextDepth;
+                cocoonCrawler.urlsNextDepth = new HashSet();
+                // fix Bugzilla Bug 25270
+                // only decrease if depth > 0, excluding decreasing
+                // if depth is already equal to -1
+                if (cocoonCrawler.depth > 0) {
+                    cocoonCrawler.depth--;
+                }
+            }
+            URL theNextUrl = null;
+            // fix Bugzilla Bug 25270
+            // return NextUrl != null only if getLinks() returns non-null
+            // list
+            for (Iterator i = cocoonCrawler.urlsToProcess.iterator(); 
+              i.hasNext() && theNextUrl == null;) {
+                // fetch a URL
+                URL url = (URL) i.next();
+
+                // remove it from the to-do list
+                i.remove();
+
+                if (cocoonCrawler.depth == -1 || cocoonCrawler.depth > 0) {
+                    // calc all links from this url
+                    List url_links = cocoonCrawler.getLinks(url);
+                    if (url_links != null) {
+                        // add links of this url to the to-do list
+                        cocoonCrawler.urlsNextDepth.addAll(url_links);
+                        theNextUrl = url;
+                    }
+                }
+            }
+            // finally return url
+            return theNextUrl;
+        }
+        
+        
+        /**
+         * remove is not implemented
+         */
+        public void remove() {
+            throw new UnsupportedOperationException("remove is not implemented");
+        }
+    }
+}
+

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/SimpleCocoonCrawlerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/package.html
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/package.html?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/package.html (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/package.html Thu Nov  3 05:41:06 2005
@@ -0,0 +1,33 @@
+<!--
+  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.
+-->
+<html>
+<head>
+  <title>Crawler - starts from a given URL and follows the links</title>
+</head>
+<body>
+  <h1>Crawler - starts from a given URL and follows the links</h1>
+  <p>
+    This package provides Cocoon crawler components.
+  </p>
+  <p>
+    A crawler component crawls through URLs.
+    The process is initiated by calling <code>crawl()</code>.
+    The method <code>iterator()</code> returns an Iterator object.
+    This Iterator object iterates over all URLs reachable from
+    the passed URL of method <code>crawl()</code>.
+  </p>
+</body>
+</html>

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/crawler/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitor.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitor.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitor.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitor.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,26 @@
+/* 
+ * Copyright 2002-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.cocoon.components.fam;
+
+import org.apache.commons.jci.monitor.FilesystemAlterationListener;
+
+public interface SitemapMonitor {
+	String ROLE = SitemapMonitor.class.getName();
+    
+    void subscribe(final FilesystemAlterationListener listener);    
+    void unsubscribe(final FilesystemAlterationListener listener);
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitorImpl.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitorImpl.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitorImpl.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitorImpl.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,57 @@
+/* 
+ * Copyright 2002-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.cocoon.components.fam;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+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.commons.jci.monitor.FilesystemAlterationListener;
+import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
+import org.apache.excalibur.source.SourceResolver;
+
+public final class SitemapMonitorImpl extends AbstractLogEnabled implements SitemapMonitor, Serviceable, ThreadSafe, Initializable, Disposable {
+
+    private ServiceManager manager;
+    private SourceResolver resolver;
+    private FilesystemAlterationMonitor monitor;
+
+    public void service( ServiceManager manager ) throws ServiceException {
+        this.manager = manager;
+        this.resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
+    }
+
+    public void initialize() throws Exception {
+        monitor = new FilesystemAlterationMonitor();
+        monitor.start();
+    }
+
+    public void dispose() {
+        monitor.stop();
+    }
+    
+    public void subscribe(final FilesystemAlterationListener listener) {
+        monitor.addListener(listener);
+    }
+    
+    public void unsubscribe(final FilesystemAlterationListener listener) {
+        monitor.removeListener(listener);
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/fam/SitemapMonitorImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/AbstractInterpreter.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/AbstractInterpreter.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/AbstractInterpreter.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/AbstractInterpreter.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,210 @@
+/*
+ * 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.cocoon.components.flow;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Map;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+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.SingleThreaded;
+import org.apache.cocoon.Constants;
+import org.apache.cocoon.components.ContextHelper;
+import org.apache.cocoon.components.flow.util.PipelineUtil;
+import org.apache.cocoon.environment.Context;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.excalibur.source.SourceUtil;
+
+/**
+ * Abstract superclass for various scripting languages used by Cocoon
+ * for flow control. Defines some useful behavior like the ability to
+ * reload script files if they get modified (useful when doing
+ * development), and passing the control to Cocoon's sitemap for
+ * result page generation.
+ * <p>
+ * Flow intrepreters belonging to different sitemaps should be isolated. To achieve this,
+ * class implements the {@link org.apache.avalon.framework.thread.SingleThreaded}. Since
+ * the sitemap engine looks up the flow intepreter once at sitemap build time, this ensures
+ * that each sitemap will use a different instance of this class. But that instance will
+ * handle all flow calls for a given sitemap, and must therefore be thread safe.
+ *
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since March 15, 2002
+ * @version CVS $Id: AbstractInterpreter.java 123711 2004-12-30 10:23:03Z cziegeler $
+ */
+public abstract class AbstractInterpreter
+        extends AbstractLogEnabled
+        implements Serviceable, Contextualizable, Interpreter,
+                   SingleThreaded, Configurable, Disposable {
+
+    // The instance ID of this interpreter, used to identify user scopes
+    private String instanceID;
+
+    protected org.apache.avalon.framework.context.Context avalonContext;
+
+    /**
+     * List of source locations that need to be resolved.
+     */
+    protected ArrayList needResolve = new ArrayList();
+
+    protected org.apache.cocoon.environment.Context context;
+    protected ServiceManager manager;
+    protected ContinuationsManager continuationsMgr;
+
+    /**
+     * Whether reloading of scripts should be done. Specified through
+     * the "reload-scripts" attribute in <code>flow.xmap</code>.
+     */
+    protected boolean reloadScripts;
+
+    /**
+     * Interval between two checks for modified script files. Specified
+     * through the "check-time" XML attribute in <code>flow.xmap</code>.
+     */
+    protected long checkTime;
+
+    /**
+     * Set the unique ID for this interpreter, which can be used to distinguish user value scopes
+     * attached to the session.
+     */
+    public void setInterpreterID(String interpreterID) {
+        this.instanceID = interpreterID;
+    }
+
+    /**
+     * Get the unique ID for this interpreter, which can be used to distinguish user value scopes
+     * attached to the session.
+     *
+     * @return a unique ID for this interpreter
+     */
+    public String getInterpreterID() {
+        return this.instanceID;
+    }
+
+    public void configure(Configuration config) throws ConfigurationException {
+        reloadScripts = config.getChild("reload-scripts").getValueAsBoolean(false);
+        checkTime = config.getChild("check-time").getValueAsLong(1000L);
+    }
+
+    /**
+     * Serviceable
+     */
+    public void service(ServiceManager sm) throws ServiceException {
+        this.manager = sm;
+        this.continuationsMgr = (ContinuationsManager)sm.lookup(ContinuationsManager.ROLE);
+    }
+
+    public void contextualize(org.apache.avalon.framework.context.Context context)
+    throws ContextException{
+        this.avalonContext = context;
+        this.context = (Context)context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose() {
+        if ( this.manager != null ) {
+            this.manager.release( this.continuationsMgr );
+            this.continuationsMgr = null;
+            this.manager = null;
+        }
+    }
+
+    /**
+     * Registers a source file with the interpreter. Using this method
+     * an implementation keeps track of all the script files which are
+     * compiled. This allows them to reload the script files which get
+     * modified on the file system.
+     *
+     * <p>The parsing/compilation of a script file by an interpreter
+     * happens in two phases. In the first phase the file's location is
+     * registered in the <code>needResolve</code> array.
+     *
+     * <p>The second is possible only when a Cocoon
+     * <code>Environment</code> is passed to the Interpreter. This
+     * allows the file location to be resolved using Cocoon's
+     * <code>SourceFactory</code> class.
+     *
+     * <p>Once a file's location can be resolved, it is removed from the
+     * <code>needResolve</code> array and placed in the
+     * <code>scripts</code> hash table. The key in this hash table is
+     * the file location string, and the value is a
+     * DelayedRefreshSourceWrapper instance which keeps track of when
+     * the file needs to re-read.
+     *
+     * @param source the location of the script
+     *
+     * @see org.apache.cocoon.environment.Environment
+     * @see org.apache.cocoon.components.source.impl.DelayedRefreshSourceWrapper
+     */
+    public void register(String source) {
+        synchronized (this) {
+            needResolve.add(source);
+        }
+    }
+
+    /**
+     * Call the Cocoon sitemap for the given URI, sending the output of the
+     * eventually matched pipeline to the specified outputstream.
+     *
+     * @param uri The URI for which the request should be generated.
+     * @param biz Extra data associated with the subrequest.
+     * @param out An OutputStream where the output should be written to.
+     * @exception Exception If an error occurs.
+     */
+    public void process(String uri, Object biz, OutputStream out)
+    throws Exception {
+        // FIXME (SW): should we deprecate this method in favor of PipelineUtil?
+        PipelineUtil pipeUtil = new PipelineUtil();
+        try {
+            pipeUtil.contextualize(this.avalonContext);
+            pipeUtil.service(this.manager);
+            pipeUtil.processToStream(uri, biz, out);
+        } finally {
+            pipeUtil.dispose();
+        }
+    }
+
+    public void forwardTo(String uri, Object bizData,
+                          WebContinuation continuation,
+                          Redirector redirector)
+    throws Exception {
+        if (SourceUtil.indexOfSchemeColon(uri) == -1) {
+            uri = "cocoon:/" + uri;
+            Map objectModel = ContextHelper.getObjectModel(this.avalonContext);
+            FlowHelper.setWebContinuation(objectModel, continuation);
+            FlowHelper.setContextObject(objectModel, bizData);
+            if (redirector.hasRedirected()) {
+                throw new IllegalStateException("Pipeline has already been processed for this request");
+            }
+            // this is a hint for the redirector
+            objectModel.put("cocoon:forward", "true");
+            redirector.redirect(false, uri);
+        } else {
+            throw new Exception("uri is not allowed to contain a scheme (cocoon:/ is always automatically used)");
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/AbstractInterpreter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,120 @@
+/*
+ * 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.cocoon.components.flow;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceResolver;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+
+/**
+ * This is a base class for all interpreters compiling the scripts
+ *
+ * @author <a href="mailto:ovidiu@apache.org">Ovidiu Predescu</a>
+ * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @version CVS $Id: CompilingInterpreter.java 55850 2004-10-28 13:43:12Z vgritsenko $
+ */
+public abstract class CompilingInterpreter
+        extends AbstractInterpreter {
+
+    /**
+     * A source resolver
+     */
+    protected SourceResolver sourceresolver;
+
+    /**
+     * Mapping of String objects (source uri's) to ScriptSourceEntry's
+     */
+    protected Map compiledScripts = new HashMap();
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+     */
+    public void service(ServiceManager manager) throws ServiceException {
+        super.service(manager);
+        this.sourceresolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose() {
+        if (this.compiledScripts != null) {
+            Iterator i = this.compiledScripts.values().iterator();
+            while (i.hasNext()) {
+                ScriptSourceEntry current = (ScriptSourceEntry)i.next();
+                this.sourceresolver.release(current.getSource());
+            }
+            this.compiledScripts = null;
+        }
+        if (this.manager != null) {
+            this.manager.release(this.sourceresolver);
+            this.sourceresolver = null;
+        }
+        super.dispose();
+    }
+
+    /**
+     * TODO - This is a little bit strange, the interpreter calls
+     * get Script on the ScriptSourceEntry which in turn
+     * calls compileScript on the interpreter. I think we
+     * need more refactoring here.
+     */
+    protected abstract Script compileScript(Context context,
+                                            Scriptable scope,
+                                            Source source) throws Exception;
+
+    protected class ScriptSourceEntry {
+        final private Source source;
+        private Script script;
+        private long compileTime;
+
+        public ScriptSourceEntry(Source source) {
+            this.source = source;
+        }
+
+        public ScriptSourceEntry(Source source, Script script, long t) {
+            this.source = source;
+            this.script = script;
+            this.compileTime = t;
+        }
+
+        public Source getSource() {
+            return source;
+        }
+
+        public Script getScript(Context context, Scriptable scope,
+                                boolean refresh, CompilingInterpreter interpreter)
+        throws Exception {
+            if (refresh) {
+                source.refresh();
+            }
+            if (script == null || compileTime < source.getLastModified()) {
+                script = interpreter.compileScript(context, scope, source);
+                compileTime = source.getLastModified();
+            }
+            return script;
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsDisposer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsDisposer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsDisposer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsDisposer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,39 @@
+/*
+ * 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.cocoon.components.flow;
+
+/**
+ * ContinuationsDisposer declares the contract for the clean-up of specfic 
+ * continuations.
+ * <p>
+ * Typically a {@link Interpreter} implementation that produces continuation 
+ * objects which require proper clean up will implement this interface to get
+ * a call-back in the event of the ContinuationsManager deciding to invalidate 
+ * a WebContinuation. 
+ */
+public interface ContinuationsDisposer {
+    /**
+     * Disposes the passed continuation object.
+     * <p>
+     * This method is called from the ContinuationsManager in the event of
+     * the invalidation of a continuation upon the {@link ContinuationsDisposer}
+     * object passed during the creation of the WebContinuation.
+     * 
+     * @param webContinuation the {@link WebContinuation} value representing the  
+     * continuation object. 
+     */
+    public void disposeContinuation(WebContinuation webContinuation);
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsDisposer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManager.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManager.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManager.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManager.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,99 @@
+/*
+ * 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.cocoon.components.flow;
+
+import java.util.List;
+
+/**
+ * The interface of the Continuations manager.
+ *
+ * The continuation manager maintains a forrest of {@link
+ * WebContinuation} trees. Each tree defines the flow of control for a
+ * user within the application.
+ *
+ * A <code>WebContinuation</code> is created for a continuation object
+ * from the scripting language used. A continuation object in the
+ * implementation of the scripting language is an opaque object
+ * here. It is only stored inside the <code>WebContinuation</code>,
+ * without being interpreted in any way.
+ *
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since March 19, 2002
+ * @see WebContinuation
+ * @version CVS $Id: ContinuationsManager.java 164239 2005-04-22 13:54:29Z reinhard $
+ */
+public interface ContinuationsManager {
+    public final String ROLE = ContinuationsManager.class.getName();
+
+    /**
+     * 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 parentKont 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 interpreterId id of interpreter invoking continuation creation
+     * @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 parentKont,
+                                                 int timeToLive,
+                                                 String interpreterId,
+                                                 ContinuationsDisposer disposer);
+
+    /**
+     * 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 k a <code>WebContinuation</code> value
+     */
+    public void invalidateWebContinuation(WebContinuation k);
+
+    /**
+     * Given a <code>WebContinuation</code> id, retrieve the associated
+     * <code>WebContinuation</code> object.
+     * @param id a <code>String</code> value
+     * @param interpreterId Id of an interpreter that queries for 
+     * the continuation
+     *
+     * @return a <code>WebContinuation</code> object, null if no such
+     * <code>WebContinuation</code> could be found. Also null if 
+     * <code>WebContinuation</code> was found but interpreter id does 
+     * not match the one that the continuation was initialy created for.
+     */
+    public WebContinuation lookupWebContinuation(String id, String interpreterId);
+
+    /**
+     * Prints debug information about all web continuations into the log file.
+     * @see WebContinuation#display()
+     */
+    public void displayAllContinuations();
+    
+    /**
+     * Get a list of all continuations as <code>WebContinuationDataBean</code> objects. 
+     */
+    public List getWebContinuationsDataBeanList();
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,581 @@
+/*
+ * 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.cocoon.components.flow;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+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}. <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
+        extends AbstractLogEnabled
+        implements ContinuationsManager, Configurable, ThreadSafe, Serviceable, Contextualizable  {
+
+    static final int CONTINUATION_ID_LENGTH = 20;
+    static final String EXPIRE_CONTINUATIONS = "expire-continuations";
+
+    /**
+     * Random number generator used to create continuation ID
+     */
+    protected SecureRandom random;
+    protected byte[] bytes;
+
+    /**
+     * How long does a continuation exist in memory since the last
+     * access? The time is in miliseconds, and the default is 1 hour.
+     */
+    protected int defaultTimeToLive;
+
+    /**
+     * Maintains the forest of <code>WebContinuation</code> trees.
+     * This set is used only for debugging puroses by
+     * {@link #displayAllContinuations()} method.
+     */
+    protected Set forest = Collections.synchronizedSet(new HashSet());
+
+    /**
+     * Main continuations holder. Used unless continuations are stored in user
+     * session.
+     */
+    protected WebContinuationsHolder continuationsHolder;
+    
+    /**
+     * 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;
+
+    protected ServiceManager serviceManager;
+    protected Context context;
+
+    public ContinuationsManagerImpl() throws Exception {
+        try {
+            random = SecureRandom.getInstance("SHA1PRNG");
+        } 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];
+    }
+
+    public void service(ServiceManager manager) throws ServiceException {
+        this.serviceManager = manager;
+    }
+
+    public void configure(Configuration config) {
+        this.defaultTimeToLive = config.getAttributeAsInteger("time-to-live", (3600 * 1000));
+        this.bindContinuationsToSession = config.getAttributeAsBoolean( "session-bound-continuations", false );
+        // create a global ContinuationsHolder if this the "session-bound-continuations" parameter is set to false
+        if (!this.bindContinuationsToSession) {
+            this.continuationsHolder = new WebContinuationsHolder();
+        }
+        
+        final Configuration expireConf = config.getChild("expirations-check");
+        final long initialDelay = expireConf.getChild("offset", true).getValueAsLong(180000);
+        final long interval = expireConf.getChild("period", true).getValueAsLong(180000);
+        try {
+            final RunnableManager runnableManager = (RunnableManager)serviceManager.lookup(RunnableManager.ROLE);
+            runnableManager.execute( new Runnable() {
+                    public void run()
+                    {
+                        expireContinuations();
+                    }
+                }, initialDelay, interval);
+            serviceManager.release(runnableManager);
+        } catch (Exception e) {
+            getLogger().warn("Could not enqueue continuations expiration task. " +
+                             "Continuations will not automatically expire.", e);
+        }
+    }
+
+    
+    public WebContinuation createWebContinuation(Object kont,
+                                                 WebContinuation parent,
+                                                 int timeToLive,
+                                                 String interpreterId, 
+                                                 ContinuationsDisposer disposer) {
+        int ttl = (timeToLive == 0 ? defaultTimeToLive : timeToLive);
+
+        WebContinuation wk = generateContinuation(kont, parent, ttl, interpreterId, disposer);
+        wk.enableLogging(getLogger());
+
+        if (parent == null) {
+            forest.add(wk);
+        } else {
+            handleParentContinuationExpiration(parent);
+        }
+
+        handleLeafContinuationExpiration(wk);
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("WK: Created continuation " + wk.getId());
+        }
+
+        return wk;
+    }
+    
+    /**
+     * 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.
+     */
+    protected void handleLeafContinuationExpiration(WebContinuation wk) {
+        expirations.add(wk);
+    }
+
+    /**
+     * 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);
+        }
+    }    
+    
+    /**
+     * Get a list of all web continuations (data only)
+     */
+    public List getWebContinuationsDataBeanList() {
+        List beanList = new ArrayList();
+        for(Iterator it = this.forest.iterator(); it.hasNext();) {
+            beanList.add(new WebContinuationDataBean((WebContinuation) it.next()));
+        }
+        return beanList;
+    }
+
+    public WebContinuation lookupWebContinuation(String id, String interpreterId) {
+        // REVISIT: Is the following check needed to avoid threading issues:
+        // return wk only if !(wk.hasExpired) ?
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(false);
+        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 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) {
+
+        char[] result = new char[bytes.length * 2];
+        WebContinuation wk = null;
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(true);
+        while (true) {
+            random.nextBytes(bytes);
+
+            for (int i = 0; i < CONTINUATION_ID_LENGTH; i++) {
+                byte ch = bytes[i];
+                result[2 * i] = Character.forDigit(Math.abs(ch >> 4), 16);
+                result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f), 16);
+            }
+
+            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;
+                }
+            }
+        }
+
+        return wk;
+    }
+
+    public void invalidateWebContinuation(WebContinuation wk) {
+        WebContinuationsHolder continuationsHolder = lookupWebContinuationsHolder(false);
+        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();
+    }
+
+    /**
+     * Makes the continuation inaccessible for lookup, and triggers possible needed
+     * cleanup code through the ContinuationsDisposer interface.
+     * @param continuationsHolder
+     *
+     * @param wk the continuation to dispose.
+     */
+    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(continuationsHolder, wk);
+        _detach(wk);
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("WK: Deleted continuation: " + wk.getId());
+        }
+
+        // now check if parent needs to be removed.
+        WebContinuation parent = wk.getParentContinuation();
+        if (null != parent && parent.hasExpired()) {
+            //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>
+     */
+    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())
+                    .append(" ExpireTime [");
+
+            if (lat < System.currentTimeMillis()) {
+                wkSet.append("Expired");
+            } else {
+                wkSet.append(lat);
+            }
+            wkSet.append("]");
+        }
+
+        getLogger().debug(wkSet.toString());
+    }
+
+    /**
+     * Dump to Log file all <code>WebContinuation</code>s
+     * in the system
+     */
+    public void displayAllContinuations() {
+        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();
+
+            /* Continuations before clean up:
+            getLogger().debug("WK: Forest before cleanup: " + forest.size());
+            displayAllContinuations();
+            displayExpireSet();
+            */
+        }
+
+        // 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++;
+        }
+
+        if (getLogger().isDebugEnabled()) {
+            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();
+            */
+        }
+    }
+
+    /**
+     * 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);
+            }
+        }
+    }
+
+    /**
+     * 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) {
+        //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
+        Map objectModel = ContextHelper.getObjectModel(this.context);
+        Request request = ObjectModelHelper.getRequest(objectModel);
+
+        if (!createNew && request.getSession(false) == null)
+            return null;
+
+        Session session = request.getSession(true);
+        WebContinuationsHolder holder = 
+            (WebContinuationsHolder) session.getAttribute(
+                    WebContinuationsHolder.CONTINUATIONS_HOLDER);
+        if (!createNew)
+            return holder;
+
+        if (holder != null)
+            return holder;
+
+        holder = new WebContinuationsHolder();
+        session.setAttribute(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);
+        }
+    }
+
+    public void contextualize(Context context) throws ContextException {
+        this.context = context;        
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/FlowHelper.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/FlowHelper.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/FlowHelper.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/FlowHelper.java Thu Nov  3 05:41:06 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.cocoon.components.flow;
+
+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: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/FlowHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/Interpreter.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/Interpreter.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/Interpreter.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/Interpreter.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,183 @@
+/*
+ * 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.cocoon.components.flow;
+
+import org.apache.cocoon.environment.Redirector;
+
+import java.util.List;
+
+/**
+ * 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;
+        }
+    }
+
+    public static final String ROLE = Interpreter.class.getName();
+
+	/**
+	 * @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
+     */
+    void callFunction(String funName, List params, Redirector redirector)
+    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,
+                            Redirector redirector)
+    throws Exception;
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/flow/Interpreter.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message