You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by cz...@apache.org on 2003/02/28 15:14:26 UTC

cvs commit: xml-cocoon2/src/documentation/xdocs/userdocs/transformers cinclude-transformer.xml

cziegeler    2003/02/28 06:14:25

  Modified:    src/java/org/apache/cocoon/transformation
                        CIncludeTransformer.java
               src/webapp/WEB-INF cocoon.xconf
               src/documentation/xdocs/userdocs/transformers
                        cinclude-transformer.xml
  Added:       src/java/org/apache/cocoon/transformation/helpers
                        PreemptiveLoaderAction.java ExpiresValidity.java
                        IncludeCacheStorageProxy.java
                        IncludeCacheManager.java
                        ModifiableSourceIncludeCacheStorageProxy.java
                        StoreIncludeCacheStorageProxy.java
                        PreemptiveLoader.java
                        IncludeCacheManagerSession.java
                        DefaultIncludeCacheManager.java
  Log:
  Added caching mechanism to the cinclude transformer.
  A cached content can get an expiration date.
  In addition the content can be fetched in parallel or
  even pre-emptive.
  
  Revision  Changes    Path
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/PreemptiveLoaderAction.java
  
  Index: PreemptiveLoaderAction.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.util.Map;
  
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.cocoon.acting.ComposerAction;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  
  /**
   * This action starts the preemptive loader and runs forever.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: PreemptiveLoaderAction.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public class PreemptiveLoaderAction 
      extends ComposerAction
      implements ThreadSafe {
  
      /**
       * This action starts the preemptive loading
       * It runs forever and is stopped by the {@link DefaultCacheManager}.
       * @see org.apache.cocoon.acting.Action#act(Redirector, SourceResolver, Map, String, Parameters)
       */
      public Map act(Redirector redirector,
                      SourceResolver resolver,
                      Map objectModel,
                      String source,
                      Parameters parameters)
      throws Exception {
          PreemptiveLoader loader = PreemptiveLoader.getInstance();
          if (!loader.alive) {
              loader.process(this.manager, resolver, this.getLogger());
              return EMPTY_MAP;
          }
          return null;
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/ExpiresValidity.java
  
  Index: ExpiresValidity.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.cocoon.transformation.helpers;
  
  import org.apache.excalibur.source.SourceValidity;
  
  /**
   * A validation object that holds an expiration date.
   * When the defined time/date has arrived, this validity object is 
   * not valid any more.
   *
   * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   * @version CVS $Revision: 1.1 $ $Date: 2003/02/28 14:14:24 $
   * @since 2.1
   */
  public final class ExpiresValidity
      implements SourceValidity
  {
      private long expires;
  
      /**
       * Constructir
       * @param expires The delta from now when this validity object gets invalid.
       */
      public ExpiresValidity( long expires ) {
          this.expires = System.currentTimeMillis() + expires;
      }
  
      /**
       * Checks if the expires date is already reached.
       * 
       * @see org.apache.excalibur.source.SourceValidity#isValid()
       */
      public int isValid() {
          final long currentTime = System.currentTimeMillis();
          return (currentTime <= this.expires ? SourceValidity.VALID : SourceValidity.INVALID);
      }
  
      /**
       * This method is never invoked as {@link #isValid} can always perform
       * the complete check.
       * 
       * @see org.apache.excalibur.source.SourceValidity#isValid(SourceValidity)
       */
      public int isValid( SourceValidity newValidity ) {
          return SourceValidity.INVALID;
      }
  
      /**
       * 
       * @see java.lang.Object#toString()
       */
      public String toString() {
          return "ExpiresValidity: " + expires;
      }
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/IncludeCacheStorageProxy.java
  
  Index: IncludeCacheStorageProxy.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  import java.io.Serializable;
  
  /**
   * A CacheStorageProxy is an interface object between the {@link CacheManager}
   * and the real store caching the content.
   * Currently you can use the {@link DefaultCacheStorageProxy} that uses the
   * usual store or the {@link ModifiableSourceCacheStorageProxy} that
   * uses a configured source.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: IncludeCacheStorageProxy.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public interface IncludeCacheStorageProxy {
  
      /**
       * Get the cached content for the given URI.
       * @param uri Absolute URI specifying the content
       * @return Serializable
       */
      Serializable get(String uri);
      
      /**
       * Put the content into the cache for the given URI.
       * @param uri Absolute URI specifying the content
       * @param object The content
       * @throws IOException
       */
      void put(String uri, Serializable object)
      throws IOException;
      
      /**
       * Remove the cached content for the given URI
       * @param uri Absolute URI specifying the content
       */
      void remove(String uri);
      
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/IncludeCacheManager.java
  
  Index: IncludeCacheManager.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.cocoon.xml.XMLConsumer;
  import org.apache.excalibur.source.SourceException;
  import org.xml.sax.SAXException;
  
  /**
   * The include cache manager is a component that can manage included content.
   * It can eiter load them in parallel or pre-emptive and cache the content
   * for a given period of time.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: IncludeCacheManager.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public interface IncludeCacheManager {
  
      /** Avalon role */
      String ROLE = IncludeCacheManager.class.getName();
      
      /**
       * Create a session for this request.
       * This should be invoked first and only one per request. It is required
       * to terminate the session with {@link #terminateSession(CacheManagerSession)}
       * @param pars The configuration
       * @return CacheManagerSession The session that should be used with all other commands.
       */
      IncludeCacheManagerSession getSession(Parameters pars);
      
      /**
       * This informs the manager that a URI should be "loaded".
       * @param uri     The URI to load (maybe relative)
       * @param session The corresponding session created by {@link #getSession(Parameters)}
       * @return String The absolute URI that must be used for {@link #stream(String, CacheManagerSession, XMLConsumer)}
       * @throws IOException
       * @throws SourceException
       */
      String load(String  uri, 
                  IncludeCacheManagerSession session)
      throws IOException, SourceException;
                
      /**
       * Stream the content of the absolute URI.
       * Depending on the configuration and state of the cache, the
       * content is either taken from the cache, fetched etc.
       * @param uri     The absolute URI returned by {@link #load(String, CacheManagerSession)}
       * @param session The current session
       * @param handler The receiver of the SAX events
       * @throws IOException
       * @throws SourceException
       * @throws SAXException
       */
      void stream(String uri,
                   IncludeCacheManagerSession session,
                   XMLConsumer handler)
      throws IOException, SourceException, SAXException;
                   
      /**
       * Terminate the session. This method must be executed at the end of the
       * request.
       * @param session The caching session.
       */
      void terminateSession(IncludeCacheManagerSession session);
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/ModifiableSourceIncludeCacheStorageProxy.java
  
  Index: ModifiableSourceIncludeCacheStorageProxy.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.OutputStream;
  import java.io.Serializable;
  
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.cocoon.CascadingIOException;
  import org.apache.cocoon.util.HashUtil;
  import org.apache.excalibur.source.ModifiableSource;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceResolver;
  
  /**
   * This is the interface between the {@link CacheManager} and a
   * {@link Source} object that stores the cached content in a directory
   * manner.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: ModifiableSourceIncludeCacheStorageProxy.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public final class ModifiableSourceIncludeCacheStorageProxy
      implements IncludeCacheStorageProxy {
  
      private SourceResolver resolver;
      private String         parentURI;
      private Logger         logger;
      
      /**
       * Constructor
       * @param resolver   For source resolving
       * @param parentURI  The "directory"
       * @param logger     A logger for debugging
       */
      public ModifiableSourceIncludeCacheStorageProxy(SourceResolver resolver,
                                               String         parentURI,
                                               Logger         logger) {
          this.resolver = resolver;
          this.parentURI= parentURI;
          this.logger = logger;
      }
      
      /**
       * Calculate the URI for a child
       * @param uri     Child URI
       * @return String Absolute URI
       */
      private String getURI(String uri) {
          final long hash = HashUtil.hash(uri);
          final StringBuffer buffer = new StringBuffer(this.parentURI);
          buffer.append('/');
          if (hash < 0) {
              buffer.append('M').append(hash * -1);
          } else {
              buffer.append(hash);
          }
          buffer.append(".cxml");
          return buffer.toString();
      }
      
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#get(java.lang.String)
       */
      public Serializable get(String uri) {
          if (logger.isDebugEnabled()) {
              logger.debug("WSCProxy: Getting content for " + uri);
          }
  
          Source child = null;
          Serializable result = null;
          try {
              child = this.resolver.resolveURI(this.getURI(uri));
  
              if (logger.isDebugEnabled()) {
                  logger.debug("WSCProxy: Resolved to " + child.getURI());
              }
  
              if (child.exists()) {
                  InputStream is = child.getInputStream();
                  ObjectInputStream ois = new ObjectInputStream(is);
                  result = (Serializable)ois.readObject();
                  ois.close();
              }
          } catch (Exception ignore) {
          } finally {
              this.resolver.release( child );
          }
  
          if (logger.isDebugEnabled()) {
              logger.debug("WSCProxy: Result for " + uri + " : " + (result == null ? "Not in cache" : "Found"));
          }
          return result;
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#put(java.lang.String, java.io.Serializable)
       */
      public void put(String uri, Serializable object) 
      throws IOException {
          if (logger.isDebugEnabled()) {
              logger.debug("WSCProxy: Storing content for " + uri);
          }
          Source child = null;
          try {
              child = this.resolver.resolveURI(this.getURI(uri));
  
              if (logger.isDebugEnabled()) {
                  logger.debug("WSCProxy: Resolved to " + child.getURI());
              }
  
              OutputStream os;
              if (child instanceof ModifiableSource) {
                  os = ((ModifiableSource)child).getOutputStream();
              } else {
                  throw new IOException("Source " + uri + " is not writeable.");
              }
              ObjectOutputStream oos = new ObjectOutputStream(os);
              oos.writeObject(object);
              oos.flush();
              oos.close();
          } catch (IOException io) {
              throw io;
          } catch (Exception ignore) {
              throw new CascadingIOException("Exception.", ignore);
          } finally {
              this.resolver.release( child );
          }
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#remove(java.lang.String)
       */
      public void remove(String uri) {
          if (logger.isDebugEnabled()) {
              logger.debug("WSCProxy: Removing content for " + uri);
          }
          Source child = null;
          try {
              child = this.resolver.resolveURI(this.getURI(uri));
  
              if (logger.isDebugEnabled()) {
                  logger.debug("WSCProxy: Resolved to " + child.getURI());
              }
              OutputStream os;
              if (child instanceof ModifiableSource) {                
                  ((ModifiableSource)child).delete();
              } else {
                  throw new IOException("Source " + uri + " is not writeable.");
              }
          } catch (Exception ignore) {
          } finally {
              this.resolver.release( child );
          }
      }
  
      /**
       * Compare
       */
      public boolean equals(Object object) {
          if (object instanceof ModifiableSourceIncludeCacheStorageProxy) {
              return this.parentURI.equals(((ModifiableSourceIncludeCacheStorageProxy)object).parentURI);
          }
          return false;
      }
  
      /**
       * Generate a hash code
       */
      public int hashCode() {
          return this.parentURI.hashCode();
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/StoreIncludeCacheStorageProxy.java
  
  Index: StoreIncludeCacheStorageProxy.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  import java.io.Serializable;
  
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.excalibur.store.Store;
  
  /**
   * This is the interface between the {@link CacheManager} and the usual
   * store.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: StoreIncludeCacheStorageProxy.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public final class StoreIncludeCacheStorageProxy
      implements IncludeCacheStorageProxy {
  
      private Store  store;
      
      private Logger logger;
      
      /**
       * Constructor
       * @param store  The store for the cached content
       * @param logger A logger for debugging
       */
      public StoreIncludeCacheStorageProxy(Store store, Logger logger) {
          this.store = store;
          this.logger = logger;
      }
      
      /** A string representation for a key */
      private String getKey(String uri) {
          return "DCS:" + uri;
      }
      
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#get(java.lang.String)
       */
      public Serializable get(String uri) {
          if (logger.isDebugEnabled()) {
              logger.debug("StoreProxy: Getting content for " + uri);
          }
  
          Serializable result = (Serializable)this.store.get(this.getKey(uri));
  
          if (logger.isDebugEnabled()) {
              logger.debug("StoreProxy: Result for " + uri + " : " + (result == null ? "Not in cache" : "Found"));
          }
          return result;
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#put(java.lang.String, java.io.Serializable)
       */
      public void put(String uri, Serializable object) 
      throws IOException {
          if (logger.isDebugEnabled()) {
              logger.debug("StoreProxy: Storing content for " + uri);
          }
          this.store.store(this.getKey(uri), object);
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheStorageProxy#remove(java.lang.String)
       */
      public void remove(String uri) {
          if (logger.isDebugEnabled()) {
              logger.debug("StoreProxy: Removing content for " + uri);
          }
          this.store.remove(this.getKey(uri));
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/PreemptiveLoader.java
  
  Index: PreemptiveLoader.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.cocoon.caching.CachedResponse;
  import org.apache.cocoon.components.sax.XMLSerializer;
  import org.apache.cocoon.components.source.SourceUtil;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceResolver;
  import org.apache.excalibur.source.SourceValidity;
  
  /**
   * The preemptive loader is a singleton that runs in the background
   * and loads content into the cache.
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: PreemptiveLoader.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public final class PreemptiveLoader {
  
      private static final PreemptiveLoader instance = new PreemptiveLoader();
      
      /** The list of proxies currently used for caching */
      private Map   cacheStorageProxyMap = new HashMap(20);
      /** The list of URIs to load */
      private List  loadList = new ArrayList(50);
      /** Is this thread still alive? */
      boolean alive = false;
      
      /**
       * Return singleton.
       * @return PreemptiveLoader
       */
      static PreemptiveLoader getInstance() {
          return instance;
      }
      
      /**
       * Add a new task
       * @param proxy   The cache to store the content
       * @param uri     The absolute URI to load
       * @param expires The expires information used for the cache
       */
      public void add(IncludeCacheStorageProxy proxy, String uri, long expires) {
          boolean addItem = true;
          List uriList = (List)this.cacheStorageProxyMap.get(proxy);
          if ( null == uriList ) {
               uriList = new ArrayList(50);
               this.cacheStorageProxyMap.put(proxy, uriList);
          } else {
              synchronized (uriList) {
                  // nothing to do: uri is alredy in list
                 if (uriList.contains(uri)) {
                     addItem = false;
                 } 
              }
          }
          if ( addItem ) {
              uriList.add(uri);
              this.loadList.add(new Object[] {proxy, uri, new Long(expires), uriList});
          }
  
          synchronized (this.cacheStorageProxyMap) {
              this.cacheStorageProxyMap.notify();
          }
      }
      
      /**
       * Start the preemptive loading
       * @param manager   A component manager
       * @param resolver  A source resolver
       * @param logger    A logger
       */
      public void process(ComponentManager manager,
                           SourceResolver  resolver,
                           Logger          logger) {
          this.alive = true;
          if (logger.isDebugEnabled()) {
              logger.debug("PreemptiveLoader: Starting preemptive loading");
          }
  
          while (this.alive) {
              while (this.loadList.size() > 0) {
                  Object[] object = (Object[])this.loadList.get(0);
                  final String uri = (String)object[1];
                  this.loadList.remove(0);
                  synchronized (object[3]) {
                      ((List)object[3]).remove(uri);
                  }
                  
                  Source source = null;
                  XMLSerializer serializer = null;
  
                  try {
                      if (logger.isDebugEnabled()) {
                          logger.debug("PreemptiveLoader: Loading " + uri);
                      }
  
                      source = resolver.resolveURI(uri);
                      serializer = (XMLSerializer)manager.lookup(XMLSerializer.ROLE);
                  
                      SourceUtil.toSAX(source, serializer,  manager);
                  
                      SourceValidity[] validities = new SourceValidity[1];
                      validities[0] = new ExpiresValidity(((Long)object[2]).longValue() * 1000); // milliseconds!
                      CachedResponse response = new CachedResponse(validities,
                                                                   (byte[])serializer.getSAXFragment());
                      ((IncludeCacheStorageProxy)object[0]).put(uri, response);
                       
                  } catch (Exception ignore) {
                      // all exceptions are ignored!
                  } finally {
                      resolver.release( source );
                      manager.release( serializer );
                  }
                  if (logger.isDebugEnabled()) {
                      logger.debug("PreemptiveLoader: Finished loading " + uri);
                  }
              }
              synchronized (this.cacheStorageProxyMap) {
                  try {
                      this.cacheStorageProxyMap.wait();
                  } catch (InterruptedException e) {
                  }
              }
          }
          if (logger.isDebugEnabled()) {
              logger.debug("PreemptiveLoader: Finished preemptive loading");
          }
      }
      
      /**
       * Stop the loading. 
       * The loader stops when all tasks from the queue are processed.
       */
      synchronized public void stop() {
          this.alive = false;
          synchronized (this.cacheStorageProxyMap) {
              this.cacheStorageProxyMap.notify();
          }
      }
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/IncludeCacheManagerSession.java
  
  Index: IncludeCacheManagerSession.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceResolver;
  import org.apache.excalibur.source.SourceValidity;
  
  /**
   * This object encapsulates a "caching session". A caching session has the
   * duration of one single request.
   * This object is used by the {@link CacheManager} and holds all required
   * configuration for performing the caching of this request.
   * 
   * The session can be configured during construction with the following parameters:
   * - purge (boolean/false) : Turn on/off purging the cache
   * - preemptive (boolean/false) : Turn on/off preemptive caching
   * - parallel (boolean/false) : Turn on/off parallel processing
   * - expires (long/0) : The lifetime of the cached content
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: IncludeCacheManagerSession.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public final class IncludeCacheManagerSession {
  
      /** The expires information */
      private long expires;
      
      /** Should we purge the cache */
      private boolean purge;
      
      /** Should we load preemptive */
      private boolean preemptive;
      
      /** Should we process everything in parallel */
      private boolean parallel;
  
      /** The used {@link CacheStorageProxy} */
      private IncludeCacheStorageProxy storage;
      
      /** The list of all threads */
      private Map threadList;
      
      /** Cache the expires validity object */
      private SourceValidity validity;
      
      /** Cache the source objects */
      private Map sourceList = new HashMap(10);;
      
      /**
       * Constructor
       * @param configuration The parameters configuring this session
       * @param proxy         The proxy used to cache the data
       */
      IncludeCacheManagerSession(Parameters configuration, 
                          IncludeCacheStorageProxy proxy) {
          this.expires = configuration.getParameterAsLong("expires", 0);
          this.purge = configuration.getParameterAsBoolean("purge", false);    
          this.preemptive = configuration.getParameterAsBoolean("preemptive", false);
          this.parallel = configuration.getParameterAsBoolean("parallel", false);
          this.storage = proxy;    
      }
      
      /**
       * Get the used storage proxy
       */
      IncludeCacheStorageProxy getCacheStorageProxy() {
          return this.storage;
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheManagerSession#expires()
       */
      public long getExpires() {
          return this.expires;
      }
  
      public SourceValidity getExpiresValidity() {
          if ( this.expires > 0 && this.validity == null) {
              this.validity = new ExpiresValidity( this.expires * 1000 ); // milliseconds
          }
          return this.validity;
      }
      
      /**
       * Is the cache purged?
       */
      public boolean isPurging() {
          return this.purge;
      }
  
      /**
       * Do we use preemptive caching?
       */
      public boolean isPreemptive() {
          return this.preemptive;
      }
  
      /**
       * Do we process the includes in parallel?
       */
      public boolean isParallel() {
          return this.parallel;
      }
  
      /**
       * Add another object to the thread list
       * @param uri    The absolute URI
       * @param object The thread
       */
      void add(String uri, Object object) {
          if ( null == this.threadList ) {
              this.threadList = new HashMap(10);
          }
          this.threadList.put(uri, object);
      }
      
      /**
       * Get the thread object.
       * @param uri     The URI
       * @return Object The thread.
       */
      Object get(String uri) {
          if ( null != this.threadList ) {
              return this.threadList.get( uri );
          }
          return null;
      }
      
      /**
       * Turn off/on preemptive caching
       */
      void setPreemptive(boolean value) {
          this.preemptive = value;
      }
      
      /**
       * Lookup a source object and cache it
       * @param uri     Absolute URI
       * @return Source The source obejct
       */
      public Source resolveURI(String uri, SourceResolver resolver) 
      throws IOException {
          Source source = (Source)this.sourceList.get(uri);
          if ( null == source ) {
              source = resolver.resolveURI( uri );
              this.sourceList.put( source.getURI(), source );
          }
          return source;
      }
      
      /**
       * Cleanup
       * @param resolver The source resolver to release cached sources
       */
      void cleanup(SourceResolver resolver) {
          Iterator iter = this.sourceList.values().iterator();
          while ( iter.hasNext() ) {
              final Source source = (Source) iter.next();
              resolver.release( source );   
          }
      }
      
      /**
       * Print a representation of this object
       */
      public String toString() {
          return "CacheManagerSession(" + this.hashCode() + ") -" +
                  " expires: " + this.expires +
                  " parallel: " + this.parallel + 
                  " preemptive: " + this.preemptive +
                  " purge: " + this.purge;
      }
  }
  
  
  
  1.1                  xml-cocoon2/src/java/org/apache/cocoon/transformation/helpers/DefaultIncludeCacheManager.java
  
  Index: DefaultIncludeCacheManager.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.cocoon.transformation.helpers;
  
  import java.io.IOException;
  import java.net.URL;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.parameters.ParameterException;
  import org.apache.avalon.framework.parameters.Parameterizable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.caching.CachedResponse;
  import org.apache.cocoon.components.sax.XMLDeserializer;
  import org.apache.cocoon.components.sax.XMLSerializer;
  import org.apache.cocoon.components.sax.XMLTeePipe;
  import org.apache.cocoon.components.source.SourceUtil;
  import org.apache.cocoon.xml.XMLConsumer;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceException;
  import org.apache.excalibur.source.SourceResolver;
  import org.apache.excalibur.source.SourceValidity;
  import org.apache.excalibur.store.Store;
  import org.xml.sax.SAXException;
  
  /**
   * Default implementation of a {@link CacheManager}.
   * 
   * This implementation requires a configuration, if preemptive
   * loading is used:
   * &lt;parameter name="preemptive-loader-url" value="some url"/&gt;
   * 
   * This is a url inside cocoon, that contains the preemptive loader
   * url; it must be specified absolute (with http://...)
   * If this loader cannot be started, only an error is logged into the
   * log, so actually cached content is never updated!
   * 
   *  @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   *  @version CVS $Id: DefaultIncludeCacheManager.java,v 1.1 2003/02/28 14:14:24 cziegeler Exp $
   *  @since   2.1
   */
  public final class DefaultIncludeCacheManager
      extends AbstractLogEnabled
      implements IncludeCacheManager, 
                  ThreadSafe, 
                  Composable, 
                  Disposable,
                  Parameterizable, 
                  Component {
  
      private ComponentManager manager;
      
      private SourceResolver   resolver;
      
      private Store            store;
      
      private IncludeCacheStorageProxy defaultCacheStorage;
      
      private String            preemptiveLoaderURI;
      
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheManager#getSession(org.apache.avalon.framework.parameters.Parameters)
       */
      public IncludeCacheManagerSession getSession(Parameters pars) {
          String sourceURI = pars.getParameter("source", null);
          IncludeCacheManagerSession session;
          if ( null == sourceURI ) {
              session = new IncludeCacheManagerSession(pars, this.defaultCacheStorage);
          } else {
              Source source = null;
              try {
                  source = this.resolver.resolveURI(sourceURI);
                  IncludeCacheStorageProxy proxy = new ModifiableSourceIncludeCacheStorageProxy(this.resolver, source.getURI(), this.getLogger());
                  session = new IncludeCacheManagerSession(pars, proxy);
              } catch (Exception local) {
                  session = new IncludeCacheManagerSession(pars, this.defaultCacheStorage);
                  this.getLogger().warn("Error creating writeable source.", local);
              } finally {
                  this.resolver.release(source);
              }
          }
          if (session.isPreemptive()) {
              if ( null == this.preemptiveLoaderURI ) {
                  this.getLogger().error("Preemptive loading is turned off because the preemptive-loader-url is not configured.");
                  session.setPreemptive(false);
              } else {
                  if ( !PreemptiveLoader.getInstance().alive ) {
  
                      if (this.getLogger().isDebugEnabled()) {
                          this.getLogger().debug("Booting preemptive loader: " + this.preemptiveLoaderURI);
                      }
                      PreemptiveBooter thread = new PreemptiveBooter();
                      thread.setURI(this.preemptiveLoaderURI);
                      thread.start();
                      Thread.yield();
                  }
              }
          } 
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Creating cache manager session: " + session);
          }
          return session;
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheManager#load(java.lang.String, boolean, de.sundn.prod.sunshine.transformation.CacheManagerSession)
       */
      public String load(String uri,
                          IncludeCacheManagerSession session) 
      throws IOException, SourceException {
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Load " + uri + " for session " + session);
          }
          
          // first make the URI absolute
          if ( uri.indexOf("://") == -1) {
              final Source source = session.resolveURI(uri, this.resolver);
              uri = source.getURI();
          }
          
          // if we are not processing in parallel (or do preemptive)
          // then we don't have to do anything in this method - everything
          // is done in the stream method.
          
          // if we are processing in parallel (and not preemptive) then....
          if ( session.isParallel() && !session.isPreemptive()) {
              
              // first look-up if we have a valid stored response
              IncludeCacheStorageProxy storage = session.getCacheStorageProxy();
              CachedResponse response = (CachedResponse)storage.get(uri);
              if ( null != response) {
                  SourceValidity[] validities = response.getValidityObjects();
                  
                  // if we are valid and do not purging
                  if ( !session.isPurging() 
                        && validities[0].isValid() == SourceValidity.VALID) {
                      if (this.getLogger().isDebugEnabled()) {
                          this.getLogger().debug("Using cached response for parallel processing.");
                      }
                      session.add(uri, response.getResponse());
                      return uri;
                  } else {
                      // response is not used
                      storage.remove(uri);
                  }
              }
  
              if (this.getLogger().isDebugEnabled()) {
                  this.getLogger().debug("Starting parallel thread for loading " + uri);
              }
              // now we start a parallel thread, this thread gets all required avalon components
              // so it does not have to lookup them by itself
              try {
                  XMLSerializer serializer = (XMLSerializer)this.manager.lookup(XMLSerializer.ROLE);
                  Source source = session.resolveURI(uri, this.resolver);
  
                  LoaderThread loader = new LoaderThread(source, serializer, this.manager);
                  Thread thread = new Thread(loader);
                  session.add(uri, loader);
                  thread.start();
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug("Thread started for " + uri);
                  }
              } catch (ComponentException ce) {
                  throw new SourceException("Unable to lookup thread pool or xml serializer.", ce);
              } catch (Exception e) {
                  throw new SourceException("Unable to get pooled thread.", e);
              }
          }
          return uri;
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheManager#stream(java.lang.String, de.sundn.prod.sunshine.transformation.CacheManagerSession, org.xml.sax.ContentHandler)
       */
      public void stream(String uri,
                          IncludeCacheManagerSession session,
                          XMLConsumer handler) 
      throws IOException, SourceException, SAXException {
  
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Stream " + uri + " for session " + session);
          }
  
          // if we are processing in parallel (and not preemptive) then....
          if ( session.isParallel() && !session.isPreemptive()) {
              
              // get either the cached content or the pooled thread
              Object object = session.get(uri);
              
              if ( null == object ) {
                  // this should never happen!
                  throw new SAXException("No pooled thread found for " + uri);
              }
              byte[] result;
              
              // is this a pooled thread?
              if (object instanceof LoaderThread) {
                  LoaderThread loader = (LoaderThread)object;
                  
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug("Waiting for pooled thread to finish loading.");
                  }
  
                  // wait
                  while (!loader.finished) {                    
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
                      }
                  }
  
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug("Pooled thread finished loading.");
                  }
                  
                  // did an exception occur? Then reraise it
                  if ( null != loader.exception) {
                      if ( loader.exception instanceof SAXException ) {
                          throw (SAXException)loader.exception;
                      } else if (loader.exception instanceof SourceException ) {
                          throw (SourceException)loader.exception;
                      } else if (loader.exception instanceof IOException) {
                          throw (IOException)loader.exception;
                      } else {
                          throw new SAXException("Exception.", loader.exception);
                      }
                  }
                  
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug("Streaming from pooled thread.");
                  }
                  result = loader.content;
  
                  // cache the response (remember preemptive is off)
                  if (session.getExpires() > 0) {
                      SourceValidity[] validities = new SourceValidity[1];
                      validities[0] = session.getExpiresValidity();
                      CachedResponse response = new CachedResponse(validities, result);
                      session.getCacheStorageProxy().put(uri, response);
                  }
              } else {
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug("Streaming from cached response.");
                  }
  
                  // use the response from the cache
                  result = (byte[])object;
              }
              
              // stream the content
              XMLDeserializer deserializer = null;
              try {
                  deserializer = (XMLDeserializer)this.manager.lookup( XMLDeserializer.ROLE );
                  deserializer.setConsumer(handler);
                  deserializer.deserialize(result);
              } catch (ComponentException ce) {
                  throw new SAXException("Unable to lookup xml deserializer.", ce);
              } finally {
                  this.manager.release( deserializer );
              }
              return;
              
          } else {
              // we are not processing parallel
              
              // first: test for a cached response
              IncludeCacheStorageProxy storage = session.getCacheStorageProxy();
              CachedResponse response = (CachedResponse)storage.get(uri);
              if ( null != response) {
                  SourceValidity[] validities = response.getValidityObjects();
                  // if purging is turned off and either the cached response is valid or
                  // we are loading preemptive, then use the cached response
                  if ( !session.isPurging() 
                        && (session.isPreemptive() || validities[0].isValid() == SourceValidity.VALID)) {
  
                      // stream the content                    
                      if (this.getLogger().isDebugEnabled()) {
                          this.getLogger().debug("Streaming from cached response.");
                      }
                      XMLDeserializer deserializer = null;
                      try {
                          deserializer = (XMLDeserializer)this.manager.lookup( XMLDeserializer.ROLE );
                          deserializer.setConsumer(handler);
                          deserializer.deserialize(response.getResponse());
                      } catch (ComponentException ce) {
                          throw new SAXException("Unable to lookup xml deserializer.", ce);
                      } finally {
                          this.manager.release( deserializer );
                      }
                      
                      // load preemptive if the response is not valid
                      if ( session.getExpires() > 0
                           && session.isPreemptive() 
                           && validities[0].isValid() != SourceValidity.VALID) {
                          if (this.getLogger().isDebugEnabled()) {
                              this.getLogger().debug("Add uri to preemptive loader list " + uri);
                          }
                          if (!PreemptiveLoader.getInstance().alive) {
                              this.getLogger().error("Preemptive loader has not started yet.");
                          }
                          PreemptiveLoader.getInstance().add(session.getCacheStorageProxy(), uri, session.getExpires());
                      }
                      return;
   
                  } else {
                      // cached response is not valid
                      storage.remove(uri);
                  }
              }
          }
  
          // we are not processing in parallel and have no (valid) cached response
          XMLSerializer serializer = null;
          try {
              final Source source = session.resolveURI(uri, this.resolver);
              
              // stream directly (and cache the response)
              if (this.getLogger().isDebugEnabled()) {
                  this.getLogger().debug("Streaming directly from source.");
              }
              if (session.getExpires() > 0) {
                  serializer = (XMLSerializer)this.manager.lookup(XMLSerializer.ROLE);
                  XMLTeePipe tee = new XMLTeePipe(handler, serializer);
                  
                  SourceUtil.toSAX(source, tee,  this.manager);
                  
                  SourceValidity[] validities = new SourceValidity[1];
                  validities[0] = session.getExpiresValidity();
                  CachedResponse response = new CachedResponse(validities,
                                                               (byte[])serializer.getSAXFragment());
                  session.getCacheStorageProxy().put(uri, response);
              } else {
                  SourceUtil.toSAX(source, handler,  this.manager);
              }
              
          } catch (ProcessingException pe) {
              throw new SAXException("ProcessingException", pe);
          } catch (ComponentException e) {
              throw new SAXException("Unable to lookup xml serializer.", e);
          }
      }
  
      /**
       * @see de.sundn.prod.sunshine.transformation.CacheManager#terminateSession(de.sundn.prod.sunshine.transformation.CacheManagerSession)
       */
      public void terminateSession(IncludeCacheManagerSession session) {
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Terminating cache manager session " + session);
          }
          session.cleanup(this.resolver);
      }
  
      /**
       * @see org.apache.avalon.framework.component.Composable#compose(org.apache.avalon.framework.component.ComponentManager)
       */
      public void compose(ComponentManager manager) throws ComponentException {
          this.manager = manager;
          this.resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
          this.store = (Store)this.manager.lookup(Store.ROLE);
          this.defaultCacheStorage = new StoreIncludeCacheStorageProxy(this.store, this.getLogger());
      }
  
      /**
       * @see org.apache.avalon.framework.activity.Disposable#dispose()
       */
      public void dispose() {
          // stop preemptive loader (if running)
          PreemptiveLoader.getInstance().stop();
          if ( null != this.manager ) {
              this.manager.release( this.resolver);
              this.manager.release(this.store);
              this.store = null;
              this.resolver = null;
              this.manager = null;
              this.defaultCacheStorage = null;
          }
      }
  
      /**
       * @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters)
       */
      public void parameterize(Parameters parameters) throws ParameterException {
          this.preemptiveLoaderURI = parameters.getParameter("preemptive-loader-url", null);
          if ( null != this.preemptiveLoaderURI 
               && this.preemptiveLoaderURI.indexOf("://") == -1) {
              throw new ParameterException("The preemptive-loader-url must be absolute: " + this.preemptiveLoaderURI);
          }
      }
  
  }
  
  final class LoaderThread implements Runnable {
      
      private  Source source;
      private  XMLSerializer serializer;
      private  ComponentManager manager;
      boolean  finished;
      Exception exception;
      byte[]    content;
  
      public LoaderThread(Source source, 
                           XMLSerializer serializer, 
                           ComponentManager manager) {
          this.source = source;
          this.serializer = serializer;
          this.manager = manager;
          this.finished = false;
      }
      
      public void run() {
          try {
              SourceUtil.toSAX(this.source, this.serializer, this.manager);
              this.content = (byte[])this.serializer.getSAXFragment();
          } catch (Exception local) {
              this.exception = local;
          } finally {
              this.finished = true;
          }
      }
      
  }
  
  final class PreemptiveBooter extends Thread {
  
      private String uri;
      
      void setURI(String uri) {
          this.uri = uri;
      }
      
      public void run() {
          try {
              URL url = new URL(this.uri);
              url.getContent();
          } catch (Exception ignore) {
          }
      }
  }
  
  
  1.24      +242 -10   xml-cocoon2/src/java/org/apache/cocoon/transformation/CIncludeTransformer.java
  
  Index: CIncludeTransformer.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/transformation/CIncludeTransformer.java,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- CIncludeTransformer.java	31 Jan 2003 22:51:56 -0000	1.23
  +++ CIncludeTransformer.java	28 Feb 2003 14:14:25 -0000	1.24
  @@ -51,17 +51,25 @@
   package org.apache.cocoon.transformation;
   
   import org.apache.excalibur.xml.xpath.XPathProcessor;
  +import org.apache.avalon.framework.activity.Disposable;
   import org.apache.avalon.framework.component.Component;
   import org.apache.avalon.framework.component.ComponentException;
  +import org.apache.avalon.framework.component.ComponentManager;
   import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.cocoon.ProcessingException;
  +import org.apache.cocoon.caching.CacheableProcessingComponent;
  +import org.apache.cocoon.components.sax.XMLDeserializer;
  +import org.apache.cocoon.components.sax.XMLSerializer;
   import org.apache.cocoon.components.source.SourceUtil;
   import org.apache.cocoon.environment.SourceResolver;
  +import org.apache.cocoon.transformation.helpers.IncludeCacheManager;
  +import org.apache.cocoon.transformation.helpers.IncludeCacheManagerSession;
   import org.apache.cocoon.xml.IncludeXMLConsumer;
   import org.apache.cocoon.xml.XMLUtils;
   import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
   import org.apache.excalibur.source.SourceParameters;
  +import org.apache.excalibur.source.SourceValidity;
   import org.apache.excalibur.xml.dom.DOMParser;
   import org.w3c.dom.Document;
   import org.w3c.dom.Node;
  @@ -74,6 +82,7 @@
   import org.xml.sax.helpers.AttributesImpl;
   
   import java.io.IOException;
  +import java.io.Serializable;
   import java.util.Map;
   
   /**
  @@ -106,12 +115,45 @@
    *     </cinclude:parameters>
    * </cinclude:includexml>
    * 
  + *
  + * This transformer also supports caching of the included content.
  + * Therefore it triggers for the element <code>cached-include</code> in the
  + * namespace "http://apache.org/cocoon/include/1.0".
  + * The <code>src</code> attribute contains the url which points to
  + * an xml resource which is include instead of the element.
  + * First, it works like the usual include command. But it can be 
  + * configured with various parameters:
  + * The most important one is the <code>expires</code> parameter.
  + * If (and only if) this is set to a value greater than zero,
  + * all included content is cached for the given period of time.
  + * So if any other request includes the same URI, the content
  + * is fetched from the cache. The expires value is in seconds.
  + * Usually the content is cached in the usual store, but you
  + * can also define a writeable source with the <code>source</code> parameter,
  + * e.g. "file:/c:/temp". Then the cached content is written into this
  + * directory.
  + * With the optional <code>purge</code> set to <code>true</code>
  + * the cache is purged which means the cached content is regarded as
  + * invalid nevertheless if it has expired or not.
  + * With the optional parameter <code>parallel</code> the various
  + * included contents are processed (included) in parallel rather than
  + * in a series.
  + * With the optional parameter <code>preemptive</code> set to <code>true</code>
  + * a pre-emptive caching is activated. When a resource is requested with
  + * pre-emptive caching, this transformer always attempts to get the 
  + * content from the cache. If the content is not in the cache, it is
  + * of course retrieved from the original source and cached.
  + * If the cached resource has expired, it is still provided. The cache
  + * is updated by a background task. This task has to be started
  + * beforehand.
  + * 
    * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
    * @author <a href="mailto:acoliver@apache.org">Andrew C. Oliver</a>
    * @version CVS $Id$
    */
   public class CIncludeTransformer 
  -extends AbstractSAXTransformer {
  +extends AbstractSAXTransformer
  +implements Disposable, CacheableProcessingComponent {
   
       public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0";
       public static final String CINCLUDE_INCLUDE_ELEMENT = "include";
  @@ -130,6 +172,9 @@
       public static final String CINCLUDE_NAME_ELEMENT          = "name";
       public static final String CINCLUDE_VALUE_ELEMENT         = "value";
   
  +    public static final String CINCLUDE_CACHED_INCLUDE_ELEMENT = "cached-include";
  +    protected static final String CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT = "cached-includep";
  +
       private static final int STATE_OUTSIDE   = 0;
       private static final int STATE_INCLUDE   = 1;
   
  @@ -142,7 +187,24 @@
       /** The current state: STATE_ */
       protected int state;
       
  -    /**
  +    protected IncludeCacheManager cacheManager;
  +
  +    protected IncludeCacheManagerSession cachingSession;
  +
  +    protected boolean compiling;
  +    
  +    protected IncludeXMLConsumer filter = new IncludeXMLConsumer(this);
  +    
  +    protected XMLSerializer recorder;
  +    
  +    protected AttributesImpl srcAttributes = new AttributesImpl();
  +    
  +    protected boolean supportCaching;
  +    
  +    /** Remember the start time of the request for profiling */
  +    protected long startTime;
  +    
  +   /**
        * Constructor
        * Set the namespace
        */
  @@ -158,15 +220,49 @@
       throws ProcessingException, SAXException, IOException {
           super.setup(resolver, objectModel, source, parameters);
           this.state = STATE_OUTSIDE;
  +        this.cachingSession = this.cacheManager.getSession( this.parameters );
  +        this.compiling = false;
  +        this.supportCaching = parameters.getParameterAsBoolean("support-caching", false);
  +        if (this.getLogger().isErrorEnabled()) {
  +            this.getLogger().debug("Starting CIncludeTransformer with session " + this.cachingSession);
  +            this.startTime = System.currentTimeMillis();
  +        }
  +    }
  +
  +    /**
  +     * @see org.apache.avalon.framework.component.Composable#compose(org.apache.avalon.framework.component.ComponentManager)
  +     */
  +    public void compose(ComponentManager manager) throws ComponentException {
  +        super.compose(manager);
  +        this.cacheManager = (IncludeCacheManager) this.manager.lookup( IncludeCacheManager.ROLE );
  +    }
  +
  +    /**
  +     * @see org.apache.avalon.framework.activity.Disposable#dispose()
  +     */
  +    public void dispose() {
  +        if ( null != this.manager ) {
  +            this.manager.release( (Component)this.cacheManager );
  +        }
       }
   
       /**
        * Recycle the component
        */
       public void recycle() {
  +        this.cacheManager.terminateSession( this.cachingSession );
  +        this.cachingSession = null;
  +        if ( null != this.recorder) {
  +            this.manager.release( this.recorder );
  +            this.recorder = null;
  +        }
           super.recycle();
           this.configurationParameters = null;
           this.resourceParameters = null;
  +        if (this.getLogger().isErrorEnabled()) {
  +            this.getLogger().debug("Finishing CachingCIncludeTransformer, time: " + (System.currentTimeMillis() - this.startTime));
  +            this.startTime = 0;
  +        }
       }
   
       public void startTransformingElement(String uri, String name, String raw, Attributes attr)
  @@ -177,7 +273,8 @@
                                           attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
                                           attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_SELECT_ATTRIBUTE),
                                           attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
  -                                        attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE));
  +                                        attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE),
  +                                        false);
   
           // Element: include
           } else if (name.equals(CINCLUDE_INCLUDEXML_ELEMENT) 
  @@ -223,6 +320,26 @@
                      && this.state == STATE_INCLUDE) {
               this.startSerializedXMLRecording(XMLUtils.defaultSerializeToXMLFormat(true));
   
  +       } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
  +
  +           String src = this.processCIncludeElement(attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
  +                                                    attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
  +                                                    null,
  +                                                    attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
  +                                                    attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE),
  +                                                    true);
  +           if (this.compiling) {
  +               this.srcAttributes.addAttribute("", "src", "src", "CDATA", src);
  +               super.startTransformingElement(uri, 
  +                                              CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT, 
  +                                              raw+"p", 
  +                                              this.srcAttributes);
  +               this.srcAttributes.clear();                                               
  +           }
  +       } else if (name.equals(CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
  +           // this is a placeholder
  +           final String src = attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE);
  +           this.cacheManager.stream(src, this.cachingSession, this.filter);
           } else {
               super.startTransformingElement(uri, name, raw, attr);
           }
  @@ -331,14 +448,24 @@
               stack.push(this.endSerializedXMLRecording());
               stack.push("value");
   
  +        } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
  +            if (this.compiling) {
  +               super.endTransformingElement(uri, 
  +                                            CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT,
  +                                            raw+"p");
  +            }
  +            // do nothing else
  +        } else if (name.equals(CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
  +            // this is the placeholder element: do nothing
           } else {
               super.endTransformingElement(uri, name, raw);
           }
       }
   
  -    protected void processCIncludeElement(String src, String element, 
  -                                            String select, String ns, String prefix)
  -    throws SAXException {
  +    protected String processCIncludeElement(String src, String element, 
  +                                            String select, String ns, String prefix,
  +                                            boolean cache)
  +    throws SAXException, IOException {
   
           if (element == null) element="";
           if (select == null) select="";
  @@ -350,11 +477,26 @@
                             + ", element=" + element
                             + ", select=" + select
                             + ", ns=" + ns
  -                          + ", prefix=" + prefix);
  +                          + ", prefix=" + prefix
  +                          + ", caching="+cache);
           }
   
  -        IncludeXMLConsumer consumer = new IncludeXMLConsumer(this);
  +        if (cache) {
  +            src = this.cacheManager.load(src, this.cachingSession);
   
  +            if (this.cachingSession.isParallel() && !this.cachingSession.isPreemptive()) {
  +                if (!this.compiling) {
  +                    this.compiling = true;
  +                    this.startCompiledXMLRecording();
  +                }
  +            } else {
  +                this.cacheManager.stream(src, this.cachingSession, this.filter);
  +            }
  +        
  +            return src;
  +        }
  +
  +        // usual no caching stuff
           if (!"".equals(element)) {
               AttributesImpl attrs = new AttributesImpl();
               if (!ns.equals("")) {
  @@ -399,7 +541,7 @@
                   if ( null != this.configurationParameters ) {
                       mimeType = this.configurationParameters.getParameter("mime-type", mimeType);                    
                   }
  -                this.resolver.toSAX(source, mimeType, consumer);
  +                this.resolver.toSAX(source, mimeType, this.filter);
               }
   
   
  @@ -421,5 +563,95 @@
                   super.endPrefixMapping(prefix);
               }
           }
  +        return src;
       }
  +    
  +    /**
  +     * Start recording of compiled xml.
  +     * The incomming SAX events are recorded and a compiled representation
  +     * is created. These events are not forwarded to the next component in
  +     * the pipeline.
  +     */
  +    protected void startCompiledXMLRecording()
  +    throws SAXException {
  +        if (this.getLogger().isDebugEnabled()) {
  +            this.getLogger().debug("BEGIN startCompiledXMLRecording");
  +        }
  +
  +        try {
  +            this.recorder = (XMLSerializer)this.manager.lookup(XMLSerializer.ROLE);
  +            
  +            this.addRecorder(recorder);
  +  
  +            this.sendStartPrefixMapping();
  +        } catch (ComponentException ce) {
  +            throw new SAXException("Unable to lookup xml serializer for compiling xml.", ce);
  +        }
  +        if (this.getLogger().isDebugEnabled()) {
  +           this.getLogger().debug("END startCompiledXMLRecording");
  +        }
  +    }
  +
  +    /**
  +     * Stop recording of compiled XML.
  +     * @return The compiled XML.
  +     */
  +    protected Object endCompiledXMLRecording()
  +    throws SAXException {
  +        if (this.getLogger().isDebugEnabled()) {
  +            this.getLogger().debug("BEGIN endCompiledXMLRecording");
  +        }
  +
  +        this.sendEndPrefixMapping();
  +
  +        XMLSerializer recorder = (XMLSerializer)this.removeRecorder();
  +        Object text = (byte[])recorder.getSAXFragment();
  +
  +        if (this.getLogger().isDebugEnabled()) {
  +            this.getLogger().debug("END endCompiledXMLRecording text="+text);
  +        }
  +        return text;
  +    }
  +
  +    /**
  +     * @see org.xml.sax.ContentHandler#endDocument()
  +     */
  +    public void endDocument() throws SAXException {
  +        if ( this.compiling ) {
  +            Object compiledXML = this.endCompiledXMLRecording();
  +            XMLDeserializer deserializer = null;
  +            try {
  +                deserializer = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
  +                deserializer.setConsumer(this.filter);
  +                deserializer.deserialize(compiledXML);
  +            } catch (ComponentException ce) {
  +                throw new SAXException("Unable to lookup xml deserializer.", ce);
  +            } finally {
  +                this.manager.release( deserializer );
  +            }
  +        }
  +        super.endDocument();
  +    }
  +
  +    /**
  +     * @see org.apache.cocoon.caching.CacheableProcessingComponent#generateKey()
  +     */
  +    public Serializable generateKey() {
  +        if (this.supportCaching && this.cachingSession.getExpires() > 0) {
  +            return "1";
  +        }
  +        return null;
  +    }
  +
  +    /**
  +     * @see org.apache.cocoon.caching.CacheableProcessingComponent#generateValidity()
  +     */
  +    public SourceValidity generateValidity() {
  +        if (this.supportCaching && this.cachingSession.getExpires() > 0
  +            && !this.cachingSession.isPurging()) {
  +            return this.cachingSession.getExpiresValidity();
  +        }
  +        return null;
  +    }
  +    
   }
  
  
  
  1.65      +12 -0     xml-cocoon2/src/webapp/WEB-INF/cocoon.xconf
  
  Index: cocoon.xconf
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/webapp/WEB-INF/cocoon.xconf,v
  retrieving revision 1.64
  retrieving revision 1.65
  diff -u -r1.64 -r1.65
  --- cocoon.xconf	28 Feb 2003 08:00:34 -0000	1.64
  +++ cocoon.xconf	28 Feb 2003 14:14:25 -0000	1.65
  @@ -626,4 +626,16 @@
     <!-- cornerstone-scheduler -->
     <cornerstone-scheduler logger="core.scheduler"/>
   
  +  <!--+
  +      | The Cache Manager is a component that can be used to cache content.
  +      | It is currently used by the cinclude transformer
  +      +-->
  +  <component class="org.apache.cocoon.transformation.helpers.DefaultIncludeCacheManager" 
  +             role="org.apache.cocoon.transformation.helpers.IncludeCacheManager">
  +    <!-- Set the preemptive-loader-url to a pipeline inside Cocoon that contains the
  +         preemptive loader action. The URL must be absolute! 
  +    <parameter name="preemptive-loader-url" 
  +               value="http://localhost:8080/cocoon/samples/cinclude/loader"/>
  +    -->
  + </component>
   </cocoon>
  
  
  
  1.3       +161 -18   xml-cocoon2/src/documentation/xdocs/userdocs/transformers/cinclude-transformer.xml
  
  Index: cinclude-transformer.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/documentation/xdocs/userdocs/transformers/cinclude-transformer.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- cinclude-transformer.xml	26 Sep 2002 06:52:23 -0000	1.2
  +++ cinclude-transformer.xml	28 Feb 2003 14:14:25 -0000	1.3
  @@ -15,10 +15,12 @@
    <body>
     <s1 title="CInclude Transformer">
      <p>This transformer includes XML in the current stream and acts therefore
  -      is a kind of (dynamic) content aggregation. Two forms, one verbose
  +      as a kind of (dynamic) content aggregation. Two forms, one verbose
         and flexible approach, and a simple approach are supported by the
         transformer. We will first discuss the simple approach and
  -      the more flexible is mentioned in the next chapter.</p>
  +      the more flexible is mentioned in the next chapter. In addition
  +      the cinclude transformer provides a caching mechanism (for the
  +      simple include form).</p>
      <p>
       This transformer triggers for the element <code>include</code> in the
       namespace "http://apache.org/cocoon/include/1.0".
  @@ -33,22 +35,6 @@
       <li>Class: org.apache.cocoon.transformation.CIncludeTransformer</li>
       <li>Cacheable: no.</li>
      </ul>
  -   <ul>
  -    <li>Name : cinclude</li>
  -    <li>Class: org.apache.cocoon.transformation.CachingCIncludeTransformer</li>
  -    <li>Cacheable: yes.</li>
  -   </ul>
  -   <p>
  -    There are two versions of CIncludeTransformer available:
  -   </p>
  -   <ul>
  -    <li>A non caching version
  -     <code>org.apache.cocoon.transformation.CIncludeTransformer</code>
  -    </li>
  -    <li>A caching version
  -     <code>org.apache.cocoon.transformation.CachingCIncludeTransformer</code>
  -    </li>
  -   </ul>
      <p>
       A simple example might help to use the CIncludeTransfomer effectivly:
      </p>
  @@ -200,5 +186,162 @@
   		  <p>The format of the parameters is the same as for the connection
   			 configuration.</p> 
   		</s1> 
  +  <s1 title="Caching">
  +   <p>This transformer includes XML in the current stream and acts therefore
  +      as a kind of (dynamic) content aggregation. However, the included content
  +      might be very big or either it might take a lot of time to fetch
  +      the content. If, in those cases, your content does not change too
  +      frequently, you can turn on caching for these contents.</p>
  +   <p>
  +    To turn on caching, this transformer triggers for the element <code>cached-include</code> 
  +    in the namespace "http://apache.org/cocoon/include/1.0/caching".
  +    The <code>src</code> attribute contains the url which points to
  +    an xml resource that is included instead of the element.
  +    It is possible to mix the <code>cached-include</code> and the <code>include</code>
  +    element, so only parts are cached and others are not.
  +   </p>
  +   <p>
  +    A simple example might help to use the caching effectivly:
  +   </p>
  +   <p>
  +     First define your pipeline to use the CIncludeTransformer with
  +     caching turned on; you turn on caching by setting the <code>expires</code>
  +     parameter to a value greater than 0. The exact meaning of this
  +     parameter is explained below.
  +   </p>
  +<source><![CDATA[
  +<map:match pattern="cinc/simple-cinc">
  +  <map:generate src="cinc/simple-cinc.xml"/>
  +  <map:transform type="cinclude">
  +      <map:parameter name="expires" value="600"/>
  +  </map:transform>
  +  <map:transform src="stylesheets/page/simple-page2html.xsl"/>
  +  <map:serialize/>
  +</map:match>
  +]]></source>
  +
  +   <p>
  +    In this example-pipeline it is assumed that simple-cinc.xml contains
  +    the <code>cached-include</code> element. Beside defining the element
  +    it uses the namespache URI "http://apache.org/cocoon/include/1.0".
  +    This helps the transformer to find the tag to get replaced by
  +    the xml content referenced via the src attribute.
  +    The simple-cinc.xml may look like this:
  +   </p>
  +<source><![CDATA[
  +<?xml version="1.0" encoding="UTF-8"?>
  +<page 
  +  xmlns:cinclude="http://apache.org/cocoon/include/1.0">
  +  <title>Hello</title>
  +  <content>
  +    <para>This is my first Cocoon page!</para>
  +    <cinclude:cached-include src="http://server/document1.xml"/>
  +    <cinclude:cached-include src="http://server/document2.xml"/>
  +  </content>
  +</page>
  +]]></source>
  +
  +   <p>
  +    Now finally we have everything put together the xml content after the
  +    CIncludeTransformer processing will look like this:
  +   </p>
  +<source><![CDATA[
  +<?xml version="1.0" encoding="UTF-8"?>
  +<page 
  +  xmlns:cinclude="http://apache.org/cocoon/include/1.0">
  +  <title>Hello</title>
  +  <content>
  +    <para>This is my first Cocoon page!</para>
  +    <document1>
  +        CONTENT OF document 1
  +    </document1>
  +    <document2>
  +        CONTENT OF document 2
  +    </document2>
  +  </content>
  +</page>
  +]]></source>
  +  <p>So, of course even with caching turned on, this transformer acts like the 
  +  usual cinclude transformer. But as you can see from the example above, you 
  +  can define an expires value. The fetched content is cached for the duration of 
  +  this value; in the example above the content is cached for 10 minutes. So, if 
  +  during the next 10 minutes after the first time this pipeline was processed, 
  +  someone else requests this pipeline, the content is not fetched again from a 
  +  distant server (or whereever the content is stored). It is directly delivered 
  +  from the cache. When the 10 minutes have expired, the next time the pipeline 
  +  is requested, the content is fetched again and stored in the cache for the 
  +  next 10 minutes.</p>
  +  <p>You can fine tune the behaviour of the transformer with several parameters.</p>
  +  <p>The <code>expires</code> parameter defines the expiration date of the
  +    content in seconds from the time the pipeline is requested.</p>
  + <p>Usually the content is cached in the common store, but you
  +   can also define a writeable/modifiable source with the "source" parameter,
  + e.g. "file:/c:/temp". Then the cached content is written into this
  + directory.</p>
  + <p>With the optional <code>purge</code> set to <code>true</code>
  +  the cache is purged which means the cached content is regarded as
  +  invalid nevertheless if it has expired or not.</p>
  + <p>With the optional parameter <code>parallel</code> the various
  +  included contents are processed (included) in parallel rather than
  +  in a series.</p>
  + <p>With the optional parameter <code>preemptive</code> set to <code>true</code>
  +  a pre-emptive caching is activated. When a resource is requested with
  +  pre-emptive caching, this transformer always attempts to get the 
  +  content from the cache. If the content is not in the cache, it is
  +  of course retrieved from the original source and cached.
  +  If the cached resource has expired, it is still provided. The cache
  +  is updated by a background task. This task has to be started
  +  beforehand.</p>
  +   <p>
  +     Complete Example:
  +   </p>
  +<source><![CDATA[
  +<map:match pattern="cinc/simple-cinc">
  +  <map:generate src="cinc/simple-cinc.xml"/>
  +  <map:transform type="cinclude">
  +      <map:parameter name="expires" value="600"/>
  +      <map:parameter name="purge" value="false"/>
  +      <map:parameter name="parallel" value="true"/>
  +      <map:parameter name="preemptive" value="false"/>
  +      <map:parameter name="source" value="file:/c:/temp"/>
  +  </map:transform>
  +  <map:transform src="stylesheets/page/simple-page2html.xsl"/>
  +  <map:serialize/>
  +</map:match>
  +]]></source>
  +  </s1>
  +  <s1 title="Configuration">
  +<p>Besides the usual transformer configuration, this transformer requires some components.
  +You have to add the following lines to the cocoon.xconf:</p>
  +<source><![CDATA[
  + <component class="org.apache.cocoon.transformation.helpers.DefaultIncludeCacheManager" 
  +            role="org.apache.cocoon.transformation.helpers.IncludeCacheManager"
  +            logger="test">
  +    <!-- Specify this only if you use preemptive-caching -->
  +    <parameter name="preemptive-loader-url" value="http://localhost:8080/cocoon/loader"/>
  + </component>
  +]]></source>
  +  <p>If you want to use preemptive caching, you have to specify a URI inside Cocoon that contains 
  +     the preemptive-loader action. This pipeline is automatically called, when
  +     preemptive loading is actived and requried. It loads the content in the background.</p>
  +     <p>First you have to define the action:</p>
  +<source><![CDATA[
  +...
  +<map:components>
  +...
  +  <map:actions>
  +  ...
  +      <map:action name="preemptive"
  +                  src="org.apache.cocoon.transformation.helpers.PreemptiveLoaderAction"/>
  +  ...
  +]]></source>  
  +   <p>Then you must define a pipeline containing the action. This is the pipeline that
  +   has to be configured in the cocoon.xconf:</p>
  +<source><![CDATA[
  +   <map:match pattern="loader">
  +       <map:act type="preemptive"></map:act>
  +   </map:match>
  +]]></source>  
  +</s1>
    </body>
   </document>