You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by cz...@apache.org on 2005/06/08 20:07:33 UTC
svn commit: r189614 - in /cocoon/branches/BRANCH_2_1_X: ./
src/java/org/apache/cocoon/caching/
src/java/org/apache/cocoon/components/pipeline/impl/ src/webapp/
Author: cziegeler
Date: Wed Jun 8 11:07:32 2005
New Revision: 189614
URL: http://svn.apache.org/viewcvs?rev=189614&view=rev
Log:
Move expires pipeline implementation out of the scratchpad.
Added:
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java (with props)
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java (with props)
Modified:
cocoon/branches/BRANCH_2_1_X/src/webapp/sitemap.xmap
cocoon/branches/BRANCH_2_1_X/status.xml
Added: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java?rev=189614&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java (added)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java Wed Jun 8 11:07:32 2005
@@ -0,0 +1,94 @@
+/*
+ * 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.caching;
+
+import java.io.Serializable;
+
+/**
+ * This is a "simple" cache key that does not consider the components used in the
+ * pipeline. It simply consists of a key (unique identifier for the request) and
+ * a boolean value that defines if the key is for a complete pipeline call or
+ * for an internal pipeline call.
+ *
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @version CVS $Id$
+ * @since 2.1.1
+ */
+public class IdentifierCacheKey
+ implements Serializable {
+
+ /** The key */
+ final protected String key;
+
+ /** Is this an external pipeline call? */
+ final protected boolean external;
+
+ /** cache key */
+ final protected String cacheKey;
+
+ /** cache toString() */
+ protected String toString;
+
+ /**
+ * Constructor
+ */
+ public IdentifierCacheKey(String key, boolean external) {
+ this.key = key;
+ this.external = external;
+ final StringBuffer buf = new StringBuffer();
+ buf.append(this.external).append(':').append(this.key);
+ this.cacheKey = buf.toString();
+ }
+
+ /**
+ * Compare
+ */
+ public boolean equals(Object object) {
+ if (object instanceof IdentifierCacheKey) {
+ IdentifierCacheKey pck = (IdentifierCacheKey)object;
+ return this.cacheKey.equals( pck.cacheKey );
+ }
+ return false;
+ }
+
+ /**
+ * Generate a hash code
+ */
+ public int hashCode() {
+ return this.cacheKey.hashCode();
+ }
+
+ /**
+ * toString
+ * The FilesystemStore uses toString!
+ */
+ public String toString() {
+ if (this.toString == null) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("IK:");
+ buffer.append(this.cacheKey);
+ this.toString = buffer.toString();
+ }
+ return toString;
+ }
+
+ /**
+ * The cache key
+ */
+ public String getKey() {
+ return this.key;
+ }
+}
Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/caching/IdentifierCacheKey.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java?rev=189614&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java (added)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java Wed Jun 8 11:07:32 2005
@@ -0,0 +1,358 @@
+/*
+ * 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.component.ComponentException;
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameters;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.caching.CachedResponse;
+import org.apache.cocoon.caching.CachingOutputStream;
+import org.apache.cocoon.caching.IdentifierCacheKey;
+import org.apache.cocoon.components.sax.XMLDeserializer;
+import org.apache.cocoon.components.sax.XMLSerializer;
+import org.apache.cocoon.components.sax.XMLTeePipe;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Response;
+import org.apache.cocoon.xml.XMLConsumer;
+
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.validity.ExpiresValidity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+/**
+ * This pipeline implementation caches the complete content for a defined
+ * period of time (expires).
+ *
+ * <map:pipe name="expires" src="org.apache.cocoon.components.pipeline.impl.ExpiresCachingProcessingPipeline">
+ * <parameter name="cache-expires" value="180"/> <!-- Expires in secondes -->
+ * </map:pipe>
+ *
+ * @since 2.1
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @version CVS $Id$
+ */
+public class ExpiresCachingProcessingPipeline
+ extends BaseCachingProcessingPipeline {
+
+ /** This key can be used to put a key in the object model */
+ public static final String CACHE_KEY_KEY = ExpiresCachingProcessingPipeline.class.getName() + "/CacheKey";
+
+ /** This key can be used to put an expires information in the object model */
+ public static final String CACHE_EXPIRES_KEY = ExpiresCachingProcessingPipeline.class.getName() + "/Expires";
+
+ /** The source validity */
+ protected SourceValidity cacheValidity;
+
+ /** The key used for caching */
+ protected IdentifierCacheKey cacheKey;
+
+ /** The expires information */
+ protected long cacheExpires;
+
+ /** Default value for expiration */
+ protected long defaultCacheExpires = 3600; // 1 hour
+
+ /** The cached response */
+ protected CachedResponse cachedResponse;
+
+ public void parameterize(Parameters params)
+ throws ParameterException {
+ super.parameterize(params);
+
+ this.defaultCacheExpires = params.getParameterAsLong("cache-expires", this.defaultCacheExpires);
+ }
+
+ /**
+ * Process the given <code>Environment</code>, producing the output.
+ */
+ protected boolean processXMLPipeline(Environment environment)
+ throws ProcessingException {
+ try {
+ if (this.cachedResponse != null) {
+ byte[] content = cachedResponse.getResponse();
+
+ if ( this.serializer == this.lastConsumer ) {
+ if ( cachedResponse.getContentType() != null ) {
+ environment.setContentType(cachedResponse.getContentType());
+ } else {
+ this.setMimeTypeForSerializer(environment);
+ }
+ final OutputStream outputStream = environment.getOutputStream(0);
+ if (content.length > 0) {
+ environment.setContentLength(content.length);
+ outputStream.write(content);
+ }
+ } else {
+ this.setMimeTypeForSerializer(environment);
+ this.xmlDeserializer.setConsumer( this.lastConsumer );
+ this.xmlDeserializer.deserialize( content );
+ }
+
+ } else {
+
+ // generate new response
+
+ if ( this.cacheExpires == 0 ) {
+ return super.processXMLPipeline( environment );
+ }
+
+ this.setMimeTypeForSerializer(environment);
+ byte[] cachedData;
+ if ( this.serializer == this.lastConsumer ) {
+
+ if (this.serializer.shouldSetContentLength()) {
+ OutputStream os = environment.getOutputStream(this.outputBufferSize);
+
+ // set the output stream
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ this.serializer.setOutputStream(baos);
+
+ this.generator.generate();
+
+ cachedData = baos.toByteArray();
+ environment.setContentLength(cachedData.length);
+ os.write(cachedData);
+ } else {
+ CachingOutputStream os = new CachingOutputStream( environment.getOutputStream(this.outputBufferSize) );
+ // set the output stream
+ this.serializer.setOutputStream( os );
+ this.generator.generate();
+
+ cachedData = os.getContent();
+ }
+
+ } else {
+ this.generator.generate();
+ cachedData = (byte[])this.xmlSerializer.getSAXFragment();
+ }
+
+ //
+ // Now that we have processed the pipeline,
+ // we do the actual caching
+ //
+ if (this.cacheValidity != null) {
+ cachedResponse = new CachedResponse(this.cacheValidity,
+ cachedData);
+ cachedResponse.setContentType(environment.getContentType());
+ this.cache.store(this.cacheKey, cachedResponse);
+ }
+ }
+ } catch (Exception e) {
+ handleException(e);
+ }
+
+ return true;
+ }
+
+ /**
+ * Connect the XML pipeline.
+ */
+ protected void connectPipeline(Environment environment)
+ throws ProcessingException {
+ if ( this.lastConsumer != this.serializer ) {
+ // internal
+ if ( this.cachedResponse == null) {
+ // if we cache, we need an xml serializer
+ if ( this.cacheExpires > 0) {
+ try {
+ final XMLConsumer old = this.lastConsumer;
+ this.xmlSerializer = (XMLSerializer)this.manager.lookup( XMLSerializer.ROLE );
+ this.lastConsumer = new XMLTeePipe(this.lastConsumer, this.xmlSerializer);
+
+ super.connectPipeline( environment );
+
+ this.lastConsumer = old;
+ } catch ( ComponentException e ) {
+ throw new ProcessingException("Could not connect pipeline.", e);
+ }
+ } else {
+ super.connectPipeline( environment );
+ }
+ } else {
+ // we use the cache, so we need an xml deserializer
+ try {
+ this.xmlDeserializer = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
+ } catch ( ComponentException e ) {
+ throw new ProcessingException("Could not connect pipeline.", e);
+ }
+ }
+ } else {
+ // external: we only need to connect if we don't use a cached response
+ if ( this.cachedResponse == null) {
+ super.connectPipeline( environment );
+ }
+ }
+ }
+
+ /**
+ * Prepare the pipeline
+ */
+ protected void preparePipeline(Environment environment)
+ throws ProcessingException {
+ // get the key and the expires info
+ // we must do this before we call super.preparePipeline,
+ // otherwise internal pipelines are instantiated and
+ // get a copy of the object model with our info!
+ final Map objectModel = environment.getObjectModel();
+ String key = (String)objectModel.get(CACHE_KEY_KEY);
+ if ( key == null ) {
+ key = this.parameters.getParameter("cache-key", null);
+ if ( key == null ) {
+ key = environment.getURIPrefix()+environment.getURI();
+ }
+ } else {
+ objectModel.remove(CACHE_KEY_KEY);
+ }
+ String expiresValue = (String)objectModel.get(CACHE_EXPIRES_KEY);
+ if ( expiresValue == null ) {
+ this.cacheExpires = this.parameters.getParameterAsLong("cache-expires", this.defaultCacheExpires);
+ } else {
+ this.cacheExpires = Long.valueOf(expiresValue).longValue();
+ objectModel.remove(CACHE_EXPIRES_KEY);
+ }
+
+ // prepare the pipeline
+ super.preparePipeline( environment );
+
+ // and now prepare the caching information
+ this.cacheKey = new IdentifierCacheKey(key,
+ this.serializer == this.lastConsumer);
+ if ( this.cacheExpires > 0) {
+ this.cacheValidity = new ExpiresValidity(this.cacheExpires*1000);
+ }
+ final boolean purge = this.parameters.getParameterAsBoolean("purge-cache", false);
+
+ this.cachedResponse = this.cache.get(this.cacheKey);
+ if ( this.cachedResponse != null ) {
+ final SourceValidity sv = cachedResponse.getValidityObjects()[0];
+ if ( purge
+ || (this.cacheExpires != -1 && sv.isValid() != SourceValidity.VALID) ) {
+ this.cache.remove( this.cacheKey );
+ this.cachedResponse = null;
+ }
+ }
+ if ( this.cacheExpires > 0
+ && (this.reader != null || this.lastConsumer == this.serializer )) {
+ Response res = ObjectModelHelper.getResponse(environment.getObjectModel());
+ res.setDateHeader("Expires", System.currentTimeMillis() + (this.cacheExpires*1000));
+ res.setHeader("Cache-Control", "max-age=" + this.cacheExpires + ", public");
+ }
+ }
+
+ /**
+ * 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 this.cacheValidity;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.cocoon.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
+ */
+ public String getKeyForEventPipeline() {
+ if (this.cacheKey != null && this.cacheValidity != null) {
+ return this.cacheKey.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Recyclable Interface
+ */
+ public void recycle() {
+ this.cacheKey = null;
+ this.cacheExpires = 0;
+ this.cachedResponse = null;
+ super.recycle();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.cocoon.components.pipeline.AbstractProcessingPipeline#processReader(org.apache.cocoon.environment.Environment)
+ */
+ protected boolean processReader(Environment environment)
+ throws ProcessingException {
+ try {
+ if (this.cachedResponse != null) {
+ if ( cachedResponse.getContentType() != null ) {
+ environment.setContentType(cachedResponse.getContentType());
+ } else {
+ this.setMimeTypeForReader(environment);
+ }
+
+ final byte[] content = cachedResponse.getResponse();
+ environment.setContentLength(content.length);
+
+ final OutputStream os = environment.getOutputStream(0);
+ os.write(content);
+
+ } else {
+ // generate new response
+
+ if ( this.cacheExpires == 0 ) {
+ return super.processReader( environment );
+ }
+
+ byte[] cachedData;
+
+ this.setMimeTypeForReader(environment);
+ if (this.reader.shouldSetContentLength()) {
+ final OutputStream os = environment.getOutputStream(this.outputBufferSize);
+
+ // set the output stream
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ this.reader.setOutputStream(baos);
+
+ this.reader.generate();
+
+ cachedData = baos.toByteArray();
+ environment.setContentLength(cachedData.length);
+ os.write(cachedData);
+ } else {
+ final CachingOutputStream os = new CachingOutputStream( environment.getOutputStream(this.outputBufferSize) );
+ // set the output stream
+ this.reader.setOutputStream( os );
+ this.reader.generate();
+
+ cachedData = os.getContent();
+ }
+
+ //
+ // Now that we have processed the pipeline,
+ // we do the actual caching
+ //
+ if (this.cacheValidity != null) {
+ cachedResponse = new CachedResponse(this.cacheValidity,
+ cachedData);
+ cachedResponse.setContentType(environment.getContentType());
+ this.cache.store(this.cacheKey, cachedResponse);
+ }
+ }
+ } catch (Exception e) {
+ handleException(e);
+ }
+
+ return true;
+ }
+}
Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/pipeline/impl/ExpiresCachingProcessingPipeline.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/sitemap.xmap
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/webapp/sitemap.xmap?rev=189614&r1=189613&r2=189614&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/sitemap.xmap (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/sitemap.xmap Wed Jun 8 11:07:32 2005
@@ -425,6 +425,13 @@
<map:pipe name="noncaching" src="org.apache.cocoon.components.pipeline.impl.NonCachingProcessingPipeline">
<!-- parameter name="outputBufferSize" value="8192"/ -->
</map:pipe>
+ <!--+
+ | This pipeline implementation caches the complete content for a defined
+ | period of time (expires). The cache key is the current uri.
+ +-->
+ <map:pipe name="expires" src="org.apache.cocoon.components.pipeline.impl.ExpiresCachingProcessingPipeline">
+ <parameter name="cache-expires" value="180"/> <!-- Expires in secondes -->
+ </map:pipe>
</map:pipes>
</map:components>
Modified: cocoon/branches/BRANCH_2_1_X/status.xml
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/status.xml?rev=189614&r1=189613&r2=189614&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Wed Jun 8 11:07:32 2005
@@ -196,6 +196,9 @@
<changes>
<release version="@version@" date="@date@">
+ <action dev="CZ" type="update">
+ Move expires pipeline implementation out of the scratchpad.
+ </action>
<action dev="CZ" type="add">
Add setter action to set values in the object model, request or session.
</action>