KickJava   Java API By Example, From Geeks To Geeks.

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


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.parameters.ParameterException;
19 import org.apache.avalon.framework.parameters.Parameters;
20
21 import org.apache.cocoon.ProcessingException;
22 import org.apache.cocoon.caching.CacheValidity;
23 import org.apache.cocoon.caching.CacheValidityToSourceValidity;
24 import org.apache.cocoon.caching.Cacheable;
25 import org.apache.cocoon.caching.CacheableProcessingComponent;
26 import org.apache.cocoon.caching.CachedResponse;
27 import org.apache.cocoon.caching.CachingOutputStream;
28 import org.apache.cocoon.caching.ComponentCacheKey;
29 import org.apache.cocoon.caching.PipelineCacheKey;
30 import org.apache.cocoon.environment.Environment;
31 import org.apache.cocoon.transformation.Transformer;
32 import org.apache.cocoon.util.HashUtil;
33
34 import org.apache.excalibur.source.SourceValidity;
35 import org.apache.excalibur.source.impl.validity.AggregatedValidity;
36 import org.apache.excalibur.source.impl.validity.DeferredValidity;
37 import org.apache.excalibur.source.impl.validity.NOPValidity;
38
39 import java.io.ByteArrayOutputStream JavaDoc;
40 import java.io.OutputStream JavaDoc;
41 import java.io.Serializable JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Date JavaDoc;
44
45 /**
46  * This is the base class for all caching pipeline implementations
47  * that check different pipeline components.
48  *
49  * @since 2.1
50  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
51  * @author <a HREF="mailto:Michael.Melhem@managesoft.com">Michael Melhem</a>
52  * @version $Id: AbstractCachingProcessingPipeline.java 161219 2005-04-13 21:59:24Z vgritsenko $
53  */

