blob: 5a352cede6e114d77cb6577e8d391bd84786d6c8 [file] [log] [blame]
/*
* 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.impl;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.ConnectionResetException;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.caching.CacheValidity;
import org.apache.cocoon.caching.CacheValidityToSourceValidity;
import org.apache.cocoon.caching.Cacheable;
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;
/**
* This is the base class for all caching pipeline implementations
* that check the 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 CVS $Id: AbstractCachingProcessingPipeline.java,v 1.17 2004/03/05 13:02:50 bdelacretaz Exp $
*/
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 byte stream */
protected byte[] cachedResponse;
/** The timestamp of the cached byte stream */
protected long cachedLastModified;
/** 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;
protected boolean generatorIsCacheableProcessingComponent;
protected boolean serializerIsCacheableProcessingComponent;
protected boolean[] transformerIsCacheableProcessingComponent;
/** 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);
} else if (this.cachedResponse != null
&& this.completeResponseIsCached) {
// Allow for 304 (not modified) responses in dynamic content
if (super.checkIfModified(environment, this.cachedLastModified)) {
return true;
}
try {
final OutputStream outputStream =
environment.getOutputStream(0);
if (this.cachedResponse.length > 0) {
environment.setContentLength(this.cachedResponse.length);
outputStream.write(this.cachedResponse);
}
} catch ( SocketException se ) {
if (se.getMessage().indexOf("reset") > 0
|| se.getMessage().indexOf("aborted") > 0
|| se.getMessage().indexOf("connection abort") > 0) {
throw new ConnectionResetException(
"Connection reset by peer", se);
} else {
throw new ProcessingException(
"Failed to execute pipeline.", se);
}
} catch ( Exception e ) {
if (e instanceof ProcessingException)
throw (ProcessingException)e;
throw new ProcessingException("Error executing pipeline.",e);
}
} else {
if (this.getLogger().isDebugEnabled() && this.toCacheKey != null) {
this.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);
} 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);
} else {
this.generator.generate();
}
byte[] data = baos.toByteArray();
environment.setContentLength(data.length);
os.write(data);
} 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);
} else {
this.generator.generate();
}
}
}
//
// Now that we have processed the pipeline,
// we do the actual caching
//
this.cacheResults(environment,os);
} catch (SocketException se) {
if (se.getMessage().indexOf("reset") > 0
|| se.getMessage().indexOf("aborted") > 0
|| se.getMessage().indexOf("connection abort") > 0) {
throw new ConnectionResetException(
"Connection reset by peer", se);
} else {
throw new ProcessingException(
"Failed to execute pipeline.", se);
}
} catch (ProcessingException e) {
throw e;
} catch (Exception e) {
throw new ProcessingException(
"Failed to execute pipeline.", 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;
Serializable key = null;
this.generatorIsCacheableProcessingComponent = false;
this.serializerIsCacheableProcessingComponent = false;
this.transformerIsCacheableProcessingComponent =
new boolean[this.transformers.size()];
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?
if (super.generator instanceof CacheableProcessingComponent) {
key = ((CacheableProcessingComponent)super.generator).getKey();
this.generatorIsCacheableProcessingComponent = true;
} else if (super.generator instanceof Cacheable) {
key = new Long(((Cacheable)super.generator).generateKey());
}
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();
this.transformerIsCacheableProcessingComponent[this.firstNotCacheableTransformerIndex] = true;
} else if (trans instanceof Cacheable) {
key = new Long(((Cacheable)trans).generateKey());
}
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();
this.serializerIsCacheableProcessingComponent = true;
} else if (this.serializer instanceof Cacheable) {
key = new Long(((Cacheable)this.serializer).generateKey());
}
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 =
this.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;
this.cachedLastModified = 0L;
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 (this.getLogger().isDebugEnabled()) {
this.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 (this.getLogger().isDebugEnabled()) {
this.getLogger().debug(
"Expires time found for " +
environment.getURI());
}
if ( responseExpires.longValue() > System.currentTimeMillis()) {
if (this.getLogger().isDebugEnabled()) {
this.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.getResponse();
this.cachedLastModified = response.getLastModified();
return;
} else {
if (this.getLogger().isDebugEnabled()) {
this.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 ? validity.isValid() : -1;
if ( valid == 0) { // don't know if valid, make second test
validity = this.getValidityForInternalPipeline(i);
if (validity != null) {
valid = fromCacheValidityObjects[i].isValid( validity );
if (valid == 0) {
validity = null;
} else {
isValid = (valid == 1);
}
}
} else {
isValid = (valid == 1);
}
if ( !isValid ) {
responseIsValid = false;
// update validity
if (validity == null) {
responseIsUsable = false;
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("validatePipeline: responseIsUsable is false, valid==" + valid + " at index " + i);
}
} else {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("validatePipeline: responseIsValid is false due to " + validity);
}
}
} else {
i++;
}
}
if ( responseIsValid ) {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("validatePipeline: using valid cached content for '" + environment.getURI() + "'.");
}
// we are valid, ok that's it
this.cachedResponse = response.getResponse();
this.cachedLastModified = response.getLastModified();
} else {
if (this.getLogger().isDebugEnabled()) {
this.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;
boolean isCacheableProcessingComponent = false;
if (super.reader instanceof CacheableProcessingComponent) {
readerKey = ((CacheableProcessingComponent)super.reader).getKey();
isCacheableProcessingComponent = true;
} else if (super.reader instanceof Cacheable) {
readerKey = new Long(((Cacheable)super.reader).generateKey());
}
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 (this.getLogger().isDebugEnabled()) {
this.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 (this.getLogger().isDebugEnabled()) {
this.getLogger().debug(
"Cached response for '" +
environment.getURI() + "' using key: " +
pcKey + " is invalid.");
}
cachedResponse = null;
} else {
SourceValidity cachedValidity = validities[0];
int result = cachedValidity.isValid();
boolean valid = false;
if ( result == 0 ) {
// get reader validity and compare
if (isCacheableProcessingComponent) {
readerValidity = ((CacheableProcessingComponent)super.reader).getValidity();
} else {
CacheValidity cv = ((Cacheable)super.reader).generateValidity();
if ( cv != null ) {
readerValidity = CacheValidityToSourceValidity.createValidity( cv );
}
}
if (readerValidity != null) {
result = cachedValidity.isValid(readerValidity);
if ( result == 0 ) {
readerValidity = null;
} else {
valid = (result == 1);
}
}
} else {
valid = (result > 0);
}
if (valid) {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("processReader: using valid cached content for '" + environment.getURI() + "'.");
}
byte[] response = cachedObject.getResponse();
if (response.length > 0) {
usedCache = true;
outputStream = environment.getOutputStream(0);
environment.setContentLength(response.length);
outputStream.write(response);
}
} else {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("processReader: cached content is invalid for '" + environment.getURI() + "'.");
}
// remove invalid cached object
this.cache.remove(pcKey);
}
}
}
}
if (!usedCache) {
if ( pcKey != null ) {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("processReader: caching content for further requests of '" + environment.getURI() + "'.");
}
if (readerValidity == null) {
if (isCacheableProcessingComponent) {
readerValidity = ((CacheableProcessingComponent)super.reader).getValidity();
} else {
CacheValidity cv = ((Cacheable)super.reader).generateValidity();
if ( cv != null ) {
readerValidity = CacheValidityToSourceValidity.createValidity( cv );
}
}
}
if (readerValidity != null) {
outputStream = environment.getOutputStream(this.outputBufferSize);
outputStream = new CachingOutputStream(outputStream);
} else {
pcKey = null;
}
}
if (this.reader.shouldSetContentLength()) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
this.reader.setOutputStream(os);
this.reader.generate();
byte[] data = os.toByteArray();
environment.setContentLength(data.length);
if (outputStream == null) {
outputStream = environment.getOutputStream(0);
}
environment.getOutputStream(0).write(data);
} else {
if (outputStream == null) {
outputStream = environment.getOutputStream(this.outputBufferSize);
}
this.reader.setOutputStream(outputStream);
this.reader.generate();
}
// store the response
if (pcKey != null) {
this.cache.store(
pcKey,
new CachedResponse( new SourceValidity[] {readerValidity},
((CachingOutputStream)outputStream).getContent())
);
}
}
} catch ( SocketException se ) {
if (se.getMessage().indexOf("reset") > 0
|| se.getMessage().indexOf("aborted") > 0
|| se.getMessage().indexOf("connection abort") > 0) {
throw new ConnectionResetException("Connection reset by peer", se);
} else {
throw new ProcessingException("Failed to execute pipeline.", se);
}
} catch ( ProcessingException e ) {
throw e;
} catch ( Exception e ) {
throw new ProcessingException("Failed to execute pipeline.", 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() {
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(this.getValidityForInternalPipeline(i));
//validity.add(new DeferredPipelineValidity(this, i));
}
return validity;
}
return null;
}
/* (non-Javadoc)
* @see org.apache.cocoon.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
*/
public String getKeyForEventPipeline() {
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
if (this.generatorIsCacheableProcessingComponent) {
validity = ((CacheableProcessingComponent)super.generator).getValidity();
if(debug) msg += "generator: using getValidity";
} else {
validity = CacheValidityToSourceValidity.createValidity(((Cacheable)super.generator).generateValidity());
if(debug) msg += "generator: using generateValidity";
}
} else if (index <= firstNotCacheableTransformerIndex) {
// test transformer
final Transformer trans = (Transformer)super.transformers.get(index-1);
if (this.transformerIsCacheableProcessingComponent[index-1]) {
validity = ((CacheableProcessingComponent)trans).getValidity();
if(debug) msg += "transformer: using getValidity";
} else {
validity = CacheValidityToSourceValidity.createValidity(((Cacheable)trans).generateValidity());
if(debug) msg += "transformer: using generateValidity";
}
} else {
// test serializer
if (this.serializerIsCacheableProcessingComponent) {
validity = ((CacheableProcessingComponent)super.serializer).getValidity();
if(debug) msg += "serializer: using getValidity";
} else {
validity = CacheValidityToSourceValidity.createValidity(((Cacheable)super.serializer).generateValidity());
if(debug) msg += "serializer: using generateValidity";
}
}
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.transformerIsCacheableProcessingComponent = 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);
}
}