KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > pipeline > AbstractProcessingPipeline


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;
17
18 import java.io.ByteArrayOutputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.net.SocketException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.NoSuchElementException JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25
26 import org.apache.avalon.excalibur.pool.Recyclable;
27 import org.apache.avalon.framework.component.Component;
28 import org.apache.avalon.framework.component.ComponentException;
29 import org.apache.avalon.framework.component.ComponentManager;
30 import org.apache.avalon.framework.component.ComponentSelector;
31 import org.apache.avalon.framework.logger.AbstractLogEnabled;
32 import org.apache.avalon.framework.parameters.ParameterException;
33 import org.apache.avalon.framework.parameters.Parameterizable;
34 import org.apache.avalon.framework.parameters.Parameters;
35 import org.apache.cocoon.ConnectionResetException;
36 import org.apache.cocoon.ProcessingException;
37 import org.apache.cocoon.components.CocoonComponentManager;
38 import org.apache.cocoon.environment.Environment;
39 import org.apache.cocoon.environment.ObjectModelHelper;
40 import org.apache.cocoon.environment.Response;
41 import org.apache.cocoon.generation.Generator;
42 import org.apache.cocoon.reading.Reader;
43 import org.apache.cocoon.serialization.Serializer;
44 import org.apache.cocoon.sitemap.SitemapErrorHandler;
45 import org.apache.cocoon.sitemap.SitemapModelComponent;
46 import org.apache.cocoon.transformation.Transformer;
47 import org.apache.cocoon.util.location.Locatable;
48 import org.apache.cocoon.util.location.Location;
49 import org.apache.cocoon.xml.SaxBuffer;
50 import org.apache.cocoon.xml.XMLConsumer;
51 import org.apache.cocoon.xml.XMLProducer;
52 import org.apache.excalibur.source.SourceValidity;
53 import org.xml.sax.SAXException JavaDoc;
54
55 /**
56  * This is the base for all implementations of a <code>ProcessingPipeline</code>.
57  *
58  * @since 2.1
59  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
60  * @version CVS $Id: AbstractProcessingPipeline.java 280628 2005-09-13 19:14:35Z sylvain $
61  */

