KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > source > impl > SitemapSource


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.source.impl;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.Serializable JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.apache.avalon.framework.component.ComponentManager;
28 import org.apache.avalon.framework.logger.AbstractLogEnabled;
29 import org.apache.avalon.framework.logger.Logger;
30 import org.apache.cocoon.Constants;
31 import org.apache.cocoon.Processor;
32 import org.apache.cocoon.ResourceNotFoundException;
33 import org.apache.cocoon.components.CocoonComponentManager;
34 import org.apache.cocoon.components.pipeline.ProcessingPipeline;
35 import org.apache.cocoon.components.source.SourceUtil;
36 import org.apache.cocoon.environment.Environment;
37 import org.apache.cocoon.environment.ObjectModelHelper;
38 import org.apache.cocoon.environment.wrapper.EnvironmentWrapper;
39 import org.apache.cocoon.environment.wrapper.MutableEnvironmentFacade;
40 import org.apache.cocoon.xml.ContentHandlerWrapper;
41 import org.apache.cocoon.xml.XMLConsumer;
42 import org.apache.excalibur.source.Source;
43 import org.apache.excalibur.source.SourceException;
44 import org.apache.excalibur.source.SourceNotFoundException;
45 import org.apache.excalibur.source.SourceResolver;
46 import org.apache.excalibur.source.SourceValidity;
47 import org.apache.excalibur.xml.sax.XMLizable;
48 import org.xml.sax.ContentHandler JavaDoc;
49 import org.xml.sax.SAXException JavaDoc;
50 import org.xml.sax.ext.LexicalHandler JavaDoc;
51
52 /**
53  * Implementation of a {@link Source} that gets its content
54  * by invoking a pipeline.
55  *
56  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
57  * @version $Id: SitemapSource.java 161223 2005-04-13 22:10:29Z vgritsenko $
58  */

