KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > pipeline > impl > ExpiresCachingProcessingPipeline


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.pipeline.impl;
17
18 import org.apache.avalon.framework.component.ComponentException;
19 import org.apache.avalon.framework.parameters.ParameterException;
20 import org.apache.avalon.framework.parameters.Parameters;
21
22 import org.apache.cocoon.ProcessingException;
23 import org.apache.cocoon.caching.CachedResponse;
24 import org.apache.cocoon.caching.CachingOutputStream;
25 import org.apache.cocoon.caching.IdentifierCacheKey;
26 import org.apache.cocoon.components.sax.XMLDeserializer;
27 import org.apache.cocoon.components.sax.XMLSerializer;
28 import org.apache.cocoon.components.sax.XMLTeePipe;
29 import org.apache.cocoon.environment.Environment;
30 import org.apache.cocoon.environment.ObjectModelHelper;
31 import org.apache.cocoon.environment.Response;
32 import org.apache.cocoon.xml.XMLConsumer;
33
34 import org.apache.excalibur.source.SourceValidity;
35 import org.apache.excalibur.source.impl.validity.ExpiresValidity;
36
37 import java.io.ByteArrayOutputStream JavaDoc;
38 import java.io.OutputStream JavaDoc;
39 import java.util.Map JavaDoc;
40
41 /**
42  * This pipeline implementation caches the complete content for a defined
43  * period of time (expires).
44  *
45  * <map:pipe name="expires" SRC="org.apache.cocoon.components.pipeline.impl.ExpiresCachingProcessingPipeline">
46  * <parameter name="cache-expires" value="180"/> <!-- Expires in secondes -->
47  * </map:pipe>
48  *
49  * @since 2.1
50  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
51  * @version CVS $Id: ExpiresCachingProcessingPipeline.java 189614 2005-06-08 18:07:32Z cziegeler $
52  */