62 public abstract class AbstractProcessingPipeline
63         extends AbstractLogEnabled
64         implements ProcessingPipeline, Parameterizable, Recyclable {
65
66     // Generator stuff
67
protected Generator generator;
68     protected Parameters generatorParam;
69     protected String JavaDoc generatorSource;
70     protected ComponentSelector generatorSelector;
71
72     // Transformer stuff
73
protected ArrayList JavaDoc transformers = new ArrayList JavaDoc();
74     protected ArrayList JavaDoc transformerParams = new ArrayList JavaDoc();
75     protected ArrayList JavaDoc transformerSources = new ArrayList JavaDoc();
76     protected ArrayList JavaDoc transformerSelectors = new ArrayList JavaDoc();
77
78     // Serializer stuff
79
protected Serializer serializer;
80     protected Parameters serializerParam;
81     protected String JavaDoc serializerSource;
82     protected String JavaDoc serializerMimeType;
83     protected String JavaDoc sitemapSerializerMimeType;
84     protected OutputComponentSelector serializerSelector;
85
86     // Reader stuff
87
protected Reader reader;
88     protected Parameters readerParam;
89     protected String JavaDoc readerSource;
90     protected String JavaDoc readerMimeType;
91     protected String JavaDoc sitemapReaderMimeType;
92     protected OutputComponentSelector readerSelector;
93
94     // Error handler stuff
95
private SitemapErrorHandler errorHandler;
96     private ProcessingPipeline errorPipeline;
97
98     /** True when pipeline has been prepared. */
99     private boolean prepared;
100
101     /**
102      * This is the last component in the pipeline, either the serializer
103      * or a custom XML consumer in case of internal processing.
104      */

105     protected XMLConsumer lastConsumer;
106
107     /** The component manager set with compose() */
108     protected ComponentManager manager;
109
110     /** The component manager set with compose() and recompose() */
111     protected ComponentManager newManager;
112
113     /** The configuration */
114     protected Parameters configuration;
115
116     /** Configured Expires value */
117     protected long configuredExpires;
118
119     /** Configured Output Buffer Size */
120     protected int configuredOutputBufferSize;
121
122     /** The parameters */
123     protected Parameters parameters;
124
125     /** Expires value */
126     protected long expires;
127
128     /** Output Buffer Size */
129     protected int outputBufferSize;
130
131     /**
132      * Composable Interface
133      */

134     public void compose(ComponentManager manager)
135     throws ComponentException {
136         this.manager = manager;
137         this.newManager = manager;
138     }
139
140     /**
141      * Recomposable Interface
142      */

143     public void recompose(ComponentManager manager)
144     throws ComponentException {
145         this.newManager = manager;
146     }
147
148     /**
149      * Parameterizable Interface - Configuration
150      */

151     public void parameterize(Parameters params)
152     throws ParameterException {
153         this.configuration = params;
154         final String JavaDoc expiresValue = params.getParameter("expires", null);
155         if (expiresValue != null) {
156             this.configuredExpires = parseExpires(expiresValue);
157         }
158         this.configuredOutputBufferSize = params.getParameterAsInteger("outputBufferSize", -1);
159     }
160
161     /**
162      * Setup this component
163      */

164     public void setup(Parameters params) {
165         this.parameters = params;
166         final String JavaDoc expiresValue = params.getParameter("expires", null);
167         if (expiresValue != null) {
168             this.expires = parseExpires(expiresValue);
169         } else {
170             this.expires = this.configuredExpires;
171         }
172         this.outputBufferSize = params.getParameterAsInteger("outputBufferSize",
173                                                               this.configuredOutputBufferSize);
174     }
175
176     /**
177      * Release this component.
178      * If you get an instance not by a component manager but for example
179      * by a processor, you have to release this component by calling
180      * this method and NOT by using a component manager!
181      */

182     public void release() {
183         try {
184             CocoonComponentManager.removeFromAutomaticRelease(this);
185         } catch (ProcessingException e) {
186             // ignore this
187
getLogger().error("Unable to release self from automatic release.", e);
188         }
189     }
190
191     /**
192      * Informs pipeline we have come across a branch point.
193      * Default behaviour is do nothing.
194      */

195     public void informBranchPoint() {
196         // this can be overwritten in subclasses
197
}
198
199     /**
200      * Get the generator - used for content aggregation
201      */

202     public Generator getGenerator() {
203         return this.generator;
204     }
205
206     /**
207      * Set the generator that will be used as the initial step in the pipeline.
208      * The generator role is given : the actual <code>Generator</code> is fetched
209      * from the latest <code>ComponentManager</code> given by <code>compose()</code>
210      * or <code>recompose()</code>.
211      *
212      * @param role the generator role in the component manager.
213      * @param source the source where to produce XML from, or <code>null</code> if no
214      * source is given.
215      * @param param the parameters for the generator.
216      * @throws ProcessingException if the generator couldn't be obtained.
217      */

218     public void setGenerator(String JavaDoc role, String JavaDoc source, Parameters param, Parameters hintParam)
219     throws ProcessingException {
220         if (this.generator != null) {
221             throw new ProcessingException ("Generator already set. Cannot set generator '" + role + "'",
222                     getLocation(param));
223         }
224         if (this.reader != null) {
225             throw new ProcessingException ("Reader already set. Cannot set generator '" + role + "'",
226                     getLocation(param));
227         }
228         try {
229             this.generatorSelector = (ComponentSelector) this.newManager.lookup(Generator.ROLE + "Selector");
230         } catch (ComponentException ce) {
231             throw ProcessingException.throwLocated("Lookup of generator selector failed", ce, getLocation(param));
232         }
233         try {
234             this.generator = (Generator) this.generatorSelector.select(role);
235         } catch (ComponentException ce) {
236             throw ProcessingException.throwLocated("Lookup of generator '" + role + "' failed", ce, getLocation(param));
237         }
238         this.generatorSource = source;
239         this.generatorParam = param;
240     }
241
242     /**
243      * Add a transformer at the end of the pipeline.
244      * The transformer role is given : the actual <code>Transformer</code> is fetched
245      * from the latest <code>ComponentManager</code> given by <code>compose()</code>
246      * or <code>recompose()</code>.
247      *
248      * @param role the transformer role in the component manager.
249      * @param source the source used to setup the transformer (e.g. XSL file), or
250      * <code>null</code> if no source is given.
251      * @param param the parameters for the transfomer.
252      * @throws ProcessingException if the generator couldn't be obtained.
253      */

254     public void addTransformer(String JavaDoc role, String JavaDoc source, Parameters param, Parameters hintParam)
255     throws ProcessingException {
256         if (this.reader != null) {
257             // Should normally never happen as setting a reader starts pipeline processing
258
throw new ProcessingException ("Reader already set. Cannot add transformer '" + role + "'",
259                     getLocation(param));
260         }
261         if (this.generator == null) {
262             throw new ProcessingException ("Must set a generator before adding transformer '" + role + "'",
263                     getLocation(param));
264         }
265         ComponentSelector selector = null;
266         try {
267             selector = (ComponentSelector) this.newManager.lookup(Transformer.ROLE + "Selector");
268         } catch (ComponentException ce) {
269             throw ProcessingException.throwLocated("Lookup of transformer selector failed", ce, getLocation(param));
270         }
271         try {
272             this.transformers.add(selector.select(role));
273             this.transformerSelectors.add(selector);
274         } catch (ComponentException ce) {
275             throw ProcessingException.throwLocated("Lookup of transformer '"+role+"' failed", ce, getLocation(param));
276         }
277         this.transformerSources.add(source);
278         this.transformerParams.add(param);
279     }
280
281     /**
282      * Set the serializer for this pipeline
283      * @param mimeType Can be null
284      */

285     public void setSerializer(String JavaDoc role, String JavaDoc source, Parameters param, Parameters hintParam, String JavaDoc mimeType)
286     throws ProcessingException {
287         if (this.serializer != null) {
288             // Should normally not happen as adding a serializer starts pipeline processing
289
throw new ProcessingException ("Serializer already set. Cannot set serializer '" + role + "'",
290                     getLocation(param));
291         }
292         if (this.reader != null) {
293             // Should normally never happen as setting a reader starts pipeline processing
294
throw new ProcessingException ("Reader already set. Cannot set serializer '" + role + "'",
295                     getLocation(param));
296         }
297         if (this.generator == null) {
298             throw new ProcessingException ("Must set a generator before setting serializer '" + role + "'",
299                     getLocation(param));
300         }
301
302         try {
303             this.serializerSelector = (OutputComponentSelector) this.newManager.lookup(Serializer.ROLE + "Selector");
304         } catch (ComponentException ce) {
305             throw ProcessingException.throwLocated("Lookup of serializer selector failed", ce, getLocation(param));
306         }
307         try {
308             this.serializer = (Serializer)serializerSelector.select(role);
309         } catch (ComponentException ce) {
310             throw ProcessingException.throwLocated("Lookup of serializer '" + role + "' failed", ce, getLocation(param));
311         }
312         this.serializerSource = source;
313         this.serializerParam = param;
314         this.serializerMimeType = mimeType;
315         this.sitemapSerializerMimeType = serializerSelector.getMimeTypeForHint(role);
316         this.lastConsumer = this.serializer;
317     }
318
319     /**
320      * Set the reader for this pipeline
321      * @param mimeType Can be null
322      */

323     public void setReader(String JavaDoc role, String JavaDoc source, Parameters param, String JavaDoc mimeType)
324     throws ProcessingException {
325         if (this.reader != null) {
326             // Should normally never happen as setting a reader starts pipeline processing
327
throw new ProcessingException ("Reader already set. Cannot set reader '" + role + "'",
328                     getLocation(param));
329         }
330         if (this.generator != null) {
331             // Should normally never happen as setting a reader starts pipeline processing
332
throw new ProcessingException ("Generator already set. Cannot use reader '" + role + "'",
333                     getLocation(param));
334         }
335
336         try {
337             this.readerSelector = (OutputComponentSelector) this.newManager.lookup(Reader.ROLE + "Selector");
338         } catch (ComponentException ce) {
339             throw ProcessingException.throwLocated("Lookup of reader selector failed", ce, getLocation(param));
340         }
341         try {
342             this.reader = (Reader)readerSelector.select(role);
343         } catch (ComponentException ce) {
344             throw ProcessingException.throwLocated("Lookup of reader '"+role+"' failed", ce, getLocation(param));
345         }
346         this.readerSource = source;
347         this.readerParam = param;
348         this.readerMimeType = mimeType;
349         this.sitemapReaderMimeType = readerSelector.getMimeTypeForHint(role);
350     }
351
352     /**
353      * Sets error handler for this pipeline.
354      * Used for handling errors in the internal pipelines.
355      * @param errorHandler error handler
356      */

357     public void setErrorHandler(SitemapErrorHandler errorHandler) {
358         this.errorHandler = errorHandler;
359     }
360
361     /**
362      * Sanity check
363      * @return true if the pipeline is 'sane', false otherwise.
364      */

365     protected boolean checkPipeline() {
366         if (this.generator == null && this.reader == null) {
367             return false;
368         }
369
370         if (this.generator != null && this.serializer == null) {
371             return false;
372         }
373
374         return true;
375     }
376
377     /**
378      * Setup pipeline components.
379      */

380     protected void setupPipeline(Environment environment)
381     throws ProcessingException {
382         try {
383             // setup the generator
384
this.generator.setup(
385                 environment,
386                 environment.getObjectModel(),
387                 generatorSource,
388                 generatorParam
389             );
390
391             Iterator JavaDoc transformerItt = this.transformers.iterator();
392             Iterator JavaDoc transformerSourceItt = this.transformerSources.iterator();
393             Iterator JavaDoc transformerParamItt = this.transformerParams.iterator();
394
395             while (transformerItt.hasNext()) {
396                 Transformer trans = (Transformer)transformerItt.next();
397                 trans.setup(environment,
398                             environment.getObjectModel(),
399                             (String JavaDoc)transformerSourceItt.next(),
400                             (Parameters)transformerParamItt.next()
401                 );
402             }
403
404             if (this.serializer instanceof SitemapModelComponent) {
405                 ((SitemapModelComponent)this.serializer).setup(
406                     environment,
407                     environment.getObjectModel(),
408                     this.serializerSource,
409                     this.serializerParam
410                 );
411             }
412         } catch (Exception JavaDoc e) {
413             handleException(e);
414         }
415     }
416
417     /**
418      * Connect the next component
419      */

420     protected void connect(Environment environment,
421                            XMLProducer producer,
422                            XMLConsumer consumer)
423     throws ProcessingException {
424         // Connect next component.
425
producer.setConsumer(consumer);
426     }
427
428     /**
429      * Connect the XML pipeline.
430      */

431     protected void connectPipeline(Environment environment)
432     throws ProcessingException {
433         XMLProducer prev = this.generator;
434
435         Iterator JavaDoc itt = this.transformers.iterator();
436         while (itt.hasNext()) {
437             Transformer next = (Transformer) itt.next();
438             connect(environment, prev, next);
439             prev = next;
440         }
441
442         // insert the serializer
443
connect(environment, prev, this.lastConsumer);
444     }
445
446     /**
447      * Process the given <code>Environment</code>, producing the output.
448      */

449     public boolean process(Environment environment)
450     throws ProcessingException {
451         if (!this.prepared) {
452             preparePipeline(environment);
453         }
454
455         // See if we need to set an "Expires:" header
456
if (this.expires != 0) {
457             Response res = ObjectModelHelper.getResponse(environment.getObjectModel());
458             res.setDateHeader("Expires", System.currentTimeMillis() + expires);
459             res.setHeader("Cache-Control", "max-age=" + expires/1000 + ", public");
460             if (getLogger().isDebugEnabled()) {
461                 getLogger().debug("Setting a new Expires object for this resource");
462             }
463             environment.getObjectModel().put(ObjectModelHelper.EXPIRES_OBJECT,
464                                              new Long JavaDoc(expires + System.currentTimeMillis()));
465         }
466
467         if (this.reader != null) {
468             if (checkIfModified(environment, this.reader.getLastModified())) {
469                 return true;
470             }
471
472             return processReader(environment);
473         } else {
474             // If this is an internal request, lastConsumer was reset!
475
if (this.lastConsumer == null) {
476                 this.lastConsumer = this.serializer;
477             }
478
479             connectPipeline(environment);
480             return processXMLPipeline(environment);
481         }
482     }
483
484     /**
485      * Prepare the pipeline
486      */

487     protected void preparePipeline(Environment environment)
488     throws ProcessingException {
489         if (!checkPipeline()) {
490             throw new ProcessingException("Attempted to process incomplete pipeline.");
491         }
492
493         if (this.prepared) {
494             throw new ProcessingException("Duplicate preparePipeline call caught.");
495         }
496
497         if (this.reader != null) {
498             setupReader(environment);
499         } else {
500             setupPipeline(environment);
501         }
502         this.prepared = true;
503     }
504
505     /**
506      * Prepare an internal processing.
507      * @param environment The current environment.
508      * @throws ProcessingException
509      */

510     public void prepareInternal(Environment environment)
511     throws ProcessingException {
512         this.lastConsumer = null;
513         try {
514             preparePipeline(environment);
515         } catch (ProcessingException e) {
516             prepareInternalErrorHandler(environment, e);
517         }
518     }
519
520     /**
521      * If prepareInternal fails, prepare internal error handler.
522      */

523     protected void prepareInternalErrorHandler(Environment environment, ProcessingException ex)
524     throws ProcessingException {
525         if (this.errorHandler != null) {
526             try {
527                 this.errorPipeline = this.errorHandler.prepareErrorPipeline(ex);
528                 if (this.errorPipeline != null) {
529                     this.errorPipeline.prepareInternal(environment);
530                     return;
531                 }
532             } catch (ProcessingException e) {
533                 // Log the original exception
534
getLogger().error("Failed to process error handler for exception", ex);
535                 throw e;
536             } catch (Exception JavaDoc e) {
537                 getLogger().error("Failed to process error handler for exception", ex);
538                 throw new ProcessingException("Failed to handle exception <" + ex.getMessage() + ">", e);
539             }
540         } else {
541             // propagate exception if we have no error handler
542
throw ex;
543         }
544     }
545
546     /**
547      * @return true if error happened during internal pipeline prepare call.
548      */

549     protected boolean isInternalError() {
550         return this.errorPipeline != null;
551     }
552
553     /**
554      * Process the SAX event pipeline
555      */

556     protected boolean processXMLPipeline(Environment environment)
557     throws ProcessingException {
558
559         setMimeTypeForSerializer(environment);
560         try {
561             if (this.lastConsumer == null) {
562                 // internal processing
563
this.generator.generate();
564             } else {
565                 if (this.serializer.shouldSetContentLength()) {
566                     // set the output stream
567
ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
568                     this.serializer.setOutputStream(os);
569
570                     // execute the pipeline:
571
this.generator.generate();
572                     environment.setContentLength(os.size());
573                     os.writeTo(environment.getOutputStream(0));
574                 } else {
575                     // set the output stream
576
this.serializer.setOutputStream(environment.getOutputStream(this.outputBufferSize));
577                     // execute the pipeline:
578
this.generator.generate();
579                 }
580             }
581         } catch (Exception JavaDoc e) {
582             handleException(e);
583         }
584
585         return true;
586     }
587
588     /**
589      * Setup the reader
590      */

591     protected void setupReader(Environment environment)
592     throws ProcessingException {
593         try {
594             this.reader.setup(environment,environment.getObjectModel(),readerSource,readerParam);
595             // set the expires parameter on the pipeline if the reader is configured with one
596
if (readerParam.isParameter("expires")) {
597                 // should this checking be done somewhere else??
598
this.expires = readerParam.getParameterAsLong("expires");
599             }
600         } catch (Exception JavaDoc e){
601             handleException(e);
602         }
603     }
604
605     /**
606      * Set the mime-type for a reader
607      * @param environment The current environment
608      */

609     protected void setMimeTypeForReader(Environment environment)
610     throws ProcessingException {
611         // Set the mime-type
612
// the behaviour has changed from 2.1.6 to 2.1.7 according to bugs #10277 and #25121:
613
// MIME type declared in the sitemap (instance or declaration, in this order)
614
// Ask the Reader for a MIME type:
615
// A *.doc reader could peek into the file
616
// and return either text/plain or application/vnd.msword or
617
// the reader can use MIME type declared in WEB-INF/web.xml or
618
// by the server.
619
if ( this.readerMimeType != null ) {
620             // there was a mime-type defined on map:read in the sitemap
621
environment.setContentType(this.readerMimeType);
622         } else if ( this.sitemapReaderMimeType != null ) {
623             // there was a mime-type defined on map:reader in the sitemap
624
environment.setContentType(this.sitemapReaderMimeType);
625         } else {
626             // ask to the component itself
627
final String JavaDoc mimeType = this.reader.getMimeType();
628             if (mimeType != null) {
629                 environment.setContentType(mimeType);
630             }
631             // If no mimeType available, leave to to upstream proxy
632
// or browser to deduce content-type from URL extension.
633
}
634     }
635
636     /**
637      * Set the mime-type for a serializer
638      * @param environment The current environment
639      */

640     protected void setMimeTypeForSerializer(Environment environment)
641     throws ProcessingException {
642         if (this.lastConsumer == null) {
643             // internal processing: text/xml
644
environment.setContentType("text/xml");
645         } else {
646             // Set the mime-type
647
// the behaviour has changed from 2.1.6 to 2.1.7 according to bugs #10277 and #25121:
648
if (serializerMimeType != null) {
649                 // there was a mime-type defined on map:serialize in the sitemap
650
environment.setContentType(serializerMimeType);
651             } else if (sitemapSerializerMimeType != null) {
652                 // there was a mime-type defined on map:serializer in the sitemap
653
environment.setContentType(sitemapSerializerMimeType);
654             } else {
655                 // ask to the component itself
656
String JavaDoc mimeType = this.serializer.getMimeType();
657                 if (mimeType != null) {
658                     environment.setContentType (mimeType);
659                 } else {
660                     // No mimeType available
661
final String JavaDoc message = "Unable to determine MIME type for " +
662                           environment.getURIPrefix() + "/" + environment.getURI();
663                     throw new ProcessingException(message);
664                 }
665             }
666         }
667     }
668
669     protected boolean checkIfModified(Environment environment,
670                                       long lastModified)
671     throws ProcessingException {
672         // has the read resource been modified?
673
if(!environment.isResponseModified(lastModified)) {
674             // environment supports this, so we are finished
675
environment.setResponseIsNotModified();
676             return true;
677         }
678         return false;
679     }
680
681     /**
682      * Process the pipeline using a reader.
683      * @throws ProcessingException if
684      */

685     protected boolean processReader(Environment environment)
686     throws ProcessingException {
687         try {
688             this.setMimeTypeForReader(environment);
689             if (this.reader.shouldSetContentLength()) {
690                 ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
691                 this.reader.setOutputStream(os);
692                 this.reader.generate();
693                 environment.setContentLength(os.size());
694                 os.writeTo(environment.getOutputStream(0));
695             } else {
696                 this.reader.setOutputStream(environment.getOutputStream(this.outputBufferSize));
697                 this.reader.generate();
698             }
699         } catch (Exception JavaDoc e) {
700             handleException(e);
701         }
702
703         return true;
704     }
705
706     public void recycle() {
707         this.prepared = false;
708
709         // Release reader.
710
if (this.readerSelector != null) {
711             this.readerSelector.release(this.reader);
712             this.newManager.release(this.readerSelector);
713             this.readerSelector = null;
714             this.reader = null;
715             this.readerParam = null;
716         }
717
718         // Release generator.
719
if (this.generatorSelector != null) {
720             this.generatorSelector.release(this.generator);
721             this.newManager.release(this.generatorSelector);
722             this.generatorSelector = null;
723             this.generator = null;
724             this.generatorParam = null;
725         }
726
727         // Release transformers
728
int size = this.transformerSelectors.size();
729         for (int i = 0; i < size; i++) {
730             final ComponentSelector selector =
731                     (ComponentSelector) this.transformerSelectors.get(i);
732             selector.release((Component) this.transformers.get(i));
733             this.newManager.release(selector);
734         }
735         this.transformerSelectors.clear();
736         this.transformers.clear();
737         this.transformerParams.clear();
738         this.transformerSources.clear();
739
740         // Release serializer
741
if (this.serializerSelector != null) {
742             this.serializerSelector.release(this.serializer);
743             this.newManager.release(this.serializerSelector);
744             this.serializerSelector = null;
745             this.serializerParam = null;
746         }
747         this.serializer = null;
748         this.parameters = null;
749         this.lastConsumer = null;
750
751         // Release error handler
752
this.errorHandler = null;
753         if (this.errorPipeline != null) {
754             this.errorPipeline.release();
755             this.errorPipeline = null;
756         }
757     }
758
759     /**
760      * Process the given <code>Environment</code>, but do not use the
761      * serializer. Instead all SAX events are streamed to the XMLConsumer.
762      */

763     public boolean process(Environment environment, XMLConsumer consumer)
764     throws ProcessingException {
765         if (this.reader != null) {
766             throw new ProcessingException("Streaming of an internal pipeline is not possible with a reader.");
767         }
768
769         // Exception happened during setup and was handled
770
if (this.errorPipeline != null) {
771             return this.errorPipeline.process(environment, consumer);
772         }
773
774         // Have to buffer events if error handler is specified.
775
SaxBuffer buffer = null;
776         this.lastConsumer = this.errorHandler == null? consumer: (buffer = new SaxBuffer());
777         try {
778             connectPipeline(environment);
779             return processXMLPipeline(environment);
780         } catch (ProcessingException e) {
781             buffer = null;
782             return processErrorHandler(environment, e, consumer);
783         } finally {
784             if (buffer != null) {
785                 try {
786                     buffer.toSAX(consumer);
787                 } catch (SAXException JavaDoc e) {
788                     throw new ProcessingException("Failed to execute pipeline.", e);
789                 }
790             }
791         }
792     }
793
794     protected boolean processErrorHandler(Environment environment, ProcessingException e, XMLConsumer consumer)
795     throws ProcessingException {
796         if (this.errorHandler != null) {
797             try {
798                 this.errorPipeline = this.errorHandler.prepareErrorPipeline(e);
799                 if (this.errorPipeline != null) {
800                     this.errorPipeline.prepareInternal(environment);
801                     return this.errorPipeline.process(environment, consumer);
802                 }
803             } catch (Exception JavaDoc ignored) {
804                 getLogger().debug("Exception in error handler", ignored);
805             }
806         }
807
808         throw e;
809     }
810
811     /**
812      * Return valid validity objects for the event pipeline
813      * If the "event pipeline" (= the complete pipeline without the
814      * serializer) is cacheable and valid, return all validity objects.
815      * Otherwise return <code>null</code>
816      */

817     public SourceValidity getValidityForEventPipeline() {
818         return null;
819     }
820
821     /**
822      * Return the key for the event pipeline
823      * If the "event pipeline" (= the complete pipeline without the
824      * serializer) is cacheable and valid, return a key.
825      * Otherwise return <code>null</code>
826      */

827     public String JavaDoc getKeyForEventPipeline() {
828         return null;
829     }
830
831
832     /**
833      * Parse the expires parameter
834      */

835     private long parseExpires(String JavaDoc expire) {
836         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(expire);
837
838         // get <base>
839
String JavaDoc current = tokens.nextToken();
840         if (current.equals("modification")) {
841             getLogger().warn("the \"modification\" keyword is not yet" +
842                              " implemented. Assuming \"now\" as the base attribute");
843             current = "now";
844         }
845
846         if (!current.equals("now") && !current.equals("access")) {
847             getLogger().error("bad <base> attribute, Expires header will not be set");
848             return -1;
849         }
850
851         long number = 0;
852         long modifier = 0;
853         long expires = 0;
854
855         while (tokens.hasMoreTokens()) {
856             current = tokens.nextToken();
857
858             // get rid of the optional <plus> keyword
859
if (current.equals("plus")) {
860                 current = tokens.nextToken();
861             }
862
863             // We're expecting a sequence of <number> and <modification> here
864
// get <number> first
865
try {
866                 number = Long.parseLong(current);
867             } catch (NumberFormatException JavaDoc nfe) {
868                 getLogger().error("state violation: a number was expected here");
869                 return -1;
870             }
871
872             // now get <modifier>
873
try {
874                 current = tokens.nextToken();
875             } catch (NoSuchElementException JavaDoc nsee) {
876                 getLogger().error("State violation: expecting a modifier" +
877                                   " but no one found: Expires header will not be set");
878             }
879             if (current.equals("years")) {
880                 modifier = 365L * 24L * 60L * 60L * 1000L;
881             } else if (current.equals("months")) {
882                 modifier = 30L * 24L * 60L * 60L * 1000L;
883             } else if (current.equals("weeks")) {
884                 modifier = 7L * 24L * 60L * 60L * 1000L;
885             } else if (current.equals("days")) {
886                 modifier = 24L * 60L * 60L * 1000L;
887             } else if (current.equals("hours")) {
888                 modifier = 60L * 60L * 1000L;
889             } else if (current.equals("minutes")) {
890                 modifier = 60L * 1000L;
891             } else if (current.equals("seconds")) {
892                 modifier = 1000L;
893             } else {
894                 getLogger().error("Bad modifier (" + current +
895                                   "): ignoring expires configuration");
896                 return -1;
897             }
898             expires += number * modifier;
899         }
900
901         return expires;
902     }
903
904     protected Location getLocation(Parameters param) {
905         Location location = null;
906         if (param instanceof Locatable) {
907             location = ((Locatable)param).getLocation();
908         }
909         if (location == null) {
910             location = Location.UNKNOWN;
911         }
912         return location;
913     }
914
915     /**
916      * Handles exception which can happen during pipeline processing.
917      * If this not a connection reset, then all locations for pipeline components are
918      * added to the exception.
919      *
920      * @throws ConnectionResetException if connection reset detected
921      * @throws ProcessingException in all other cases
922      */

923     protected void handleException(Exception JavaDoc e) throws ProcessingException {
924         // Check if the client aborted the connection
925
if (e instanceof SocketException JavaDoc) {
926             if (e.getMessage().indexOf("reset") > 0
927                     || e.getMessage().indexOf("aborted") > 0
928                     || e.getMessage().indexOf("connection abort") > 0) {
929                 throw new ConnectionResetException("Connection reset by peer", e);
930             }
931         } else if (e instanceof IOException JavaDoc) {
932             // Tomcat5 wraps SocketException into ClientAbortException which extends IOException.
933
if (e.getClass().getName().endsWith("ClientAbortException")) {
934                 throw new ConnectionResetException("Connection reset by peer", e);
935             }
936         } else if (e instanceof ConnectionResetException) {
937             // Exception comes up from a deeper pipeline
938
throw (ConnectionResetException)e;
939         }
940
941         // Not a connection reset: add all location information
942
if (this.reader == null) {
943             // Add all locations in reverse order
944
ArrayList JavaDoc locations = new ArrayList JavaDoc(this.transformers.size() + 2);
945             locations.add(getLocation(this.serializerParam));
946             for (int i = this.transformerParams.size() - 1; i >= 0; i--) {
947                 locations.add(getLocation((Parameters)this.transformerParams.get(i)));
948             }
949             locations.add(getLocation(this.generatorParam));
950             
951             throw ProcessingException.throwLocated("Failed to process pipeline", e, locations);
952
953         } else {
954             // Add reader location
955
throw ProcessingException.throwLocated("Failed to process reader", e, getLocation(this.readerParam));
956         }
957     }
958 }
959
Popular Tags