59 public final class SitemapSource
60         extends AbstractLogEnabled
61         implements Source, XMLizable {
62
63     /** The internal event pipeline validities */
64     private SitemapSourceValidity validity;
65
66     /** The system id */
67     private final String JavaDoc systemId;
68
69     /** The system id used for caching */
70     private String JavaDoc systemIdForCaching;
71
72     /** The current ComponentManager */
73     private final ComponentManager manager;
74
75     /** The processor */
76     private final Processor processor;
77
78     /** The pipeline processor */
79     private Processor pipelineProcessor;
80
81     /** The environment */
82     private final MutableEnvironmentFacade environment;
83
84     /** The <code>ProcessingPipeline</code> */
85     private ProcessingPipeline processingPipeline;
86
87     /** The redirect <code>Source</code> */
88     private Source redirectSource;
89
90     /** The <code>SAXException</code> if unable to get resource */
91     private SAXException JavaDoc exception;
92
93     /** Do I need a refresh ? */
94     private boolean needsRefresh;
95
96     /** The unique key for this processing */
97     private Object JavaDoc processKey;
98
99     /** The used protocol */
100     private final String JavaDoc protocol;
101
102     /** SourceResolver (for the redirect source) */
103     private SourceResolver sourceResolver;
104
105     private String JavaDoc mimeType;
106
107     /**
108      * Construct a new object
109      */

110     public SitemapSource(ComponentManager manager,
111                           String JavaDoc uri,
112                           Map JavaDoc parameters,
113                           Logger logger)
114     throws MalformedURLException JavaDoc {
115
116         Environment env = CocoonComponentManager.getCurrentEnvironment();
117         if ( env == null ) {
118             throw new MalformedURLException JavaDoc("The cocoon protocol can not be used outside an environment.");
119         }
120
121         this.manager = manager;
122         this.enableLogging(logger);
123
124         boolean rawMode = false;
125
126         // remove the protocol
127
int position = uri.indexOf(':') + 1;
128         if (position != 0) {
129             this.protocol = uri.substring(0, position-1);
130             // check for subprotocol
131
if (uri.startsWith("raw:", position)) {
132                 position += 4;
133                 rawMode = true;
134             }
135         } else {
136             throw new MalformedURLException JavaDoc("No protocol found for sitemap source in " + uri);
137         }
138
139         // does the uri point to this sitemap or to the root sitemap?
140
String JavaDoc prefix;
141         if (uri.startsWith("//", position)) {
142             position += 2;
143             this.processor = CocoonComponentManager.getActiveProcessor(env).getRootProcessor();
144             prefix = ""; // start at the root
145
} else if (uri.startsWith("/", position)) {
146             position ++;
147             prefix = null;
148             this.processor = CocoonComponentManager.getActiveProcessor(env);
149         } else {
150             throw new MalformedURLException JavaDoc("Malformed cocoon URI: " + uri);
151         }
152
153         // create the queryString (if available)
154
String JavaDoc queryString = null;
155         int queryStringPos = uri.indexOf('?', position);
156         if (queryStringPos != -1) {
157             queryString = uri.substring(queryStringPos + 1);
158             uri = uri.substring(position, queryStringPos);
159         } else if (position > 0) {
160             uri = uri.substring(position);
161         }
162
163         // determine if the queryString specifies a cocoon-view
164
String JavaDoc view = null;
165         if (queryString != null) {
166             int index = queryString.indexOf(Constants.VIEW_PARAM);
167             if (index != -1
168                 && (index == 0 || queryString.charAt(index-1) == '&')
169                 && queryString.length() > index + Constants.VIEW_PARAM.length()
170                 && queryString.charAt(index+Constants.VIEW_PARAM.length()) == '=') {
171
172                 String JavaDoc tmp = queryString.substring(index+Constants.VIEW_PARAM.length()+1);
173                 index = tmp.indexOf('&');
174                 if (index != -1) {
175                     view = tmp.substring(0,index);
176                 } else {
177                     view = tmp;
178                 }
179             } else {
180                 view = env.getView();
181             }
182         } else {
183             view = env.getView();
184         }
185
186         // build the request uri which is relative to the context
187
String JavaDoc requestURI = (prefix == null ? env.getURIPrefix() + uri : uri);
188
189         // create system ID
190
this.systemId = queryString == null ?
191             this.protocol + "://" + requestURI :
192             this.protocol + "://" + requestURI + "?" + queryString;
193
194         // create environment...
195
EnvironmentWrapper wrapper = new EnvironmentWrapper(env, requestURI,
196                                                    queryString, logger, manager, rawMode, view);
197         wrapper.setURI(prefix, uri);
198
199         // The environment is a facade whose delegate can be changed in case of internal redirects
200
this.environment = new MutableEnvironmentFacade(wrapper);
201
202         // ...and put information passed from the parent request to the internal request
203
if ( null != parameters ) {
204             this.environment.getObjectModel().put(ObjectModelHelper.PARENT_CONTEXT, parameters);
205         } else {
206             this.environment.getObjectModel().remove(ObjectModelHelper.PARENT_CONTEXT);
207         }
208
209         // create a new validity holder
210
this.validity = new SitemapSourceValidity();
211
212         // initialize
213
this.init();
214     }
215
216     /**
217      * Return the protocol identifier.
218      */

219     public String JavaDoc getScheme() {
220         return this.protocol;
221     }
222
223     /**
224      * Get the content length of the source or -1 if it
225      * is not possible to determine the length.
226      */

227     public long getContentLength() {
228         return -1;
229     }
230
231     /**
232      * Get the last modification date.
233      * @return The last modification in milliseconds since January 1, 1970 GMT
234      * or 0 if it is unknown
235      */

236     public long getLastModified() {
237         return 0;
238     }
239
240     /**
241      * Return an <code>InputStream</code> object to read from the source.
242      */

243     public InputStream JavaDoc getInputStream()
244     throws IOException JavaDoc, SourceException {
245
246         if (this.needsRefresh) {
247             this.refresh();
248         }
249         // VG: Why exception is not thrown in constructor?
250
if (this.exception != null) {
251             throw new SourceException("Cannot get input stream for " + getURI(), this.exception);
252         }
253
254         if (this.redirectSource != null) {
255             return this.redirectSource.getInputStream();
256         }
257
258         try {
259             ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
260             this.environment.setOutputStream(os);
261             CocoonComponentManager.enterEnvironment(this.environment,
262                                                     this.manager,
263                                                     this.pipelineProcessor);
264             try {
265                 this.processingPipeline.process(this.environment);
266             } finally {
267                 CocoonComponentManager.leaveEnvironment();
268             }
269
270             return new ByteArrayInputStream JavaDoc(os.toByteArray());
271
272         } catch (ResourceNotFoundException e) {
273             throw new SourceNotFoundException("Exception during processing of " + this.systemId, e);
274         } catch (Exception JavaDoc e) {
275             throw new SourceException("Exception during processing of " + this.systemId, e);
276         } finally {
277             // Unhide wrapped environment output stream
278
this.environment.setOutputStream(null);
279             this.needsRefresh = true;
280         }
281     }
282
283     /**
284      * Returns the unique identifer for this source
285      */

286     public String JavaDoc getURI() {
287         return this.systemIdForCaching;
288     }
289
290     /**
291      * Returns true always.
292      * @see org.apache.excalibur.source.Source#exists()
293      */

294     public boolean exists() {
295         return true;
296     }
297
298     /**
299      * Get the validity object. This wraps validity of the enclosed event
300      * pipeline. If pipeline is not cacheable, <code>null</code> is returned.
301      */

302     public SourceValidity getValidity() {
303         return this.validity.getNestedValidity() == null? null: this.validity;
304     }
305
306     /**
307      * The mime-type of the content described by this object.
308      * If the source is not able to determine the mime-type by itself
309      * this can be null.
310      */

311      public String JavaDoc getMimeType() {
312          return this.mimeType;
313      }
314
315     /**
316      * Refresh this object and update the last modified date
317      * and content length.
318      */

319     public void refresh() {
320         this.reset();
321         this.init();
322     }
323
324     /**
325      * Initialize
326      */

327     protected void init() {
328         this.systemIdForCaching = this.systemId;
329         try {
330             this.processKey = CocoonComponentManager.startProcessing(this.environment);
331             this.processingPipeline = this.processor.buildPipeline(this.environment);
332             this.pipelineProcessor = CocoonComponentManager.getActiveProcessor(this.environment);
333
334             String JavaDoc redirectURL = this.environment.getRedirectURL();
335             if (redirectURL == null) {
336
337                 CocoonComponentManager.enterEnvironment(this.environment,
338                                                         this.manager,
339                                                         this.pipelineProcessor);
340                 try {
341                     this.processingPipeline.prepareInternal(this.environment);
342                     this.validity.set(this.processingPipeline.getValidityForEventPipeline());
343                     this.mimeType = this.environment.getContentType();
344
345                     final String JavaDoc eventPipelineKey = this.processingPipeline.getKeyForEventPipeline();
346                     if (eventPipelineKey != null) {
347                         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(this.systemId);
348                         if (this.systemId.indexOf('?') == -1) {
349                             buffer.append('?');
350                         } else {
351                             buffer.append('&');
352                         }
353                         buffer.append("pipelinehash=");
354                         buffer.append(eventPipelineKey);
355                         this.systemIdForCaching = buffer.toString();
356                     } else {
357                         this.systemIdForCaching = this.systemId;
358                     }
359                 } finally {
360                     CocoonComponentManager.leaveEnvironment();
361                 }
362             } else {
363                 if (redirectURL.indexOf(":") == -1) {
364                     redirectURL = this.protocol + ":/" + redirectURL;
365                 }
366                 if (this.sourceResolver == null) {
367                     this.sourceResolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
368                 }
369                 this.redirectSource = this.sourceResolver.resolveURI(redirectURL);
370                 this.validity.set(this.redirectSource.getValidity());
371                 this.mimeType = this.redirectSource.getMimeType();
372             }
373         } catch (SAXException JavaDoc e) {
374             reset();
375             this.exception = e;
376         } catch (Exception JavaDoc e) {
377             reset();
378             this.exception = new SAXException JavaDoc("Could not get sitemap source " + this.systemId, e);
379         }
380         this.needsRefresh = false;
381     }
382
383     /**
384      * Stream content to the content handler
385      */

386     public void toSAX(ContentHandler JavaDoc contentHandler)
387     throws SAXException JavaDoc {
388         if (this.needsRefresh) {
389             this.refresh();
390         }
391         if (this.exception != null) {
392             throw this.exception;
393         }
394         try {
395             if (this.redirectSource != null) {
396                 SourceUtil.parse(this.manager, this.redirectSource, contentHandler);
397             } else {
398                 XMLConsumer consumer;
399                 if (contentHandler instanceof XMLConsumer) {
400                     consumer = (XMLConsumer)contentHandler;
401                 } else if (contentHandler instanceof LexicalHandler JavaDoc) {
402                     consumer = new ContentHandlerWrapper(contentHandler, (LexicalHandler JavaDoc)contentHandler);
403                 } else {
404                     consumer = new ContentHandlerWrapper(contentHandler);
405                 }
406                 // We have to add an environment changer
407
// for clean environment stack handling.
408
CocoonComponentManager.enterEnvironment(this.environment,
409                                                         this.manager,
410                                                         this.pipelineProcessor);
411                 try {
412                     this.processingPipeline.process(this.environment,
413                                                     CocoonComponentManager.createEnvironmentAwareConsumer(consumer));
414                 } finally {
415                     CocoonComponentManager.leaveEnvironment();
416                 }
417             }
418         } catch (SAXException JavaDoc e) {
419             // Preserve original exception
420
throw e;
421         } catch (Exception JavaDoc e) {
422             throw new SAXException JavaDoc("Exception during processing of " + this.systemId, e);
423         } finally {
424             this.needsRefresh = true;
425         }
426     }
427
428     /**
429      * Reset everything
430      */

431     private void reset() {
432         if (this.processingPipeline != null) {
433             this.processingPipeline.release();
434             this.processingPipeline = null;
435         }
436
437         if (this.processKey != null) {
438             CocoonComponentManager.endProcessing(this.environment, this.processKey);
439             this.processKey = null;
440         }
441
442         if (this.redirectSource != null) {
443             this.sourceResolver.release(this.redirectSource);
444             this.redirectSource = null;
445         }
446
447         this.validity.set(null);
448
449         this.environment.reset();
450         this.exception = null;
451         this.needsRefresh = true;
452         this.pipelineProcessor = null;
453     }
454
455     /**
456      * Recyclable
457      */

458     public void recycle() {
459         this.validity = new SitemapSourceValidity();
460         this.reset();
461         if (this.sourceResolver != null) {
462             this.manager.release(this.sourceResolver);
463             this.sourceResolver = null;
464         }
465     }
466
467     /**
468      * Get the value of a parameter.
469      * Using this it is possible to get custom information provided by the
470      * source implementation, like an expires date, HTTP headers etc.
471      */

472     public String JavaDoc getParameter(String JavaDoc name) {
473         return null;
474     }
475
476     /**
477      * Get the value of a parameter.
478      * Using this it is possible to get custom information provided by the
479      * source implementation, like an expires date, HTTP headers etc.
480      */

481     public long getParameterAsLong(String JavaDoc name) {
482         return 0;
483     }
484
485     /**
486      * Get parameter names
487      * Using this it is possible to get custom information provided by the
488      * source implementation, like an expires date, HTTP headers etc.
489      */

490     public Iterator JavaDoc getParameterNames() {
491         return java.util.Collections.EMPTY_LIST.iterator();
492     }
493
494     /**
495      * A simple SourceValidity protecting callers from resets.
496      */

497     public static final class SitemapSourceValidity implements SourceValidity, Serializable JavaDoc {
498
499         private SourceValidity validity;
500
501         private SitemapSourceValidity() {
502             super();
503         }
504
505         void set(SourceValidity validity) {
506             this.validity = validity;
507         }
508
509         public int isValid() {
510             return (this.validity != null ?
511                     this.validity.isValid() :
512                     SourceValidity.INVALID);
513         }
514
515         public int isValid(SourceValidity validity) {
516             if (validity instanceof SitemapSourceValidity) {
517                 return (this.validity != null ?
518                         this.validity.isValid(((SitemapSourceValidity) validity).getNestedValidity()) :
519                         SourceValidity.INVALID);
520             }
521             return SourceValidity.INVALID;
522         }
523
524         public SourceValidity getNestedValidity() {
525             return this.validity;
526         }
527     }
528 }
529
Popular Tags