/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.pipeline;
import java.io.OutputStream;
import java.util.Map;
import org.apache.cocoon.pipeline.caching.CacheKey;
import org.apache.cocoon.pipeline.caching.CacheValue;
import org.apache.cocoon.pipeline.caching.CachingOutputStream;
import org.apache.cocoon.pipeline.caching.CompleteCacheValue;
import org.apache.cocoon.pipeline.caching.CompoundCacheKey;
import org.apache.cocoon.pipeline.caching.PipelineCache;
import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
import org.apache.cocoon.pipeline.component.PipelineComponent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <p>
* A {@link Pipeline} implementation that returns a cached result if, and only if all its components support caching. A
* {@link PipelineComponent} is cacheable if it implements the interface {@link CachingPipelineComponent}.
* </p>
*/
public class CachingPipeline extends AbstractPipeline {
private final Log logger = LogFactory.getLog(this.getClass());
protected PipelineCache pipelineCache;
protected CacheKey cacheKey;
protected CachingOutputStream cachingOutputStream;
@Override
public void setup(OutputStream outputStream) {
this.setup(outputStream);
}
@Override
public void setup(OutputStream outputStream, Map<String, Object> parameters) {
// create a caching output stream to intercept the result
this.cachingOutputStream = new CachingOutputStream(outputStream);
super.setup(this.cachingOutputStream, parameters);
}
@Override
public void execute() throws Exception {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Used cache: " + this.pipelineCache);
}
this.cacheKey = this.constructCacheKey();
// checked for a cached value first
CacheValue cachedValue = this.getCachedValue(this.cacheKey);
if (cachedValue != null && cachedValue.isValid(this.cacheKey)) {
// cached value found
if (this.logger.isDebugEnabled()) {
this.logger.debug("Write cache value to output stream: " + cachedValue);
}
cachedValue.writeTo(this.cachingOutputStream.getOutputStream());
return;
}
// execute the pipeline
this.invokeStarter();
// cache the result
this
.setCachedValue(this.cacheKey, new CompleteCacheValue(this.cachingOutputStream.getContent(),
this.cacheKey));
}
public void setPipelineCache(PipelineCache pipelineCache) {
this.pipelineCache = pipelineCache;
}
@Override
public long getLastModified() {
if (this.cacheKey == null) {
return -1;
}
return this.cacheKey.getLastModifed();
}
public CacheKey constructCacheKey() {
CompoundCacheKey result = new CompoundCacheKey();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating " + result + ": ");
}
for (PipelineComponent pipelineComponent : this.getComponents()) {
if (pipelineComponent instanceof CachingPipelineComponent) {
CachingPipelineComponent cachablePipelineComponent = (CachingPipelineComponent) pipelineComponent;
CacheKey cacheKey = cachablePipelineComponent.constructCacheKey();
result.addCacheKey(cacheKey);
if (this.logger.isDebugEnabled()) {
this.logger.debug(" ~ adding " + cacheKey + " for component " + pipelineComponent);
}
continue;
}
// component does not support caching
if (this.logger.isDebugEnabled()) {
this.logger.debug(" ~ no caching: " + pipelineComponent);
this.logger.debug("Stop construction a cache key");
}
return null;
}
return result;
}
protected CacheValue getCachedValue(CacheKey cacheKey) {
if (cacheKey == null) {
return null;
}
CacheValue cacheValue = this.pipelineCache.get(cacheKey);
if (this.logger.isDebugEnabled()) {
if (cacheValue != null) {
this.logger.debug("Retrieved content from cache: " + cacheValue);
} else {
this.logger.debug("No cache value available for " + cacheKey);
}
}
return cacheValue;
}
protected void setCachedValue(CacheKey cacheKey, CacheValue cacheValue) {
if (cacheKey != null) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Putting result into pipeline cache: " + cacheValue + ")");
}
this.pipelineCache.put(cacheKey, cacheValue);
}
}
}