cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jheym...@apache.org
Subject svn commit: r330548 [22/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/...
Date Thu, 03 Nov 2005 14:00:48 GMT
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/ProcessingPipeline.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/ProcessingPipeline.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/ProcessingPipeline.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/ProcessingPipeline.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.pipeline;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.sitemap.SitemapErrorHandler;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.generation.Generator;
+import org.apache.cocoon.xml.XMLConsumer;
+
+import org.apache.excalibur.source.SourceValidity;
+
+/**
+ * A <code>ProcessingPipeline<code> produces the response for a given request.
+ * It is assembled according to the commands in the sitemap and can either
+ * <ul>
+ *  <li>Collect a <code>Reader</code> and let it produce a byte stream,</li>
+ *  <li>Or connect a <code>Generator</code> with zero or more
+ *      <code>Transformer</code>s and a <code>Serializer</code>, and let them
+ *      produce the byte stream. This pipeline uses SAX events for
+ *      communication.
+ *  </li>
+ * </ul>
+ *
+= * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @author <a href="mailto:Giacomo.Pati@pwr.ch">Giacomo Pati</a>
+ * @version $Id: ProcessingPipeline.java 157555 2005-03-15 15:49:32Z vgritsenko $
+ */
+public interface ProcessingPipeline {
+
+    String ROLE = ProcessingPipeline.class.getName();
+
+    /**
+     * Setup this component.
+     */
+    void setup(Parameters params);
+
+    /**
+     * Set the generator that will be used as the initial step in the pipeline.
+     * The generator role is given : the actual <code>Generator</code> is fetched
+     * from the latest <code>ServiceManager</code>.
+     *
+     * @param role the generator role in the component manager.
+     * @param source the source where to produce XML from, or <code>null</code> if no
+     *        source is given.
+     * @param param the parameters for the generator.
+     * @throws ProcessingException if the generator couldn't be obtained.
+     */
+    void setGenerator(String role, String source, Parameters param, Parameters hintParam)
+    throws ProcessingException;
+
+    /**
+     * Get the generator - used for content aggregation
+     */
+    Generator getGenerator();
+
+    /**
+     * Informs pipeline we have come across a branch point
+     */
+    void informBranchPoint();
+
+    /**
+     * Set the <code>ServiceManager</code> where pipeline components have to be searched for.
+     * @param manager the processor's service manager.
+     */
+    void setProcessorManager(ServiceManager manager);
+
+    /**
+     * Add a transformer at the end of the pipeline.
+     * The transformer role is given : the actual <code>Transformer</code> is fetched
+     * from the latest <code>ServiceManager</code>.
+     *
+     * @param role the transformer role in the service manager.
+     * @param source the source used to setup the transformer (e.g. XSL file), or
+     *        <code>null</code> if no source is given.
+     * @param param the parameters for the transfomer.
+     * @throws ProcessingException if the generator couldn't be obtained.
+     */
+    void addTransformer(String role, String source, Parameters param, Parameters hintParam)
+    throws ProcessingException;
+
+    /**
+     * Set the serializer for this pipeline
+     * @param mimeType Can be null
+     */
+    void setSerializer(String role, String source, Parameters param, Parameters hintParam, String mimeType)
+    throws ProcessingException;
+
+    /**
+     * Set the reader for this pipeline
+     * @param mimeType Can be null
+     */
+    void setReader(String role, String source, Parameters param, String mimeType)
+    throws ProcessingException;
+
+    /**
+     * Sets error handler for this pipeline.
+     * Used for handling errors in the internal pipelines.
+     */
+    void setErrorHandler(SitemapErrorHandler errorHandler)
+    throws ProcessingException;
+
+    /**
+     * Process the given <code>Environment</code>, producing the output.
+     */
+    boolean process(Environment environment)
+    throws ProcessingException;
+
+    /**
+     * Prepare an internal processing
+     * @param environment          The current environment.
+     * @throws ProcessingException
+     */
+    void prepareInternal(Environment environment)
+    throws ProcessingException;
+
+    /**
+     * Process the given <code>Environment</code>, but do not use the
+     * serializer. Instead the sax events are streamed to the XMLConsumer.
+     * Make sure to call {@link #prepareInternal(Environment)} beforehand.
+     */
+    boolean process(Environment environment, XMLConsumer consumer)
+    throws ProcessingException;
+
+    /**
+     * Return valid validity objects for the event pipeline
+     * If the "event pipeline" (= the complete pipeline without the
+     * serializer) is cacheable and valid, return all validity objects.
+     * Otherwise return <code>null</code>
+     */
+    SourceValidity getValidityForEventPipeline();
+
+    /**
+     * Return the key for the event pipeline
+     * If the "event pipeline" (= the complete pipeline without the
+     * serializer) is cacheable and valid, return a key.
+     * Otherwise return <code>null</code>
+     */
+    String getKeyForEventPipeline();
+}

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

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/VirtualProcessingPipeline.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/VirtualProcessingPipeline.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/VirtualProcessingPipeline.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/VirtualProcessingPipeline.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,617 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.pipeline;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.Constants;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.Processor;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.environment.internal.EnvironmentHelper;
+import org.apache.cocoon.generation.Generator;
+import org.apache.cocoon.serialization.Serializer;
+import org.apache.cocoon.sitemap.SitemapErrorHandler;
+import org.apache.cocoon.sitemap.SitemapModelComponent;
+import org.apache.cocoon.transformation.Transformer;
+import org.apache.cocoon.util.location.Locatable;
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.xml.SaxBuffer;
+import org.apache.cocoon.xml.XMLConsumer;
+import org.apache.cocoon.xml.XMLProducer;
+import org.apache.excalibur.source.SourceValidity;
+import org.xml.sax.SAXException;
+
+/**
+ * Pipeline used by virtual pipeline components
+ *
+ * @version CVS $Id: VirtualProcessingPipeline.java 292282 2005-09-28 19:54:16Z vgritsenko $
+ */
+public class VirtualProcessingPipeline extends AbstractLogEnabled
+                                       implements ProcessingPipeline, Recyclable, Serviceable {
+
+    // Resolver of the defining sitemap
+    private SourceResolver resolver;
+
+    // Generator stuff
+    protected Generator generator;
+    protected Parameters generatorParam;
+    protected String generatorSource;
+
+    // Transformer stuff
+    protected ArrayList transformers = new ArrayList();
+    protected ArrayList transformerParams = new ArrayList();
+    protected ArrayList transformerSources = new ArrayList();
+
+    // Serializer stuff
+    protected Serializer serializer;
+    protected Parameters serializerParam;
+    protected String serializerSource;
+    protected String serializerMimeType;
+
+    // Error handler stuff
+    private SitemapErrorHandler errorHandler;
+    private Processor.InternalPipelineDescription errorPipeline;
+
+    /**
+     * True when pipeline has been prepared.
+     */
+    private boolean prepared;
+
+    /**
+     * This is the first consumer component in the pipeline, either
+     * the first transformer or the serializer.
+     */
+    protected XMLConsumer firstConsumer;
+
+    /**
+     * This is the last component in the pipeline, either the serializer
+     * or a custom xmlconsumer for the cocoon: protocol etc.
+     */
+    protected XMLConsumer lastConsumer;
+
+    /** The component manager set with compose() */
+    protected ServiceManager manager;
+
+    /** The component manager set with compose() and recompose() */
+    protected ServiceManager newManager;
+
+    /** The current Processor */
+    protected Processor processor;
+
+
+    public VirtualProcessingPipeline(Context context) throws Exception {
+        this.resolver = (EnvironmentHelper) context.get(Constants.CONTEXT_ENV_HELPER);
+    }
+
+    public void service(ServiceManager manager)
+    throws ServiceException {
+        this.manager = manager;
+        this.newManager = manager;
+    }
+
+    /**
+     * Set the processor's service manager
+     */
+    public void setProcessorManager(ServiceManager manager) {
+        this.newManager = manager;
+    }
+
+    /**
+     * Setup this component
+     */
+    public void setup(Parameters params) {
+    }
+
+    /**
+     * Get the generator - used for content aggregation
+     */
+    public Generator getGenerator() {
+        return this.generator;
+    }
+
+    /**
+     * Informs pipeline we have come across a branch point
+     * Default Behaviour is do nothing
+     */
+    public void informBranchPoint() {
+        // this can be overwritten in subclasses
+    }
+
+    /**
+     * Set the generator that will be used as the initial step in the pipeline.
+     * The generator role is given : the actual <code>Generator</code> is fetched
+     * from the latest <code>ServiceManager</code>.
+     *
+     * @param role the generator role in the component manager.
+     * @param source the source where to produce XML from, or <code>null</code> if no
+     *        source is given.
+     * @param param the parameters for the generator.
+     * @throws org.apache.cocoon.ProcessingException if the generator couldn't be obtained.
+     */
+    public void setGenerator(String role, String source, Parameters param, Parameters hintParam)
+    throws ProcessingException {
+        if (this.generator != null) {
+            throw new ProcessingException ("Generator already set. Cannot set generator '" + role + "'",
+                getLocation(param));
+        }
+
+        try {
+            this.generator = (Generator) this.newManager.lookup(Generator.ROLE + '/' + role);
+        } catch (ServiceException ce) {
+            throw ProcessingException.throwLocated("Lookup of generator '" + role + "' failed", ce, getLocation(param));
+        }
+
+        this.generatorSource = source;
+        this.generatorParam = param;
+    }
+
+    /**
+     * Add a transformer at the end of the pipeline.
+     * The transformer role is given : the actual <code>Transformer</code> is fetched
+     * from the latest <code>ServiceManager</code>.
+     *
+     * @param role the transformer role in the component manager.
+     * @param source the source used to setup the transformer (e.g. XSL file), or
+     *        <code>null</code> if no source is given.
+     * @param param the parameters for the transfomer.
+     */
+    public void addTransformer(String role, String source, Parameters param, Parameters hintParam)
+    throws ProcessingException {
+        try {
+            this.transformers.add(this.newManager.lookup(Transformer.ROLE + '/' + role));
+        } catch (ServiceException ce) {
+            throw ProcessingException.throwLocated("Lookup of transformer '"+role+"' failed", ce, getLocation(param));
+        }
+        this.transformerSources.add(source);
+        this.transformerParams.add(param);
+    }
+
+    /**
+     * Set the serializer for this pipeline
+     * @param mimeType Can be null
+     */
+    public void setSerializer(String role, String source, Parameters param, Parameters hintParam, String mimeType)
+    throws ProcessingException {
+        if (this.serializer != null) {
+            // Should normally not happen as adding a serializer starts pipeline processing
+            throw new ProcessingException ("Serializer already set. Cannot set serializer '" + role + "'",
+                    getLocation(param));
+        }
+
+        try {
+            this.serializer = (Serializer)this.newManager.lookup(Serializer.ROLE + '/' + role);
+        } catch (ServiceException ce) {
+            throw ProcessingException.throwLocated("Lookup of serializer '" + role + "' failed", ce, getLocation(param));
+        }
+        this.serializerSource = source;
+        this.serializerParam = param;
+        this.serializerMimeType = mimeType;
+        this.lastConsumer = this.serializer;
+    }
+
+    /**
+     * Sets error handler for this pipeline.
+     * Used for handling errors in the internal pipelines.
+     * @param errorHandler error handler
+     */
+    public void setErrorHandler(SitemapErrorHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    /**
+     * Sanity check
+     * @return true if the pipeline is 'sane', for VPCs all pipelines are sane
+     */
+    protected boolean checkPipeline() {
+        return true;
+    }
+
+    /**
+     * Setup pipeline components.
+     */
+    protected void setupPipeline(Environment environment)
+    throws ProcessingException {
+        try {
+            // SourceResolver resolver = this.processor.getSourceResolver();
+
+            // setup the generator
+            if (this.generator != null) {
+                this.generator.setup(resolver,
+                                     environment.getObjectModel(),
+                                     generatorSource,
+                                     generatorParam);
+            }
+
+            Iterator transformerItt = this.transformers.iterator();
+            Iterator transformerSourceItt = this.transformerSources.iterator();
+            Iterator transformerParamItt = this.transformerParams.iterator();
+
+            while (transformerItt.hasNext()) {
+                Transformer trans = (Transformer)transformerItt.next();
+                trans.setup(resolver,
+                            environment.getObjectModel(),
+                            (String)transformerSourceItt.next(),
+                            (Parameters)transformerParamItt.next()
+                );
+            }
+
+            if (this.serializer != null && this.serializer instanceof SitemapModelComponent) {
+                ((SitemapModelComponent)this.serializer).setup(
+                    resolver,
+                    environment.getObjectModel(),
+                    this.serializerSource,
+                    this.serializerParam
+                );
+            }
+
+        } catch (SAXException e) {
+            throw new ProcessingException("Could not setup pipeline.", e);
+        } catch (IOException e) {
+            throw new ProcessingException("Could not setup pipeline.", e);
+        }
+    }
+
+    /**
+     * Connect the next component
+     */
+    protected void connect(Environment environment,
+                           XMLProducer producer,
+                           XMLConsumer consumer)
+    throws ProcessingException {
+        // Connect next component.
+        producer.setConsumer(consumer);
+    }
+
+    /**
+     * Connect the XML pipeline.
+     */
+    protected void connectPipeline(Environment environment)
+    throws ProcessingException {
+        XMLProducer prev = this.generator;
+
+        Iterator itt = this.transformers.iterator();
+
+        // No generator for VPC transformer and serializer 
+        if (this.generator == null && itt.hasNext()) {
+            this.firstConsumer = (XMLConsumer)(prev = (XMLProducer)itt.next());
+        }
+
+        while (itt.hasNext()) {
+            Transformer next = (Transformer) itt.next();
+            connect(environment, prev, next);
+            prev = next;
+        }
+        
+        // insert the serializer
+        if (prev != null) {
+            connect(environment, prev, this.lastConsumer);
+        } else {
+            this.firstConsumer = this.lastConsumer;
+        }
+    }
+
+    /**
+     * Prepare the pipeline
+     */
+    protected void preparePipeline(Environment environment)
+    throws ProcessingException {
+        // TODO (CZ) Get the processor set via IoC
+        this.processor = EnvironmentHelper.getCurrentProcessor();
+        if (!checkPipeline()) {
+            throw new ProcessingException("Attempted to process incomplete pipeline.");
+        }
+
+        if (this.prepared) {
+            throw new ProcessingException("Duplicate preparePipeline call caught.");
+        }
+
+        setupPipeline(environment);
+        this.prepared = true;
+    }
+
+    /**
+     * Prepare an internal processing
+     * @param environment          The current environment.
+     * @throws org.apache.cocoon.ProcessingException
+     */
+    public void prepareInternal(Environment environment)
+    throws ProcessingException {
+        this.lastConsumer = null;
+        try {
+            preparePipeline(environment);
+        } catch (ProcessingException e) {
+            prepareInternalErrorHandler(environment, e);
+        }
+    }
+
+    /**
+     * If prepareInternal fails, prepare internal error handler.
+     */
+    protected void prepareInternalErrorHandler(Environment environment, ProcessingException ex)
+    throws ProcessingException {
+        if (this.errorHandler != null) {
+            try {
+                this.errorPipeline = this.errorHandler.prepareErrorPipeline(ex);
+                if (this.errorPipeline != null) {
+                    this.errorPipeline.processingPipeline.prepareInternal(environment);
+                    return;
+                }
+            } catch (ProcessingException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new ProcessingException("Failed to handle exception <" + ex + ">", e);
+            }
+        }
+    }
+
+    /**
+     * @return true if error happened during internal pipeline prepare call.
+     */
+    protected boolean isInternalError() {
+        return this.errorPipeline != null;
+    }
+
+    public void setReader(String role, String source, Parameters param, String mimeType) throws ProcessingException {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean process(Environment environment) throws ProcessingException {
+        if (!this.prepared) {
+            preparePipeline(environment);
+        }
+
+        // If this is an internal request, lastConsumer was reset!
+        if (this.lastConsumer == null) {
+            this.lastConsumer = this.serializer;
+        }
+        
+        connectPipeline(environment);
+        return processXMLPipeline(environment);
+    }
+
+    /**
+     * Process the given <code>Environment</code>, but do not use the
+     * serializer. Instead the sax events are streamed to the XMLConsumer.
+     */
+    public boolean process(Environment environment, XMLConsumer consumer)
+    throws ProcessingException {
+        // Exception happened during setup and was handled
+        if (this.errorPipeline != null) {
+            return this.errorPipeline.processingPipeline.process(environment, consumer);
+        }
+
+        // Have to buffer events if error handler is specified.
+        SaxBuffer buffer = null;
+        this.lastConsumer = this.errorHandler == null? consumer: (buffer = new SaxBuffer());
+        try {
+            connectPipeline(environment);
+            return processXMLPipeline(environment);
+        } catch (ProcessingException e) {
+            buffer = null;
+            return processErrorHandler(environment, e, consumer);
+        } finally {
+            if (buffer != null) {
+                try {
+                    buffer.toSAX(consumer);
+                } catch (SAXException e) {
+                    throw new ProcessingException("Failed to execute pipeline.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the first consumer - used for VPC transformers
+     */
+    public XMLConsumer getXMLConsumer(Environment environment, XMLConsumer consumer)
+        throws ProcessingException {
+        if (!this.prepared) {
+            preparePipeline(environment);
+        }
+
+        this.lastConsumer = consumer;
+        connectPipeline(environment);
+
+        if (this.firstConsumer == null)
+            throw new ProcessingException("A VPC transformer pipeline should not contain a generator.");
+
+        return this.firstConsumer;
+    }
+
+    /**
+     * Get the first consumer - used for VPC serializers
+     */
+    public XMLConsumer getXMLConsumer(Environment environment) throws ProcessingException {
+        if (!this.prepared) {
+            preparePipeline(environment);
+        }
+
+        // If this is an internal request, lastConsumer was reset!
+        if (this.lastConsumer == null) {
+            this.lastConsumer = this.serializer;
+        }
+        
+        connectPipeline(environment);
+
+        if (this.serializer == null) {
+            throw new ProcessingException("A VPC serializer pipeline must contain a serializer.");
+        }
+
+        try {
+            this.serializer.setOutputStream(environment.getOutputStream(0));
+        } catch (Exception e) {
+            throw new ProcessingException("Couldn't set output stream ", e);
+        }
+            
+        if (this.firstConsumer == null)
+            throw new ProcessingException("A VPC serializer pipeline should not contain a generator.");
+
+        return this.firstConsumer;
+    }
+
+    /**
+     * Get the mime-type for the serializer
+     */
+    public String getMimeType() {
+        if (this.lastConsumer == null) {
+            // internal processing: text/xml
+            return "text/xml";
+        } else {
+            // Get the mime-type
+            if (serializerMimeType != null) {
+                // there was a serializer defined in the sitemap
+                return serializerMimeType;
+            } else {
+                // ask to the component itself
+                return this.serializer.getMimeType();
+            }
+        }
+    }
+
+    /**
+     * Test if the serializer wants to set the content length
+     */
+    public boolean shouldSetContentLength() {
+        return this.serializer.shouldSetContentLength();
+    }
+
+    /**
+     * Process the SAX event pipeline
+     */
+    protected boolean processXMLPipeline(Environment environment)
+    throws ProcessingException {
+
+        try {
+            if (this.lastConsumer == null || this.serializer == null) {
+                // internal processing
+                this.generator.generate();
+            } else {
+                if (this.serializer.shouldSetContentLength()) {
+                    // set the output stream
+                    ByteArrayOutputStream os = new ByteArrayOutputStream();
+                    this.serializer.setOutputStream(os);
+
+                    // execute the pipeline:
+                    this.generator.generate();
+                    environment.setContentLength(os.size());
+                    os.writeTo(environment.getOutputStream(0));
+                } else {
+                    // set the output stream
+                    this.serializer.setOutputStream(environment.getOutputStream(0));
+                    // execute the pipeline:
+                    this.generator.generate();
+                }
+            }
+        } catch (ProcessingException e) {
+            throw e;
+        } catch (Exception e) {
+            // TODO: Unwrap SAXException ?
+            throw new ProcessingException("Failed to execute pipeline.", e);
+        }
+        return true;
+    }
+
+    public void recycle() {
+        this.prepared = false;
+
+        if (this.generator != null) {
+            // Release generator.
+            this.newManager.release(this.generator);
+            this.generator = null;
+            this.generatorParam = null;
+        }
+
+        // Release transformers
+        int size = this.transformers.size();
+        for (int i = 0; i < size; i++) {
+            this.newManager.release(this.transformers.get(i));
+        }
+        this.transformers.clear();
+        this.transformerParams.clear();
+        this.transformerSources.clear();
+
+        // Release serializer
+        if (this.serializer != null) {
+            this.newManager.release(this.serializer);
+            this.serializerParam = null;
+        }
+        this.serializer = null;
+        this.processor = null;
+        this.lastConsumer = null;
+        this.firstConsumer = null;
+
+        // Release error handler
+        this.errorHandler = null;
+        if (this.errorPipeline != null) {
+            this.errorPipeline.release();
+            this.errorPipeline = null;
+        }
+    }
+
+    protected boolean processErrorHandler(Environment environment, ProcessingException e, XMLConsumer consumer)
+    throws ProcessingException {
+        if (this.errorHandler != null) {
+            try {
+                this.errorPipeline = this.errorHandler.prepareErrorPipeline(e);
+                if (this.errorPipeline != null) {
+                    this.errorPipeline.processingPipeline.prepareInternal(environment);
+                    return this.errorPipeline.processingPipeline.process(environment, consumer);
+                }
+            } catch (Exception ignored) {
+                getLogger().debug("Exception in error handler", ignored);
+            }
+        }
+
+        throw e;
+    }
+
+    /**
+     * Return valid validity objects for the event pipeline
+     * If the "event pipeline" (= the complete pipeline without the
+     * serializer) is cacheable and valid, return all validity objects.
+     * Otherwise return <code>null</code>
+     */
+    public SourceValidity getValidityForEventPipeline() {
+        return null;
+    }
+
+    public String getKeyForEventPipeline() {
+        return null;
+    }
+
+    protected Location getLocation(Parameters param) {
+        Location value = null;
+        if (param instanceof Locatable) {
+            value = ((Locatable) param).getLocation();
+        }
+        if (value == null) {
+            value = Location.UNKNOWN;
+        }
+        return value;
+    }
+}

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

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/AbstractCachingProcessingPipeline.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/AbstractCachingProcessingPipeline.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/AbstractCachingProcessingPipeline.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/AbstractCachingProcessingPipeline.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,932 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.pipeline.impl;
+
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameters;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.caching.CacheableProcessingComponent;
+import org.apache.cocoon.caching.CachedResponse;
+import org.apache.cocoon.caching.CachingOutputStream;
+import org.apache.cocoon.caching.ComponentCacheKey;
+import org.apache.cocoon.caching.PipelineCacheKey;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.transformation.Transformer;
+import org.apache.cocoon.util.HashUtil;
+
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.validity.AggregatedValidity;
+import org.apache.excalibur.source.impl.validity.DeferredValidity;
+import org.apache.excalibur.source.impl.validity.NOPValidity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * This is the base class for all caching pipeline implementations
+ * that check different pipeline components.
+ *
+ * @since 2.1
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @author <a href="mailto:Michael.Melhem@managesoft.com">Michael Melhem</a>
+ * @version $Id: AbstractCachingProcessingPipeline.java 161222 2005-04-13 22:04:23Z vgritsenko $
+ */
+public abstract class AbstractCachingProcessingPipeline extends BaseCachingProcessingPipeline {
+
+    /** The role name of the generator */
+    protected String generatorRole;
+
+    /** The role names of the transfomrers */
+    protected ArrayList transformerRoles = new ArrayList();
+
+    /** The role name of the serializer */
+    protected String serializerRole;
+
+    /** The role name of the reader */
+    protected String readerRole;
+
+    /** The cached response */
+    protected CachedResponse cachedResponse;
+
+    /** The index indicating the first transformer getting input from the cache */
+    protected int firstProcessedTransformerIndex;
+
+    /** Complete response is cached */
+    protected boolean completeResponseIsCached;
+
+
+    /** This key indicates the response that is fetched from the cache */
+    protected PipelineCacheKey fromCacheKey;
+
+    /** This key indicates the response that will get into the cache */
+    protected PipelineCacheKey toCacheKey;
+
+    /** The source validities used for caching */
+    protected SourceValidity[] toCacheSourceValidities;
+
+    /** The index indicating to the first transformer which is not cacheable */
+    protected int firstNotCacheableTransformerIndex;
+
+    /** Cache complete response */
+    protected boolean cacheCompleteResponse;
+
+    /** Smart caching ? */
+    protected boolean doSmartCaching;
+
+    /** Default setting for smart caching */
+    protected boolean configuredDoSmartCaching;
+
+    /**
+     * Abstract methods defined in subclasses
+     */
+    protected abstract void cacheResults(Environment environment,
+                                         OutputStream os)
+    throws Exception;
+
+    protected abstract ComponentCacheKey newComponentCacheKey(int type,
+                                                              String role,
+                                                              Serializable key);
+
+    protected abstract void connectCachingPipeline(Environment environment)
+    throws ProcessingException;
+
+    /**
+     * Parameterizable Interface - Configuration
+     */
+    public void parameterize(Parameters params)
+    throws ParameterException {
+        super.parameterize(params);
+        this.configuredDoSmartCaching = params.getParameterAsBoolean("smart-caching", true);
+    }
+
+    /**
+     * Setup this component
+     */
+    public void setup(Parameters params) {
+        super.setup(params);
+        this.doSmartCaching = params.getParameterAsBoolean("smart-caching",
+                                                           this.configuredDoSmartCaching);
+    }
+
+    /**
+     * Set the generator.
+     */
+    public void setGenerator (String role, String source, Parameters param,
+                              Parameters hintParam)
+    throws ProcessingException {
+        super.setGenerator(role, source, param, hintParam);
+        this.generatorRole = role;
+    }
+
+    /**
+     * Add a transformer.
+     */
+    public void addTransformer (String role, String source, Parameters param,
+                                Parameters hintParam)
+    throws ProcessingException {
+        super.addTransformer(role, source, param, hintParam);
+        this.transformerRoles.add(role);
+    }
+
+    /**
+     * Set the serializer.
+     */
+    public void setSerializer (String role, String source, Parameters param,
+                               Parameters hintParam, String mimeType)
+    throws ProcessingException {
+        super.setSerializer(role, source, param, hintParam, mimeType);
+        this.serializerRole = role;
+    }
+
+    /**
+     * Set the Reader.
+     */
+    public void setReader (String role, String source, Parameters param,
+                           String mimeType)
+    throws ProcessingException {
+        super.setReader(role, source, param, mimeType);
+        this.readerRole = role;
+    }
+
+    /**
+     * Process the given <code>Environment</code>, producing the output.
+     */
+    protected boolean processXMLPipeline(Environment environment)
+    throws ProcessingException {
+        if (this.toCacheKey == null && this.cachedResponse == null) {
+            return super.processXMLPipeline(environment);
+        }
+
+        if (this.cachedResponse != null && this.completeResponseIsCached) {
+
+            // Allow for 304 (not modified) responses in dynamic content
+            if (checkIfModified(environment, this.cachedResponse.getLastModified())) {
+                return true;
+            }
+
+            // Set mime-type
+            if (this.cachedResponse.getContentType() != null) {
+                environment.setContentType(this.cachedResponse.getContentType());
+            } else {
+                setMimeTypeForSerializer(environment);
+            }
+
+            // Write response out
+            try {
+                final OutputStream outputStream = environment.getOutputStream(0);
+                final byte[] content = this.cachedResponse.getResponse();
+                if (content.length > 0) {
+                    environment.setContentLength(content.length);
+                    outputStream.write(content);
+                }
+            } catch (Exception e) {
+                handleException(e);
+            }
+        } else {
+            setMimeTypeForSerializer(environment);
+            if (getLogger().isDebugEnabled() && this.toCacheKey != null) {
+                getLogger().debug("processXMLPipeline: caching content for further" +
+                                  " requests of '" + environment.getURI() +
+                                  "' using key " + this.toCacheKey);
+            }
+
+            try {
+                OutputStream os = null;
+                if (this.cacheCompleteResponse && this.toCacheKey != null) {
+                    os = new CachingOutputStream(environment.getOutputStream(this.outputBufferSize));
+                }
+
+                if (super.serializer != super.lastConsumer) {
+                    if (os == null) {
+                        os = environment.getOutputStream(this.outputBufferSize);
+                    }
+
+                    // internal processing
+                    if (this.xmlDeserializer != null) {
+                        this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
+                    } else {
+                        this.generator.generate();
+                    }
+
+                } else {
+                    if (this.serializer.shouldSetContentLength()) {
+                        if (os == null) {
+                            os = environment.getOutputStream(0);
+                        }
+
+                        // Set the output stream
+                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                        this.serializer.setOutputStream(baos);
+
+                        // Execute the pipeline
+                        if (this.xmlDeserializer != null) {
+                            this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
+                        } else {
+                            this.generator.generate();
+                        }
+
+                        environment.setContentLength(baos.size());
+                        baos.writeTo(os);
+                    } else {
+                        if (os == null) {
+                            os = environment.getOutputStream(this.outputBufferSize);
+                        }
+
+                        // Set the output stream
+                        this.serializer.setOutputStream(os);
+
+                        // Execute the pipeline
+                        if (this.xmlDeserializer != null) {
+                            this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
+                        } else {
+                            this.generator.generate();
+                        }
+                    }
+                }
+
+                //
+                // Now that we have processed the pipeline,
+                // we do the actual caching
+                //
+                cacheResults(environment,os);
+
+            } catch (Exception e) {
+                handleException(e);
+            }
+
+            return true;
+        }
+
+        return true;
+    }
+
+    /**
+     * The components of the pipeline are checked if they are Cacheable.
+     */
+    protected void generateCachingKey(Environment environment)
+    throws ProcessingException {
+
+        this.toCacheKey = null;
+
+        this.firstNotCacheableTransformerIndex = 0;
+        this.cacheCompleteResponse = false;
+
+        // first step is to generate the key:
+        // All pipeline components starting with the generator
+        // are tested if they are either a CacheableProcessingComponent
+        // or Cacheable (deprecated). The returned keys are chained together
+        // to build a unique key of the request
+
+        // is the generator cacheable?
+        Serializable key = null;
+        if (super.generator instanceof CacheableProcessingComponent) {
+            key = ((CacheableProcessingComponent)super.generator).getKey();
+        }
+
+        if (key != null) {
+            this.toCacheKey = new PipelineCacheKey();
+            this.toCacheKey.addKey(
+                    this.newComponentCacheKey(
+                            ComponentCacheKey.ComponentType_Generator,
+                            this.generatorRole, key));
+
+            // now testing transformers
+            final int transformerSize = super.transformers.size();
+            boolean continueTest = true;
+
+            while (this.firstNotCacheableTransformerIndex < transformerSize && continueTest) {
+                final Transformer trans =
+                    (Transformer) super.transformers.get(this.firstNotCacheableTransformerIndex);
+                key = null;
+                if (trans instanceof CacheableProcessingComponent) {
+                    key = ((CacheableProcessingComponent)trans).getKey();
+                }
+                if (key != null) {
+                    this.toCacheKey.addKey(
+                            this.newComponentCacheKey(
+                                    ComponentCacheKey.ComponentType_Transformer,
+                                    (String)this.transformerRoles.get(
+                                            this.firstNotCacheableTransformerIndex),
+                                            key));
+
+                    this.firstNotCacheableTransformerIndex++;
+                } else {
+                    continueTest = false;
+                }
+            }
+            // all transformers are cacheable => pipeline is cacheable
+            // test serializer if this is not an internal request
+            if (this.firstNotCacheableTransformerIndex == transformerSize
+                    && super.serializer == this.lastConsumer) {
+
+                key = null;
+                if (super.serializer instanceof CacheableProcessingComponent) {
+                    key = ((CacheableProcessingComponent)this.serializer).getKey();
+                }
+                if (key != null) {
+                    this.toCacheKey.addKey(
+                            this.newComponentCacheKey(
+                                    ComponentCacheKey.ComponentType_Serializer,
+                                    this.serializerRole,
+                                    key));
+                    this.cacheCompleteResponse = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Generate validity objects for the new response
+     */
+    protected void setupValidities() throws ProcessingException {
+
+        if (this.toCacheKey != null) {
+            // only update validity objects if we cannot use
+            // a cached response or when the cached response does
+            // cache less than now is cacheable
+            if (this.fromCacheKey == null
+                || this.fromCacheKey.size() < this.toCacheKey.size()) {
+
+                this.toCacheSourceValidities =
+                    new SourceValidity[this.toCacheKey.size()];
+
+                int len = this.toCacheSourceValidities.length;
+                int i = 0;
+                while (i < len) {
+                    final SourceValidity validity = getValidityForInternalPipeline(i);
+
+                    if (validity == null) {
+                        if (i > 0
+                            && (this.fromCacheKey == null
+                                    || i > this.fromCacheKey.size())) {
+                            // shorten key
+                            for (int m=i; m < this.toCacheSourceValidities.length; m++) {
+                                this.toCacheKey.removeLastKey();
+                                if (!this.cacheCompleteResponse) {
+                                    this.firstNotCacheableTransformerIndex--;
+                                }
+                                this.cacheCompleteResponse = false;
+                            }
+                            SourceValidity[] copy = new SourceValidity[i];
+                            System.arraycopy(this.toCacheSourceValidities, 0, copy, 0, copy.length);
+                            this.toCacheSourceValidities = copy;
+                            len = this.toCacheSourceValidities.length;
+                        } else {
+                            // caching is not possible!
+                            this.toCacheKey = null;
+                            this.toCacheSourceValidities = null;
+                            this.cacheCompleteResponse = false;
+                            len = 0;
+                        }
+                    } else {
+                        this.toCacheSourceValidities[i] = validity;
+                    }
+                    i++;
+                }
+            } else {
+                // we don't have to cache
+                this.toCacheKey = null;
+                this.cacheCompleteResponse = false;
+            }
+        }
+    }
+
+    /**
+     * Calculate the key that can be used to get something from the cache, and
+     * handle expires properly.
+     */
+    protected void validatePipeline(Environment environment)
+    throws ProcessingException {
+        this.completeResponseIsCached = this.cacheCompleteResponse;
+        this.fromCacheKey = this.toCacheKey.copy();
+        this.firstProcessedTransformerIndex = this.firstNotCacheableTransformerIndex;
+
+        boolean finished = false;
+        while (this.fromCacheKey != null && !finished) {
+            finished = true;
+
+            final CachedResponse response = this.cache.get(this.fromCacheKey);
+
+            // now test validity
+            if (response != null) {
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Found cached response for '" + environment.getURI() +
+                                      "' using key: " + this.fromCacheKey);
+                }
+
+                boolean responseIsValid = true;
+                boolean responseIsUsable = true;
+
+                // See if we have an explicit "expires" setting. If so,
+                // and if it's still fresh, we're done.
+                Long responseExpires = response.getExpires();
+
+                if (responseExpires != null) {
+                    if (getLogger().isDebugEnabled()) {
+                       getLogger().debug("Expires time found for " + environment.getURI());
+                    }
+
+                    if (responseExpires.longValue() > System.currentTimeMillis()) {
+                        if (getLogger().isDebugEnabled()) {
+                            getLogger().debug("Expires time still fresh for " + environment.getURI() +
+                                              ", ignoring all other cache settings. This entry expires on "+
+                                              new Date(responseExpires.longValue()));
+                        }
+                        this.cachedResponse = response;
+                        return;
+                    } else {
+                        if (getLogger().isDebugEnabled()) {
+                            getLogger().debug("Expires time has expired for " + environment.getURI() +
+                                              ", regenerating content.");
+                        }
+
+                        // If an expires parameter was provided, use it. If this parameter is not available
+                        // it means that the sitemap was modified, and the old expires value is not valid
+                        // anymore.
+                        if (expires != 0) {
+                            if (this.getLogger().isDebugEnabled())
+                                this.getLogger().debug("Refreshing expires informations");
+                            response.setExpires(new Long(expires + System.currentTimeMillis()));
+                        } else {
+                            if (this.getLogger().isDebugEnabled())
+                                this.getLogger().debug("No expires defined anymore for this object, setting it to no expires");
+                            response.setExpires(null);
+                        }
+                    }
+                } else {
+                    // The response had no expires informations. See if it needs to be set (i.e. because the configuration has changed)
+                    if (expires != 0) {
+                        if (this.getLogger().isDebugEnabled())
+                            this.getLogger().debug("Setting a new expires object for this resource");
+                        response.setExpires(new Long(expires + System.currentTimeMillis()));
+                    }
+                }
+
+                SourceValidity[] fromCacheValidityObjects = response.getValidityObjects();
+
+                int i = 0;
+                while (responseIsValid && i < fromCacheValidityObjects.length) {
+                    boolean isValid = false;
+
+                    // BH Check if validities[i] is null, may happen
+                    //    if exception was thrown due to malformed content
+                    SourceValidity validity = fromCacheValidityObjects[i];
+                    int valid = validity == null ? SourceValidity.INVALID : validity.isValid();
+                    if (valid == SourceValidity.UNKNOWN) {
+                        // Don't know if valid, make second test
+                        validity = getValidityForInternalPipeline(i);
+                        if (validity != null) {
+                            valid = fromCacheValidityObjects[i].isValid(validity);
+                            if (valid == SourceValidity.UNKNOWN) {
+                                validity = null;
+                            } else {
+                                isValid = (valid == SourceValidity.VALID);
+                            }
+                        }
+                    } else {
+                        isValid = (valid == SourceValidity.VALID);
+                    }
+
+                    if (!isValid) {
+                        responseIsValid = false;
+                        // update validity
+                        if (validity == null) {
+                            responseIsUsable = false;
+                            if (getLogger().isDebugEnabled()) {
+                                getLogger().debug("validatePipeline: responseIsUsable is false, valid=" +
+                                                  valid + " at index " + i);
+                            }
+                        } else {
+                            if (getLogger().isDebugEnabled()) {
+                                getLogger().debug("validatePipeline: responseIsValid is false due to " +
+                                                  validity);
+                            }
+                        }
+                    } else {
+                        i++;
+                    }
+                }
+
+                if (responseIsValid) {
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("validatePipeline: using valid cached content for '" +
+                                          environment.getURI() + "'.");
+                    }
+
+                    // we are valid, ok that's it
+                    this.cachedResponse = response;
+                    this.toCacheSourceValidities = fromCacheValidityObjects;
+                } else {
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("validatePipeline: cached content is invalid for '" +
+                                          environment.getURI() + "'.");
+                    }
+                    // we are not valid!
+
+                    if (!responseIsUsable) {
+                        // we could not compare, because we got no
+                        // validity object, so shorten pipeline key
+                        if (i > 0) {
+                            int deleteCount = fromCacheValidityObjects.length - i;
+                            if (i > 0 && i <= firstNotCacheableTransformerIndex + 1) {
+                                this.firstNotCacheableTransformerIndex = i-1;
+                            }
+                            for(int x=0; x < deleteCount; x++) {
+                                this.toCacheKey.removeLastKey();
+                            }
+                            finished = false;
+                        } else {
+                            this.toCacheKey = null;
+                        }
+                        this.cacheCompleteResponse = false;
+                    } else {
+                        // the entry is invalid, remove it
+                        this.cache.remove(this.fromCacheKey);
+                    }
+
+                    // try a shorter key
+                    if (i > 0) {
+                        this.fromCacheKey.removeLastKey();
+                        if (!this.completeResponseIsCached) {
+                            this.firstProcessedTransformerIndex--;
+                        }
+                    } else {
+                        this.fromCacheKey = null;
+                    }
+                    finished = false;
+                    this.completeResponseIsCached = false;
+                }
+            } else {
+
+                // no cached response found
+                if (this.getLogger().isDebugEnabled()) {
+                    this.getLogger().debug(
+                        "Cached response not found for '" + environment.getURI() +
+                        "' using key: " +  this.fromCacheKey
+                    );
+                }
+
+                if (!this.doSmartCaching) {
+                    // try a shorter key
+                    if (this.fromCacheKey.size() > 1) {
+                        this.fromCacheKey.removeLastKey();
+                        if (!this.completeResponseIsCached) {
+                            this.firstProcessedTransformerIndex--;
+                        }
+                        finished = false;
+                    } else {
+                        this.fromCacheKey = null;
+                    }
+                } else {
+                    // stop on longest key for smart caching
+                    this.fromCacheKey = null;
+                }
+                this.completeResponseIsCached = false;
+            }
+        }
+
+    }
+
+    /**
+     * Setup the evenet pipeline.
+     * The components of the pipeline are checked if they are
+     * Cacheable.
+     */
+    protected void setupPipeline(Environment environment)
+    throws ProcessingException {
+        super.setupPipeline( environment );
+
+        // generate the key to fill the cache
+        this.generateCachingKey(environment);
+
+        // test the cache for a valid response
+        if (this.toCacheKey != null) {
+            this.validatePipeline(environment);
+        }
+        this.setupValidities();
+    }
+
+    /**
+     * Connect the pipeline.
+     */
+    protected void connectPipeline(Environment   environment)
+    throws ProcessingException {
+        if (this.toCacheKey == null && this.cachedResponse == null) {
+            super.connectPipeline(environment);
+            return;
+        } else if (this.completeResponseIsCached) {
+            // do nothing
+            return;
+        } else {
+            this.connectCachingPipeline(environment);
+        }
+    }
+
+    /** Process the pipeline using a reader.
+     * @throws ProcessingException if an error occurs
+     */
+    protected boolean processReader(Environment  environment)
+    throws ProcessingException {
+        try {
+            boolean usedCache = false;
+            OutputStream outputStream = null;
+            SourceValidity readerValidity = null;
+            PipelineCacheKey pcKey = null;
+
+            // test if reader is cacheable
+            Serializable readerKey = null;
+            if (super.reader instanceof CacheableProcessingComponent) {
+                readerKey = ((CacheableProcessingComponent)super.reader).getKey();
+            }
+
+            if (readerKey != null) {
+                // response is cacheable, build the key
+                pcKey = new PipelineCacheKey();
+                pcKey.addKey(new ComponentCacheKey(ComponentCacheKey.ComponentType_Reader,
+                                                   this.readerRole,
+                                                   readerKey)
+                            );
+
+                // now we have the key to get the cached object
+                CachedResponse cachedObject = this.cache.get(pcKey);
+                if (cachedObject != null) {
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("Found cached response for '" +
+                                          environment.getURI() + "' using key: " + pcKey);
+                    }
+
+                    SourceValidity[] validities = cachedObject.getValidityObjects();
+                    if (validities == null || validities.length != 1) {
+                        // to avoid getting here again and again, we delete it
+                        this.cache.remove(pcKey);
+                        if (getLogger().isDebugEnabled()) {
+                            getLogger().debug("Cached response for '" + environment.getURI() +
+                                              "' using key: " + pcKey + " is invalid.");
+                        }
+                        this.cachedResponse = null;
+                    } else {
+                        SourceValidity cachedValidity = validities[0];
+                        boolean isValid = false;
+                        int valid = cachedValidity.isValid();
+                        if (valid == SourceValidity.UNKNOWN) {
+                            // get reader validity and compare
+                            readerValidity = ((CacheableProcessingComponent) super.reader).getValidity();
+                            if (readerValidity != null) {
+                                valid = cachedValidity.isValid(readerValidity);
+                                if (valid == SourceValidity.UNKNOWN) {
+                                    readerValidity = null;
+                                } else {
+                                    isValid = (valid == SourceValidity.VALID);
+                                }
+                            }
+                        } else {
+                            isValid = (valid == SourceValidity.VALID);
+                        }
+
+                        if (isValid) {
+                            if (getLogger().isDebugEnabled()) {
+                                getLogger().debug("processReader: using valid cached content for '" +
+                                                  environment.getURI() + "'.");
+                            }
+                            byte[] response = cachedObject.getResponse();
+                            if (response.length > 0) {
+                                usedCache = true;
+                                if (cachedObject.getContentType() != null) {
+                                    environment.setContentType(cachedObject.getContentType());
+                                } else {
+                                    setMimeTypeForReader(environment);
+                                }
+                                outputStream = environment.getOutputStream(0);
+                                environment.setContentLength(response.length);
+                                outputStream.write(response);
+                            }
+                        } else {
+                            if (getLogger().isDebugEnabled()) {
+                                getLogger().debug("processReader: cached content is invalid for '" +
+                                                  environment.getURI() + "'.");
+                            }
+                            // remove invalid cached object
+                            this.cache.remove(pcKey);
+                        }
+                    }
+                }
+            }
+
+            if (!usedCache) {
+                if (pcKey != null) {
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("processReader: caching content for further requests of '" +
+                                          environment.getURI() + "'.");
+                    }
+
+                    if (readerValidity == null) {
+                        readerValidity = ((CacheableProcessingComponent)super.reader).getValidity();
+                    }
+
+                    if (readerValidity != null) {
+                        outputStream = environment.getOutputStream(this.outputBufferSize);
+                        outputStream = new CachingOutputStream(outputStream);
+                    } else {
+                        pcKey = null;
+                    }
+                }
+
+                setMimeTypeForReader(environment);
+                if (this.reader.shouldSetContentLength()) {
+                    ByteArrayOutputStream os = new ByteArrayOutputStream();
+                    this.reader.setOutputStream(os);
+                    this.reader.generate();
+                    environment.setContentLength(os.size());
+                    if (outputStream == null) {
+                        outputStream = environment.getOutputStream(0);
+                    }
+                    os.writeTo(outputStream);
+                } else {
+                    if (outputStream == null) {
+                        outputStream = environment.getOutputStream(this.outputBufferSize);
+                    }
+                    this.reader.setOutputStream(outputStream);
+                    this.reader.generate();
+                }
+
+                // store the response
+                if (pcKey != null) {
+                    final CachedResponse res = new CachedResponse(new SourceValidity[] {readerValidity},
+                            ((CachingOutputStream)outputStream).getContent());
+                    res.setContentType(environment.getContentType());
+                    this.cache.store(pcKey, res);
+                }
+            }
+        } catch (Exception e) {
+            handleException(e);
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Return valid validity objects for the event pipeline.
+     *
+     * If the event pipeline (the complete pipeline without the
+     * serializer) is cacheable and valid, return all validity objects.
+     *
+     * Otherwise, return <code>null</code>.
+     */
+    public SourceValidity getValidityForEventPipeline() {
+        if (isInternalError()) {
+            return null;
+        }
+
+        if (this.cachedResponse != null) {
+            if (!this.cacheCompleteResponse &&
+                    this.firstNotCacheableTransformerIndex < super.transformers.size()) {
+                // Cache contains only partial pipeline.
+                return null;
+            }
+
+            if (this.toCacheSourceValidities != null) {
+                // This means the pipeline is valid based on the validities
+                // of the individual components
+                final AggregatedValidity validity = new AggregatedValidity();
+                for (int i=0; i < this.toCacheSourceValidities.length; i++) {
+                    validity.add(this.toCacheSourceValidities[i]);
+                }
+
+                return validity;
+            }
+
+            // This means that the pipeline is valid because it has not yet expired
+            return NOPValidity.SHARED_INSTANCE;
+        } else {
+            int vals = 0;
+
+            if (null != this.toCacheKey
+                    && !this.cacheCompleteResponse
+                    && this.firstNotCacheableTransformerIndex == super.transformers.size()) {
+                vals = this.toCacheKey.size();
+            } else if (null != this.fromCacheKey
+                    && !this.completeResponseIsCached
+                    && this.firstProcessedTransformerIndex == super.transformers.size()) {
+                vals = this.fromCacheKey.size();
+            }
+
+            if (vals > 0) {
+                final AggregatedValidity validity = new AggregatedValidity();
+                for (int i = 0; i < vals; i++) {
+                    validity.add(getValidityForInternalPipeline(i));
+                }
+
+                return validity;
+            }
+
+            return null;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.cocoon.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
+     */
+    public String getKeyForEventPipeline() {
+        if (isInternalError()) {
+            return null;
+        }
+
+        if (null != this.toCacheKey
+             && !this.cacheCompleteResponse
+             && this.firstNotCacheableTransformerIndex == super.transformers.size()) {
+             return String.valueOf(HashUtil.hash(this.toCacheKey.toString()));
+        }
+        if (null != this.fromCacheKey
+             && !this.completeResponseIsCached
+             && this.firstProcessedTransformerIndex == super.transformers.size()) {
+            return String.valueOf(HashUtil.hash(this.fromCacheKey.toString()));
+        }
+
+        return null;
+    }
+
+    SourceValidity getValidityForInternalPipeline(int index) {
+        final SourceValidity validity;
+
+        // if debugging try to tell why something is not cacheable
+        final boolean debug = this.getLogger().isDebugEnabled();
+        String msg = null;
+        if(debug) msg = "getValidityForInternalPipeline(" + index + "): ";
+
+        if (index == 0) {
+            // test generator
+            validity = ((CacheableProcessingComponent)super.generator).getValidity();
+            if(debug) msg += "generator: using getValidity";
+        } else if (index <= firstNotCacheableTransformerIndex) {
+            // test transformer
+            final Transformer trans = (Transformer)super.transformers.get(index-1);
+            validity = ((CacheableProcessingComponent)trans).getValidity();
+            if(debug) msg += "transformer: using getValidity";
+        } else {
+            // test serializer
+            validity = ((CacheableProcessingComponent)super.serializer).getValidity();
+            if(debug) msg += "serializer: using getValidity";
+        }
+
+        if(debug) {
+            msg += ", validity==" + validity;
+            this.getLogger().debug(msg);
+        }
+        return validity;
+    }
+
+    /**
+     * Recyclable Interface
+     */
+    public void recycle() {
+
+        this.generatorRole = null;
+        this.transformerRoles.clear();
+        this.serializerRole = null;
+        this.readerRole = null;
+
+        this.fromCacheKey = null;
+        this.cachedResponse = null;
+
+        this.toCacheKey = null;
+        this.toCacheSourceValidities = null;
+
+        super.recycle();
+    }
+}
+
+final class DeferredPipelineValidity implements DeferredValidity {
+
+    private final AbstractCachingProcessingPipeline pipeline;
+    private final int index;
+
+    public DeferredPipelineValidity(AbstractCachingProcessingPipeline pipeline, int index) {
+        this.pipeline = pipeline;
+        this.index = index;
+    }
+
+    /**
+     * @see org.apache.excalibur.source.impl.validity.DeferredValidity#getValidity()
+     */
+    public SourceValidity getValidity() {
+        return pipeline.getValidityForInternalPipeline(this.index);
+    }
+}

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

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/BaseCachingProcessingPipeline.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/BaseCachingProcessingPipeline.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/BaseCachingProcessingPipeline.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/components/pipeline/impl/BaseCachingProcessingPipeline.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.pipeline.impl;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.cocoon.caching.Cache;
+import org.apache.cocoon.components.pipeline.AbstractProcessingPipeline;
+import org.apache.cocoon.components.sax.XMLDeserializer;
+import org.apache.cocoon.components.sax.XMLSerializer;
+
+/**
+ * This is the base class for all caching pipeline implementations.
+ * The pipeline can be configured with the {@link Cache} to use
+ * by specifying the <code>cache-role</code> parameter.
+ *
+ * @since 2.1
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @version $Id: BaseCachingProcessingPipeline.java 157160 2005-03-11 21:18:58Z vgritsenko $
+ */
+public abstract class BaseCachingProcessingPipeline extends AbstractProcessingPipeline
+                                                    implements Disposable {
+
+    /** This is the Cache holding cached responses */
+    protected Cache  cache;
+
+    /** The deserializer */
+    protected XMLDeserializer xmlDeserializer;
+
+    /** The serializer */
+    protected XMLSerializer xmlSerializer;
+
+    /**
+     * Parameterizable Interface - Configuration
+     */
+    public void parameterize(Parameters params)
+    throws ParameterException {
+        super.parameterize(params);
+
+        String cacheRole = params.getParameter("cache-role", Cache.ROLE);
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Using cache " + cacheRole);
+        }
+
+        try {
+            this.cache = (Cache) this.manager.lookup(cacheRole);
+        } catch (ServiceException ce) {
+            throw new ParameterException("Unable to lookup cache: " + cacheRole, ce);
+        }
+    }
+
+    /**
+     * Recyclable Interface
+     */
+    public void recycle() {
+        this.manager.release(this.xmlDeserializer);
+        this.xmlDeserializer = null;
+
+        this.manager.release(this.xmlSerializer);
+        this.xmlSerializer = null;
+
+        super.recycle();
+    }
+
+    /**
+     * Disposable Interface
+     */
+    public void dispose() {
+        if (null != this.manager) {
+            this.manager.release(this.cache);
+        }
+        this.cache = null;
+        this.manager = null;
+    }
+}

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



Mime
View raw message