54 public abstract class AbstractCachingProcessingPipeline extends BaseCachingProcessingPipeline {
55
56     /** The role name of the generator */
57     protected String JavaDoc generatorRole;
58
59     /** The role names of the transfomrers */
60     protected ArrayList JavaDoc transformerRoles = new ArrayList JavaDoc();
61
62     /** The role name of the serializer */
63     protected String JavaDoc serializerRole;
64
65     /** The role name of the reader */
66     protected String JavaDoc readerRole;
67
68     /** The cached response */
69     protected CachedResponse cachedResponse;
70
71     /** The index indicating the first transformer getting input from the cache */
72     protected int firstProcessedTransformerIndex;
73
74     /** Complete response is cached */
75     protected boolean completeResponseIsCached;
76
77
78     /** This key indicates the response that is fetched from the cache */
79     protected PipelineCacheKey fromCacheKey;
80
81     /** This key indicates the response that will get into the cache */
82     protected PipelineCacheKey toCacheKey;
83
84     /** The source validities used for caching */
85     protected SourceValidity[] toCacheSourceValidities;
86
87     /** The index indicating to the first transformer which is not cacheable */
88     protected int firstNotCacheableTransformerIndex;
89
90     /** Cache complete response */
91     protected boolean cacheCompleteResponse;
92
93     protected boolean generatorIsCacheableProcessingComponent;
94     protected boolean serializerIsCacheableProcessingComponent;
95     protected boolean[] transformerIsCacheableProcessingComponent;
96
97     /** Smart caching ? */
98     protected boolean doSmartCaching;
99
100     /** Default setting for smart caching */
101     protected boolean configuredDoSmartCaching;
102
103
104     /** Abstract method defined in subclasses */
105     protected abstract void cacheResults(Environment environment,
106                                          OutputStream os)
107     throws Exception JavaDoc;
108
109     /** Abstract method defined in subclasses */
110     protected abstract ComponentCacheKey newComponentCacheKey(int type,
111                                                               String JavaDoc role,
112                                                               Serializable JavaDoc key);
113
114     /** Abstract method defined in subclasses */
115     protected abstract void connectCachingPipeline(Environment environment)
116     throws ProcessingException;
117
118
119     /**
120      * Parameterizable Interface - Configuration
121      */

122     public void parameterize(Parameters params)
123     throws ParameterException {
124         super.parameterize(params);
125         this.configuredDoSmartCaching =
126             params.getParameterAsBoolean("smart-caching", true);
127     }
128
129     /**
130      * Setup this component
131      */

132     public void setup(Parameters params) {
133         super.setup(params);
134         this.doSmartCaching = params.getParameterAsBoolean("smart-caching",
135                                                this.configuredDoSmartCaching);
136     }
137
138     /**
139      * Set the generator.
140      */

141     public void setGenerator (String JavaDoc role, String JavaDoc source, Parameters param,
142             Parameters hintParam)
143     throws ProcessingException {
144         super.setGenerator(role, source, param, hintParam);
145         this.generatorRole = role;
146     }
147
148     /**
149      * Add a transformer.
150      */

151     public void addTransformer (String JavaDoc role, String JavaDoc source, Parameters param,
152             Parameters hintParam) throws ProcessingException {
153         super.addTransformer(role, source, param, hintParam);
154         this.transformerRoles.add(role);
155     }
156
157
158     /**
159      * Set the serializer.
160      */

161     public void setSerializer (String JavaDoc role, String JavaDoc source, Parameters param,
162             Parameters hintParam, String JavaDoc mimeType) throws ProcessingException {
163         super.setSerializer(role, source, param, hintParam, mimeType);
164         this.serializerRole = role;
165     }
166
167     /**
168      * Set the Reader.
169      */

170     public void setReader (String JavaDoc role, String JavaDoc source, Parameters param,
171                            String JavaDoc mimeType)
172     throws ProcessingException {
173         super.setReader(role, source, param, mimeType);
174         this.readerRole = role;
175     }
176
177     /**
178      * Process the given <code>Environment</code>, producing the output.
179      */

180     protected boolean processXMLPipeline(Environment environment)
181     throws ProcessingException {
182         if (this.toCacheKey == null && this.cachedResponse == null) {
183             return super.processXMLPipeline(environment);
184         }
185
186         if (this.cachedResponse != null && this.completeResponseIsCached) {
187
188             // Allow for 304 (not modified) responses in dynamic content
189
if (checkIfModified(environment, this.cachedResponse.getLastModified())) {
190                 return true;
191             }
192
193             // Set mime-type
194
if (this.cachedResponse.getContentType() != null) {
195                 environment.setContentType(this.cachedResponse.getContentType());
196             } else {
197                 setMimeTypeForSerializer(environment);
198             }
199
200             // Write response out
201
try {
202                 final OutputStream outputStream = environment.getOutputStream(0);
203                 final byte[] content = this.cachedResponse.getResponse();
204                 if (content.length > 0) {
205                     environment.setContentLength(content.length);
206                     outputStream.write(content);
207                 }
208             } catch (Exception JavaDoc e) {
209                 handleException(e);
210             }
211         } else {
212             setMimeTypeForSerializer(environment);
213             if (getLogger().isDebugEnabled() && this.toCacheKey != null) {
214                 getLogger().debug("processXMLPipeline: caching content for further" +
215                                   " requests of '" + environment.getURI() +
216                                   "' using key " + this.toCacheKey);
217             }
218
219             try {
220                 OutputStream os = null;
221
222                 if (this.cacheCompleteResponse && this.toCacheKey != null) {
223                     os = new CachingOutputStream(environment.getOutputStream(this.outputBufferSize));
224                 }
225
226                 if (super.serializer != super.lastConsumer) {
227                     if (os == null) {
228                         os = environment.getOutputStream(this.outputBufferSize);
229                     }
230
231                     // internal processing
232
if (this.xmlDeserializer != null) {
233                         this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
234                     } else {
235                         this.generator.generate();
236                     }
237
238                 } else {
239                     if (this.serializer.shouldSetContentLength()) {
240                         if (os == null) {
241                             os = environment.getOutputStream(0);
242                         }
243
244                         // Set the output stream
245
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
246                         this.serializer.setOutputStream(baos);
247
248                         // Execute the pipeline
249
if (this.xmlDeserializer != null) {
250                             this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
251                         } else {
252                             this.generator.generate();
253                         }
254
255                         environment.setContentLength(baos.size());
256                         baos.writeTo(os);
257                     } else {
258                         if (os == null) {
259                             os = environment.getOutputStream(this.outputBufferSize);
260                         }
261
262                         // Set the output stream
263
this.serializer.setOutputStream(os);
264
265                         // Execute the pipeline
266
if (this.xmlDeserializer != null) {
267                             this.xmlDeserializer.deserialize(this.cachedResponse.getResponse());
268                         } else {
269                             this.generator.generate();
270                         }
271                     }
272                 }
273
274                 //
275
// Now that we have processed the pipeline,
276
// we do the actual caching
277
//
278
cacheResults(environment,os);
279
280             } catch (Exception JavaDoc e) {
281                 handleException(e);
282             }
283
284             return true;
285         }
286
287         return true;
288     }
289
290     /**
291      * The components of the pipeline are checked if they are Cacheable.
292      */

293     protected void generateCachingKey(Environment environment)
294     throws ProcessingException {
295
296         this.toCacheKey = null;
297
298         this.generatorIsCacheableProcessingComponent = false;
299         this.serializerIsCacheableProcessingComponent = false;
300         this.transformerIsCacheableProcessingComponent =
301             new boolean[this.transformers.size()];
302
303         this.firstNotCacheableTransformerIndex = 0;
304         this.cacheCompleteResponse = false;
305
306         // first step is to generate the key:
307
// All pipeline components starting with the generator
308
// are tested if they are either a CacheableProcessingComponent
309
// or Cacheable (deprecated). The returned keys are chained together
310
// to build a unique key of the request
311

312         // is the generator cacheable?
313
Serializable JavaDoc key = getGeneratorKey();
314         if (key != null) {
315             this.toCacheKey = new PipelineCacheKey();
316             this.toCacheKey.addKey(
317                     this.newComponentCacheKey(
318                             ComponentCacheKey.ComponentType_Generator,
319                             this.generatorRole, key));
320
321             // now testing transformers
322
final int transformerSize = super.transformers.size();
323             boolean continueTest = true;
324
325             while (this.firstNotCacheableTransformerIndex < transformerSize && continueTest) {
326                 final Transformer trans =
327                         (Transformer)super.transformers.get(this.firstNotCacheableTransformerIndex);
328                 key = getTransformerKey(trans);
329                 if (key != null) {
330                     this.toCacheKey.addKey(
331                             this.newComponentCacheKey(
332                                     ComponentCacheKey.ComponentType_Transformer,
333                                     (String JavaDoc)this.transformerRoles.get(
334                                             this.firstNotCacheableTransformerIndex),
335                                             key));
336
337                     this.firstNotCacheableTransformerIndex++;
338                 } else {
339                     continueTest = false;
340                 }
341             }
342             // all transformers are cacheable => pipeline is cacheable
343
// test serializer if this is not an internal request
344
if (this.firstNotCacheableTransformerIndex == transformerSize
345                     && super.serializer == this.lastConsumer) {
346
347                 key = getSerializerKey();
348                 if (key != null) {
349                     this.toCacheKey.addKey(
350                             this.newComponentCacheKey(
351                                     ComponentCacheKey.ComponentType_Serializer,
352                                     this.serializerRole,
353                                     key));
354                     this.cacheCompleteResponse = true;
355                 }
356             }
357         }
358     }
359
360     /**
361      * Generate validity objects for the new response
362      */

363     protected void setupValidities() throws ProcessingException {
364
365         if (this.toCacheKey != null) {
366             // only update validity objects if we cannot use
367
// a cached response or when the cached response does
368
// cache less than now is cacheable
369
if (this.fromCacheKey == null
370                 || this.fromCacheKey.size() < this.toCacheKey.size()) {
371
372                 this.toCacheSourceValidities =
373                     new SourceValidity[this.toCacheKey.size()];
374
375                 int len = this.toCacheSourceValidities.length;
376                 int i = 0;
377                 while (i < len) {
378                     final SourceValidity validity = getValidityForInternalPipeline(i);
379
380                     if (validity == null) {
381                         if (i > 0
382                             && (this.fromCacheKey == null
383                                     || i > this.fromCacheKey.size())) {
384                             // shorten key
385
for (int m=i; m < this.toCacheSourceValidities.length; m++) {
386                                 this.toCacheKey.removeLastKey();
387                                 if (!this.cacheCompleteResponse) {
388                                     this.firstNotCacheableTransformerIndex--;
389                                 }
390                                 this.cacheCompleteResponse = false;
391                             }
392                             SourceValidity[] copy = new SourceValidity[i];
393                             System.arraycopy(this.toCacheSourceValidities, 0, copy, 0, copy.length);
394                             this.toCacheSourceValidities = copy;
395                             len = this.toCacheSourceValidities.length;
396                         } else {
397                             // caching is not possible!
398
this.toCacheKey = null;
399                             this.toCacheSourceValidities = null;
400                             this.cacheCompleteResponse = false;
401                             len = 0;
402                         }
403                     } else {
404                         this.toCacheSourceValidities[i] = validity;
405                     }
406                     i++;
407                 }
408             } else {
409                 // we don't have to cache
410
this.toCacheKey = null;
411                 this.cacheCompleteResponse = false;
412             }
413         }
414     }
415
416     /**
417      * Calculate the key that can be used to get something from the cache, and
418      * handle expires properly.
419      */

420     protected void validatePipeline(Environment environment)
421     throws ProcessingException {
422         this.completeResponseIsCached = this.cacheCompleteResponse;
423         this.fromCacheKey = this.toCacheKey.copy();
424         this.firstProcessedTransformerIndex = this.firstNotCacheableTransformerIndex;
425
426         boolean finished = false;
427         while (this.fromCacheKey != null && !finished) {
428             finished = true;
429
430             final CachedResponse response = this.cache.get(this.fromCacheKey);
431
432             // now test validity
433
if (response != null) {
434                 if (getLogger().isDebugEnabled()) {
435                     getLogger().debug("Found cached response for '" + environment.getURI() +
436                                       "' using key: " + this.fromCacheKey);
437                 }
438
439                 boolean responseIsValid = true;
440                 boolean responseIsUsable = true;
441
442                 // See if we have an explicit "expires" setting. If so,
443
// and if it's still fresh, we're done.
444
Long JavaDoc responseExpires = response.getExpires();
445
446                 if (responseExpires != null) {
447                     if (getLogger().isDebugEnabled()) {
448                        getLogger().debug("Expires time found for " + environment.getURI());
449                     }
450
451                     if (responseExpires.longValue() > System.currentTimeMillis()) {
452                         if (getLogger().isDebugEnabled()) {
453                             getLogger().debug("Expires time still fresh for " + environment.getURI() +
454                                               ", ignoring all other cache settings. This entry expires on "+
455                                               new Date JavaDoc(responseExpires.longValue()));
456                         }
457                         this.cachedResponse = response;
458                         return;
459                     } else {
460                         if (getLogger().isDebugEnabled()) {
461                             getLogger().debug("Expires time has expired for " + environment.getURI() +
462                                               ", regenerating content.");
463                         }
464
465                         // If an expires parameter was provided, use it. If this parameter is not available
466
// it means that the sitemap was modified, and the old expires value is not valid
467
// anymore.
468
if (expires != 0) {
469                             if (this.getLogger().isDebugEnabled())
470                                 this.getLogger().debug("Refreshing expires informations");
471                             response.setExpires(new Long JavaDoc(expires + System.currentTimeMillis()));
472                         } else {
473                             if (this.getLogger().isDebugEnabled())
474                                 this.getLogger().debug("No expires defined anymore for this object, setting it to no expires");
475                             response.setExpires(null);
476                         }
477                     }
478                 } else {
479                     // The response had no expires informations. See if it needs to be set (i.e. because the configuration has changed)
480
if (expires != 0) {
481                         if (this.getLogger().isDebugEnabled())
482                             this.getLogger().debug("Setting a new expires object for this resource");
483                         response.setExpires(new Long JavaDoc(expires + System.currentTimeMillis()));
484                     }
485                 }
486
487                 SourceValidity[] fromCacheValidityObjects = response.getValidityObjects();
488
489                 int i = 0;
490                 while (responseIsValid && i < fromCacheValidityObjects.length) {
491                     boolean isValid = false;
492
493                     // BH Check if validities[i] is null, may happen
494
// if exception was thrown due to malformed content
495
SourceValidity validity = fromCacheValidityObjects[i];
496                     int valid = validity == null ? SourceValidity.INVALID : validity.isValid();
497                     if (valid == SourceValidity.UNKNOWN) {
498                         // Don't know if valid, make second test
499
validity = getValidityForInternalPipeline(i);
500                         if (validity != null) {
501                             valid = fromCacheValidityObjects[i].isValid(validity);
502                             if (valid == SourceValidity.UNKNOWN) {
503                                 validity = null;
504                             } else {
505                                 isValid = (valid == SourceValidity.VALID);
506                             }
507                         }
508                     } else {
509                         isValid = (valid == SourceValidity.VALID);
510                     }
511
512                     if (!isValid) {
513                         responseIsValid = false;
514                         // update validity
515
if (validity == null) {
516                             responseIsUsable = false;
517                             if (getLogger().isDebugEnabled()) {
518                                 getLogger().debug("validatePipeline: responseIsUsable is false, valid=" +
519                                                   valid + " at index " + i);
520                             }
521                         } else {
522                             if (getLogger().isDebugEnabled()) {
523                                 getLogger().debug("validatePipeline: responseIsValid is false due to " +
524                                                   validity);
525                             }
526                         }
527                     } else {
528                         i++;
529                     }
530                 }
531
532                 if (responseIsValid) {
533                     if (getLogger().isDebugEnabled()) {
534                         getLogger().debug("validatePipeline: using valid cached content for '" +
535                                           environment.getURI() + "'.");
536                     }
537
538                     // we are valid, ok that's it
539
this.cachedResponse = response;
540                     this.toCacheSourceValidities = fromCacheValidityObjects;
541                 } else {
542                     if (getLogger().isDebugEnabled()) {
543                         getLogger().debug("validatePipeline: cached content is invalid for '" +
544                                           environment.getURI() + "'.");
545                     }
546                     // we are not valid!
547

548                     if (!responseIsUsable) {
549                         // we could not compare, because we got no
550
// validity object, so shorten pipeline key
551
if (i > 0) {
552                             int deleteCount = fromCacheValidityObjects.length - i;
553                             if (i > 0 && i <= firstNotCacheableTransformerIndex + 1) {
554                                 this.firstNotCacheableTransformerIndex = i-1;
555                             }
556                             for(int x=0; x < deleteCount; x++) {
557                                 this.toCacheKey.removeLastKey();
558                             }
559                             finished = false;
560                         } else {
561                             this.toCacheKey = null;
562                         }
563                         this.cacheCompleteResponse = false;
564                     } else {
565                         // the entry is invalid, remove it
566
this.cache.remove(this.fromCacheKey);
567                     }
568
569                     // try a shorter key
570
if (i > 0) {
571                         this.fromCacheKey.removeLastKey();
572                         if (!this.completeResponseIsCached) {
573                             this.firstProcessedTransformerIndex--;
574                         }
575                     } else {
576                         this.fromCacheKey = null;
577                     }
578                     finished = false;
579                     this.completeResponseIsCached = false;
580                 }
581             } else {
582
583                 // no cached response found
584
if (this.getLogger().isDebugEnabled()) {
585                     this.getLogger().debug(
586                         "Cached response not found for '" + environment.getURI() +
587                         "' using key: " + this.fromCacheKey
588                     );
589                 }
590
591                 if (!this.doSmartCaching) {
592                     // try a shorter key
593
if (this.fromCacheKey.size() > 1) {
594                         this.fromCacheKey.removeLastKey();
595                         if (!this.completeResponseIsCached) {
596                             this.firstProcessedTransformerIndex--;
597                         }
598                         finished = false;
599                     } else {
600                         this.fromCacheKey = null;
601                     }
602                 } else {
603                     // stop on longest key for smart caching
604
this.fromCacheKey = null;
605                 }
606                 this.completeResponseIsCached = false;
607             }
608         }
609
610     }
611
612     /**
613      * Setup the evenet pipeline.
614      * The components of the pipeline are checked if they are
615      * Cacheable.
616      */

617     protected void setupPipeline(Environment environment)
618     throws ProcessingException {
619         super.setupPipeline(environment);
620
621         // Generate the key to fill the cache
622
generateCachingKey(environment);
623
624         // Test the cache for a valid response
625
if (this.toCacheKey != null) {
626             validatePipeline(environment);
627         }
628
629         setupValidities();
630     }
631
632     /**
633      * Connect the pipeline.
634      */

635     protected void connectPipeline(Environment environment)
636     throws ProcessingException {
637         if (this.toCacheKey == null && this.cachedResponse == null) {
638             super.connectPipeline(environment);
639             return;
640         } else if (this.completeResponseIsCached) {
641             // do nothing
642
return;
643         } else {
644             this.connectCachingPipeline(environment);
645         }
646     }
647
648     /** Process the pipeline using a reader.
649      * @throws ProcessingException if an error occurs
650      */

651     protected boolean processReader(Environment environment)
652     throws ProcessingException {
653         try {
654             boolean usedCache = false;
655             OutputStream outputStream = null;
656             SourceValidity readerValidity = null;
657             PipelineCacheKey pcKey = null;
658
659             // test if reader is cacheable
660
Serializable JavaDoc readerKey = null;
661             boolean isCacheableProcessingComponent = false;
662             if (super.reader instanceof CacheableProcessingComponent) {
663                 readerKey = ((CacheableProcessingComponent)super.reader).getKey();
664                 isCacheableProcessingComponent = true;
665             } else if (super.reader instanceof Cacheable) {
666                 readerKey = new Long JavaDoc(((Cacheable)super.reader).generateKey());
667             }
668
669             if (readerKey != null) {
670                 // response is cacheable, build the key
671
pcKey = new PipelineCacheKey();
672                 pcKey.addKey(new ComponentCacheKey(ComponentCacheKey.ComponentType_Reader,
673                                                    this.readerRole,
674                                                    readerKey)
675                             );
676
677                 // now we have the key to get the cached object
678
CachedResponse cachedObject = this.cache.get(pcKey);
679                 if (cachedObject != null) {
680                     if (getLogger().isDebugEnabled()) {
681                         getLogger().debug("Found cached response for '" +
682                                           environment.getURI() + "' using key: " + pcKey);
683                     }
684
685                     SourceValidity[] validities = cachedObject.getValidityObjects();
686                     if (validities == null || validities.length != 1) {
687                         // to avoid getting here again and again, we delete it
688
this.cache.remove(pcKey);
689                         if (getLogger().isDebugEnabled()) {
690                             getLogger().debug("Cached response for '" + environment.getURI() +
691                                               "' using key: " + pcKey + " is invalid.");
692                         }
693                         this.cachedResponse = null;
694                     } else {
695                         SourceValidity cachedValidity = validities[0];
696                         boolean isValid = false;
697                         int valid = cachedValidity.isValid();
698                         if (valid == SourceValidity.UNKNOWN) {
699                             // get reader validity and compare
700
if (isCacheableProcessingComponent) {
701                                 readerValidity = ((CacheableProcessingComponent) super.reader).getValidity();
702                             } else {
703                                 CacheValidity cv = ((Cacheable) super.reader).generateValidity();
704                                 if (cv != null) {
705                                     readerValidity = CacheValidityToSourceValidity.createValidity(cv);
706                                 }
707                             }
708                             if (readerValidity != null) {
709                                 valid = cachedValidity.isValid(readerValidity);
710                                 if (valid == SourceValidity.UNKNOWN) {
711                                     readerValidity = null;
712                                 } else {
713                                     isValid = (valid == SourceValidity.VALID);
714                                 }
715                             }
716                         } else {
717                             isValid = (valid == SourceValidity.VALID);
718                         }
719
720                         if (isValid) {
721                             if (getLogger().isDebugEnabled()) {
722                                 getLogger().debug("processReader: using valid cached content for '" +
723                                                   environment.getURI() + "'.");
724                             }
725                             byte[] response = cachedObject.getResponse();
726                             if (response.length > 0) {
727                                 usedCache = true;
728                                 if (cachedObject.getContentType() != null) {
729                                     environment.setContentType(cachedObject.getContentType());
730                                 } else {
731                                     setMimeTypeForReader(environment);
732                                 }
733                                 outputStream = environment.getOutputStream(0);
734                                 environment.setContentLength(response.length);
735                                 outputStream.write(response);
736                             }
737                         } else {
738                             if (getLogger().isDebugEnabled()) {
739                                 getLogger().debug("processReader: cached content is invalid for '" +
740                                                   environment.getURI() + "'.");
741                             }
742                             // remove invalid cached object
743
this.cache.remove(pcKey);
744                         }
745                     }
746                 }
747             }
748
749             if (!usedCache) {
750                 if (pcKey != null) {
751                     if (getLogger().isDebugEnabled()) {
752                         getLogger().debug("processReader: caching content for further requests of '" +
753                                           environment.getURI() + "'.");
754                     }
755
756                     if (readerValidity == null) {
757                         if (isCacheableProcessingComponent) {
758                             readerValidity = ((CacheableProcessingComponent)super.reader).getValidity();
759                         } else {
760                             CacheValidity cv = ((Cacheable)super.reader).generateValidity();
761                             if ( cv != null ) {
762                                 readerValidity = CacheValidityToSourceValidity.createValidity( cv );
763                             }
764                         }
765                     }
766
767                     if (readerValidity != null) {
768                         outputStream = environment.getOutputStream(this.outputBufferSize);
769                         outputStream = new CachingOutputStream(outputStream);
770                     } else {
771                         pcKey = null;
772                     }
773                 }
774
775                 setMimeTypeForReader(environment);
776                 if (this.reader.shouldSetContentLength()) {
777                     ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
778                     this.reader.setOutputStream(os);
779                     this.reader.generate();
780                     environment.setContentLength(os.size());
781                     if (outputStream == null) {
782                         outputStream = environment.getOutputStream(0);
783                     }
784                     os.writeTo(outputStream);
785                 } else {
786                     if (outputStream == null) {
787                         outputStream = environment.getOutputStream(this.outputBufferSize);
788                     }
789                     this.reader.setOutputStream(outputStream);
790                     this.reader.generate();
791                 }
792
793                 // store the response
794
if (pcKey != null) {
795                     final CachedResponse res = new CachedResponse(new SourceValidity[] {readerValidity},
796                             ((CachingOutputStream)outputStream).getContent());
797                     res.setContentType(environment.getContentType());
798                     this.cache.store(pcKey, res);
799                 }
800             }
801         } catch (Exception JavaDoc e) {
802             handleException(e);
803         }
804
805         return true;
806     }
807
808
809     /**
810      * Return valid validity objects for the event pipeline.
811      *
812      * If the event pipeline (the complete pipeline without the
813      * serializer) is cacheable and valid, return all validity objects.
814      *
815      * Otherwise, return <code>null</code>.
816      */

817     public SourceValidity getValidityForEventPipeline() {
818         if (isInternalError()) {
819             return null;
820         }
821
822         if (this.cachedResponse != null) {
823             if (!this.cacheCompleteResponse &&
824                     this.firstNotCacheableTransformerIndex < super.transformers.size()) {
825                 // Cache contains only partial pipeline.
826
return null;
827             }
828
829             if (this.toCacheSourceValidities != null) {
830                 // This means that the pipeline is valid based on the validities
831
// of the individual components
832
final AggregatedValidity validity = new AggregatedValidity();
833                 for (int i=0; i < this.toCacheSourceValidities.length; i++) {
834                     validity.add(this.toCacheSourceValidities[i]);
835                 }
836
837                 return validity;
838             }
839
840             // This means that the pipeline is valid because it has not yet expired
841
return NOPValidity.SHARED_INSTANCE;
842         } else {
843             int vals = 0;
844
845             if (null != this.toCacheKey
846                     && !this.cacheCompleteResponse
847                     && this.firstNotCacheableTransformerIndex == super.transformers.size()) {
848                 vals = this.toCacheKey.size();
849             } else if (null != this.fromCacheKey
850                     && !this.completeResponseIsCached
851                     && this.firstProcessedTransformerIndex == super.transformers.size()) {
852                 vals = this.fromCacheKey.size();
853             }
854
855             if (vals > 0) {
856                 final AggregatedValidity validity = new AggregatedValidity();
857                 for (int i = 0; i < vals; i++) {
858                     validity.add(getValidityForInternalPipeline(i));
859                 }
860
861                 return validity;
862             }
863
864             return null;
865         }
866     }
867
868     /**
869      * Get generator cache key (null if not cacheable)
870      */

871     private Serializable JavaDoc getGeneratorKey() {
872         Serializable JavaDoc key = null;
873         if (super.generator instanceof CacheableProcessingComponent) {
874             key = ((CacheableProcessingComponent)super.generator).getKey();
875             this.generatorIsCacheableProcessingComponent = true;
876         } else if (super.generator instanceof Cacheable) {
877             key = new Long JavaDoc(((Cacheable)super.generator).generateKey());
878         }
879         return key;
880     }
881
882     /**
883      * Get transformer cache key (null if not cacheable)
884      */

885     private Serializable JavaDoc getTransformerKey(final Transformer transformer) {
886         Serializable JavaDoc key = null;
887         if (transformer instanceof CacheableProcessingComponent) {
888             key = ((CacheableProcessingComponent)transformer).getKey();
889             this.transformerIsCacheableProcessingComponent[this.firstNotCacheableTransformerIndex] = true;
890         } else if (transformer instanceof Cacheable) {
891             key = new Long JavaDoc(((Cacheable)transformer).generateKey());
892         }
893         return key;
894     }
895
896     /**
897      * Get serializer cache key (null if not cacheable)
898      */

899     private Serializable JavaDoc getSerializerKey() {
900         Serializable JavaDoc key = null;
901         if (super.serializer instanceof CacheableProcessingComponent) {
902             key = ((CacheableProcessingComponent)this.serializer).getKey();
903             this.serializerIsCacheableProcessingComponent = true;
904         } else if (this.serializer instanceof Cacheable) {
905             key = new Long JavaDoc(((Cacheable)this.serializer).generateKey());
906         }
907         return key;
908     }
909
910     /* (non-Javadoc)
911      * @see org.apache.cocoon.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
912      */

913     public String JavaDoc getKeyForEventPipeline() {
914         if (isInternalError()) {
915             return null;
916         }
917
918         if (null != this.toCacheKey
919              && !this.cacheCompleteResponse
920              && this.firstNotCacheableTransformerIndex == super.transformers.size()) {
921              return String.valueOf(HashUtil.hash(this.toCacheKey.toString()));
922         }
923         if (null != this.fromCacheKey
924              && !this.completeResponseIsCached
925              && this.firstProcessedTransformerIndex == super.transformers.size()) {
926             return String.valueOf(HashUtil.hash(this.fromCacheKey.toString()));
927         }
928
929         return null;
930     }
931
932     SourceValidity getValidityForInternalPipeline(int index) {
933         final SourceValidity validity;
934
935         // if debugging try to tell why something is not cacheable
936
final boolean debug = this.getLogger().isDebugEnabled();
937         String JavaDoc msg = null;
938         if(debug) msg = "getValidityForInternalPipeline(" + index + "): ";
939
940         if (index == 0) {
941             // test generator
942
if (this.generatorIsCacheableProcessingComponent) {
943                 validity = ((CacheableProcessingComponent)super.generator).getValidity();
944                 if(debug) msg += "generator: using getValidity";
945             } else {
946                 validity = CacheValidityToSourceValidity.createValidity(((Cacheable)super.generator).generateValidity());
947                 if(debug) msg += "generator: using generateValidity";
948             }
949         } else if (index <= firstNotCacheableTransformerIndex) {
950             // test transformer
951
final Transformer trans = (Transformer)super.transformers.get(index-1);
952             if (this.transformerIsCacheableProcessingComponent[index-1]) {
953                 validity = ((CacheableProcessingComponent)trans).getValidity();
954                 if(debug) msg += "transformer: using getValidity";
955             } else {
956                 validity = CacheValidityToSourceValidity.createValidity(((Cacheable)trans).generateValidity());
957                 if(debug) msg += "transformer: using generateValidity";
958             }
959         } else {
960             // test serializer
961
if (this.serializerIsCacheableProcessingComponent) {
962                 validity = ((CacheableProcessingComponent)super.serializer).getValidity();
963                 if(debug) msg += "serializer: using getValidity";
964             } else {
965                 validity = CacheValidityToSourceValidity.createValidity(((Cacheable)super.serializer).generateValidity());
966                 if(debug) msg += "serializer: using generateValidity";
967             }
968         }
969
970         if(debug) {
971             msg += ", validity==" + validity;
972             this.getLogger().debug(msg);
973         }
974         return validity;
975     }
976
977     /**
978      * Recyclable Interface
979      */

980     public void recycle() {
981         this.generatorRole = null;
982         this.transformerRoles.clear();
983         this.serializerRole = null;
984         this.readerRole = null;
985
986         this.fromCacheKey = null;
987         this.cachedResponse = null;
988
989         this.transformerIsCacheableProcessingComponent = null;
990         this.toCacheKey = null;
991         this.toCacheSourceValidities = null;
992
993         super.recycle();
994     }
995 }
996
997 final class DeferredPipelineValidity implements DeferredValidity {
998
999     private final AbstractCachingProcessingPipeline pipeline;
1000    private final int index;
1001
1002    public DeferredPipelineValidity(AbstractCachingProcessingPipeline pipeline, int index) {
1003        this.pipeline = pipeline;
1004        this.index = index;
1005    }
1006
1007    /**
1008     * @see org.apache.excalibur.source.impl.validity.DeferredValidity#getValidity()
1009     */

1010    public SourceValidity getValidity() {
1011        return pipeline.getValidityForInternalPipeline(this.index);
1012    }
1013}
1014
Popular Tags