53 public class ExpiresCachingProcessingPipeline
54     extends BaseCachingProcessingPipeline {
55
56     /** This key can be used to put a key in the object model */
57     public static final String JavaDoc CACHE_KEY_KEY = ExpiresCachingProcessingPipeline.class.getName() + "/CacheKey";
58
59     /** This key can be used to put an expires information in the object model */
60     public static final String JavaDoc CACHE_EXPIRES_KEY = ExpiresCachingProcessingPipeline.class.getName() + "/Expires";
61
62     /** The source validity */
63     protected SourceValidity cacheValidity;
64
65     /** The key used for caching */
66     protected IdentifierCacheKey cacheKey;
67
68     /** The expires information */
69     protected long cacheExpires;
70
71     /** Default value for expiration */
72     protected long defaultCacheExpires = 3600; // 1 hour
73

74     /** The cached response */
75     protected CachedResponse cachedResponse;
76
77     public void parameterize(Parameters params)
78     throws ParameterException {
79         super.parameterize(params);
80
81         this.defaultCacheExpires = params.getParameterAsLong("cache-expires", this.defaultCacheExpires);
82     }
83
84     /**
85      * Process the given <code>Environment</code>, producing the output.
86      */

87     protected boolean processXMLPipeline(Environment environment)
88     throws ProcessingException {
89         try {
90             if (this.cachedResponse != null) {
91                 byte[] content = cachedResponse.getResponse();
92
93                 if ( this.serializer == this.lastConsumer ) {
94                     if ( cachedResponse.getContentType() != null ) {
95                         environment.setContentType(cachedResponse.getContentType());
96                     } else {
97                         this.setMimeTypeForSerializer(environment);
98                     }
99                     final OutputStream outputStream = environment.getOutputStream(0);
100                     if (content.length > 0) {
101                         environment.setContentLength(content.length);
102                         outputStream.write(content);
103                     }
104                 } else {
105                     this.setMimeTypeForSerializer(environment);
106                     this.xmlDeserializer.setConsumer( this.lastConsumer );
107                     this.xmlDeserializer.deserialize( content );
108                 }
109
110             } else {
111
112                 // generate new response
113

114                 if ( this.cacheExpires == 0 ) {
115                     return super.processXMLPipeline( environment );
116                 }
117
118                 this.setMimeTypeForSerializer(environment);
119                 byte[] cachedData;
120                 if ( this.serializer == this.lastConsumer ) {
121
122                     if (this.serializer.shouldSetContentLength()) {
123                         OutputStream os = environment.getOutputStream(this.outputBufferSize);
124
125                         // set the output stream
126
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
127                         this.serializer.setOutputStream(baos);
128
129                         this.generator.generate();
130
131                         cachedData = baos.toByteArray();
132                         environment.setContentLength(cachedData.length);
133                         os.write(cachedData);
134                     } else {
135                         CachingOutputStream os = new CachingOutputStream( environment.getOutputStream(this.outputBufferSize) );
136                         // set the output stream
137
this.serializer.setOutputStream( os );
138                         this.generator.generate();
139
140                         cachedData = os.getContent();
141                     }
142
143                 } else {
144                     this.generator.generate();
145                     cachedData = (byte[])this.xmlSerializer.getSAXFragment();
146                 }
147
148                 //
149
// Now that we have processed the pipeline,
150
// we do the actual caching
151
//
152
if (this.cacheValidity != null) {
153                     cachedResponse = new CachedResponse(this.cacheValidity,
154                                                         cachedData);
155                     cachedResponse.setContentType(environment.getContentType());
156                     this.cache.store(this.cacheKey, cachedResponse);
157                 }
158             }
159         } catch (Exception JavaDoc e) {
160             handleException(e);
161         }
162
163         return true;
164     }
165
166     /**
167      * Connect the XML pipeline.
168      */

169     protected void connectPipeline(Environment environment)
170     throws ProcessingException {
171         if ( this.lastConsumer != this.serializer ) {
172             // internal
173
if ( this.cachedResponse == null) {
174                 // if we cache, we need an xml serializer
175
if ( this.cacheExpires > 0) {
176                     try {
177                         final XMLConsumer old = this.lastConsumer;
178                         this.xmlSerializer = (XMLSerializer)this.manager.lookup( XMLSerializer.ROLE );
179                         this.lastConsumer = new XMLTeePipe(this.lastConsumer, this.xmlSerializer);
180
181                         super.connectPipeline( environment );
182
183                         this.lastConsumer = old;
184                     } catch ( ComponentException e ) {
185                         throw new ProcessingException("Could not connect pipeline.", e);
186                     }
187                 } else {
188                     super.connectPipeline( environment );
189                 }
190             } else {
191                 // we use the cache, so we need an xml deserializer
192
try {
193                     this.xmlDeserializer = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
194                 } catch ( ComponentException e ) {
195                     throw new ProcessingException("Could not connect pipeline.", e);
196                 }
197             }
198         } else {
199             // external: we only need to connect if we don't use a cached response
200
if ( this.cachedResponse == null) {
201                 super.connectPipeline( environment );
202             }
203         }
204     }
205
206     /**
207      * Prepare the pipeline
208      */

209     protected void preparePipeline(Environment environment)
210     throws ProcessingException {
211         // get the key and the expires info
212
// we must do this before we call super.preparePipeline,
213
// otherwise internal pipelines are instantiated and
214
// get a copy of the object model with our info!
215
final Map JavaDoc objectModel = environment.getObjectModel();
216         String JavaDoc key = (String JavaDoc)objectModel.get(CACHE_KEY_KEY);
217         if ( key == null ) {
218             key = this.parameters.getParameter("cache-key", null);
219             if ( key == null ) {
220                 key = environment.getURIPrefix()+environment.getURI();
221             }
222         } else {
223             objectModel.remove(CACHE_KEY_KEY);
224         }
225         String JavaDoc expiresValue = (String JavaDoc)objectModel.get(CACHE_EXPIRES_KEY);
226         if ( expiresValue == null ) {
227             this.cacheExpires = this.parameters.getParameterAsLong("cache-expires", this.defaultCacheExpires);
228         } else {
229             this.cacheExpires = Long.valueOf(expiresValue).longValue();
230             objectModel.remove(CACHE_EXPIRES_KEY);
231         }
232
233         // prepare the pipeline
234
super.preparePipeline( environment );
235
236         // and now prepare the caching information
237
this.cacheKey = new IdentifierCacheKey(key,
238                                            this.serializer == this.lastConsumer);
239         if ( this.cacheExpires > 0) {
240             this.cacheValidity = new ExpiresValidity(this.cacheExpires*1000);
241         }
242         final boolean purge = this.parameters.getParameterAsBoolean("purge-cache", false);
243
244         this.cachedResponse = this.cache.get(this.cacheKey);
245         if ( this.cachedResponse != null ) {
246             final SourceValidity sv = cachedResponse.getValidityObjects()[0];
247             if ( purge
248                  || (this.cacheExpires != -1 && sv.isValid() != SourceValidity.VALID) ) {
249                 this.cache.remove( this.cacheKey );
250                 this.cachedResponse = null;
251             }
252         }
253         if ( this.cacheExpires > 0
254              && (this.reader != null || this.lastConsumer == this.serializer )) {
255             Response res = ObjectModelHelper.getResponse(environment.getObjectModel());
256             res.setDateHeader("Expires", System.currentTimeMillis() + (this.cacheExpires*1000));
257             res.setHeader("Cache-Control", "max-age=" + this.cacheExpires + ", public");
258         }
259     }
260
261     /**
262      * Return valid validity objects for the event pipeline
263      * If the "event pipeline" (= the complete pipeline without the
264      * serializer) is cacheable and valid, return all validity objects.
265      * Otherwise return <code>null</code>
266      */

267     public SourceValidity getValidityForEventPipeline() {
268         return this.cacheValidity;
269     }
270
271     /* (non-Javadoc)
272      * @see org.apache.cocoon.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
273      */

274     public String JavaDoc getKeyForEventPipeline() {
275         if (this.cacheKey != null && this.cacheValidity != null) {
276             return this.cacheKey.toString();
277         }
278         return null;
279     }
280
281     /**
282      * Recyclable Interface
283      */

284     public void recycle() {
285         this.cacheKey = null;
286         this.cacheExpires = 0;
287         this.cachedResponse = null;
288         super.recycle();
289     }
290
291     /* (non-Javadoc)
292      * @see org.apache.cocoon.components.pipeline.AbstractProcessingPipeline#processReader(org.apache.cocoon.environment.Environment)
293      */

294     protected boolean processReader(Environment environment)
295     throws ProcessingException {
296         try {
297             if (this.cachedResponse != null) {
298                 if ( cachedResponse.getContentType() != null ) {
299                     environment.setContentType(cachedResponse.getContentType());
300                 } else {
301                     this.setMimeTypeForReader(environment);
302                 }
303
304                 final byte[] content = cachedResponse.getResponse();
305                 environment.setContentLength(content.length);
306
307                 final OutputStream os = environment.getOutputStream(0);
308                 os.write(content);
309
310             } else {
311                 // generate new response
312

313                 if ( this.cacheExpires == 0 ) {
314                     return super.processReader( environment );
315                 }
316
317                 byte[] cachedData;
318
319                 this.setMimeTypeForReader(environment);
320                 if (this.reader.shouldSetContentLength()) {
321                     final OutputStream os = environment.getOutputStream(this.outputBufferSize);
322
323                     // set the output stream
324
final ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
325                     this.reader.setOutputStream(baos);
326
327                     this.reader.generate();
328
329                     cachedData = baos.toByteArray();
330                     environment.setContentLength(cachedData.length);
331                     os.write(cachedData);
332                 } else {
333                     final CachingOutputStream os = new CachingOutputStream( environment.getOutputStream(this.outputBufferSize) );
334                     // set the output stream
335
this.reader.setOutputStream( os );
336                     this.reader.generate();
337
338                     cachedData = os.getContent();
339                 }
340
341                 //
342
// Now that we have processed the pipeline,
343
// we do the actual caching
344
//
345
if (this.cacheValidity != null) {
346                     cachedResponse = new CachedResponse(this.cacheValidity,
347                                                         cachedData);
348                     cachedResponse.setContentType(environment.getContentType());
349                     this.cache.store(this.cacheKey, cachedResponse);
350                 }
351             }
352         } catch (Exception JavaDoc e) {
353             handleException(e);
354         }
355
356         return true;
357     }
358 }
359
Popular Tags