001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.source;
018:
019: import org.apache.avalon.framework.component.ComponentException;
020: import org.apache.avalon.framework.component.ComponentManager;
021: import org.apache.avalon.framework.logger.Logger;
022:
023: import org.apache.excalibur.source.SourceException;
024:
025: import org.apache.cocoon.ProcessingException;
026: import org.apache.cocoon.Processor;
027: import org.apache.cocoon.components.CocoonComponentManager;
028: import org.apache.cocoon.components.pipeline.ProcessingPipeline;
029: import org.apache.cocoon.components.sax.XMLDeserializer;
030: import org.apache.cocoon.components.sax.XMLSerializer;
031: import org.apache.cocoon.environment.Environment;
032: import org.apache.cocoon.environment.ModifiableSource;
033: import org.apache.cocoon.environment.wrapper.EnvironmentWrapper;
034: import org.apache.cocoon.xml.AbstractXMLConsumer;
035: import org.apache.cocoon.xml.ContentHandlerWrapper;
036: import org.apache.cocoon.xml.XMLConsumer;
037: import org.xml.sax.ContentHandler;
038: import org.xml.sax.InputSource;
039: import org.xml.sax.SAXException;
040: import org.xml.sax.ext.LexicalHandler;
041:
042: import java.io.ByteArrayInputStream;
043: import java.io.ByteArrayOutputStream;
044: import java.io.IOException;
045: import java.io.InputStream;
046: import java.net.MalformedURLException;
047:
048: /**
049: * Description of a source which is defined by a pipeline.
050: *
051: * @deprecated by the Avalon Excalibur Source Resolving
052: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
053: * @version CVS $Id: SitemapSource.java 433543 2006-08-22 06:22:54Z crossley $
054: */
055: public final class SitemapSource extends AbstractXMLConsumer implements
056: ModifiableSource {
057:
058: /** The last modification date or 0 */
059: private long lastModificationDate;
060:
061: /** The system id */
062: private String systemId;
063:
064: /** The uri */
065: private String uri;
066:
067: /** The current ComponentManager */
068: private ComponentManager manager;
069:
070: /** The processor */
071: private Processor processor;
072:
073: /** The pipeline processor */
074: private Processor pipelineProcessor;
075:
076: /** The environment */
077: private EnvironmentWrapper environment;
078:
079: /** The prefix for the processing */
080: private String prefix;
081:
082: /** The <code>ProcessingPipeline</code> */
083: private ProcessingPipeline processingPipeline;
084:
085: /** The redirect <code>Source</code> */
086: private org.apache.excalibur.source.Source redirectSource;
087:
088: /** The <code>SAXException</code> if unable to get resource */
089: private SAXException exception;
090:
091: /** Do I need a refresh ? */
092: private boolean needsRefresh;
093:
094: /** The unique key for this processing */
095: private Object processKey;
096:
097: /**
098: * Construct a new object
099: */
100: public SitemapSource(ComponentManager manager, String uri,
101: Logger logger) throws IOException, ProcessingException {
102:
103: Environment env = CocoonComponentManager
104: .getCurrentEnvironment();
105: if (env == null) {
106: throw new MalformedURLException(
107: "The cocoon protocol can not be used outside an environment.");
108: }
109:
110: this .manager = manager;
111: this .enableLogging(logger);
112: boolean rawMode = false;
113:
114: // remove the protocol
115: int position = uri.indexOf(':') + 1;
116: if (position != 0) {
117: // check for subprotocol
118: if (uri.startsWith("raw:", position)) {
119: position += 4;
120: rawMode = true;
121: }
122: }
123:
124: // does the uri point to this sitemap or to the root sitemap?
125: if (uri.startsWith("//", position)) {
126: position += 2;
127: try {
128: this .processor = (Processor) this .manager
129: .lookup(Processor.ROLE);
130: } catch (ComponentException e) {
131: throw new ProcessingException(
132: "Cannot get Processor instance", e);
133: }
134: this .prefix = ""; // start at the root
135: } else if (uri.startsWith("/", position)) {
136: position++;
137: this .prefix = null;
138: this .processor = CocoonComponentManager
139: .getActiveProcessor(env);
140: } else {
141: throw new ProcessingException("Malformed cocoon URI.");
142: }
143:
144: // create the queryString (if available)
145: String queryString = null;
146: int queryStringPos = uri.indexOf('?', position);
147: if (queryStringPos != -1) {
148: queryString = uri.substring(queryStringPos + 1);
149: uri = uri.substring(position, queryStringPos);
150: } else if (position > 0) {
151: uri = uri.substring(position);
152: }
153:
154: // build the request uri which is relative to the context
155: String requestURI = (this .prefix == null ? env.getURIPrefix()
156: + uri : uri);
157:
158: // create system ID
159: this .systemId = queryString == null ? "cocoon://" + requestURI
160: : "cocoon://" + requestURI + "?" + queryString;
161:
162: this .environment = new EnvironmentWrapper(env, requestURI,
163: queryString, logger, manager, rawMode);
164: this .uri = uri;
165: this .refresh();
166: }
167:
168: /**
169: * Get the last modification date of the source or 0 if it
170: * is not possible to determine the date.
171: */
172: public long getLastModified() {
173: if (this .needsRefresh) {
174: this .refresh();
175: }
176: return this .lastModificationDate;
177: }
178:
179: /**
180: * Get the content length of the source or -1 if it
181: * is not possible to determine the length.
182: */
183: public long getContentLength() {
184: return -1;
185: }
186:
187: /**
188: * Return an <code>InputStream</code> object to read from the source.
189: */
190: public InputStream getInputStream() throws ProcessingException,
191: IOException {
192:
193: if (this .needsRefresh) {
194: this .refresh();
195: }
196: // VG: Why exception is not thrown in constructor?
197: if (this .exception != null) {
198: throw new ProcessingException(this .exception);
199: }
200:
201: if (this .redirectSource != null) {
202: try {
203: return this .redirectSource.getInputStream();
204: } catch (SourceException se) {
205: throw SourceUtil.handle(se);
206: }
207: }
208:
209: try {
210: ByteArrayOutputStream os = new ByteArrayOutputStream();
211: this .environment.setOutputStream(os);
212: CocoonComponentManager.enterEnvironment(this .environment,
213: this .manager, this .pipelineProcessor);
214: try {
215: this .processingPipeline.process(this .environment);
216: } finally {
217: CocoonComponentManager.leaveEnvironment();
218: }
219: return new ByteArrayInputStream(os.toByteArray());
220:
221: } catch (ProcessingException e) {
222: throw e;
223: } catch (Exception e) {
224: throw new ProcessingException(
225: "Exception during processing of " + this .systemId,
226: e);
227: } finally {
228: // Unhide wrapped environment output stream
229: this .environment.setOutputStream(null);
230: reset();
231: }
232: }
233:
234: /**
235: * Return the unique identifer for this source
236: */
237: public String getSystemId() {
238: return this .systemId;
239: }
240:
241: /**
242: * Refresh this object and update the last modified date
243: * and content length.
244: */
245: public void refresh() {
246: reset();
247: try {
248: this .processKey = CocoonComponentManager
249: .startProcessing(this .environment);
250: this .environment.setURI(this .prefix, this .uri);
251: this .processingPipeline = this .processor
252: .buildPipeline(this .environment);
253: this .pipelineProcessor = CocoonComponentManager
254: .getActiveProcessor(this .environment);
255: String redirectURL = this .environment.getRedirectURL();
256: if (redirectURL != null) {
257: if (redirectURL.indexOf(":") == -1) {
258: redirectURL = "cocoon:/" + redirectURL;
259: }
260: this .redirectSource = this .environment
261: .resolveURI(redirectURL);
262: this .lastModificationDate = this .redirectSource
263: .getLastModified();
264: }
265: } catch (SAXException e) {
266: reset();
267: this .exception = e;
268: } catch (Exception e) {
269: reset();
270: this .exception = new SAXException(
271: "Could not get sitemap source " + this .systemId, e);
272: }
273: this .needsRefresh = false;
274: }
275:
276: /**
277: * Return a new <code>InputSource</code> object
278: */
279: public InputSource getInputSource() throws ProcessingException,
280: IOException {
281: InputSource newObject = new InputSource(this .getInputStream());
282: newObject.setSystemId(this .systemId);
283: return newObject;
284: }
285:
286: /**
287: * Stream content to the content handler
288: */
289: public void toSAX(ContentHandler contentHandler)
290: throws SAXException {
291: if (this .needsRefresh) {
292: this .refresh();
293: }
294: if (this .exception != null) {
295: throw this .exception;
296: }
297: try {
298: XMLConsumer consumer;
299: if (contentHandler instanceof XMLConsumer) {
300: consumer = (XMLConsumer) contentHandler;
301: } else if (contentHandler instanceof LexicalHandler) {
302: consumer = new ContentHandlerWrapper(contentHandler,
303: (LexicalHandler) contentHandler);
304: } else {
305: consumer = new ContentHandlerWrapper(contentHandler);
306: }
307: if (this .redirectSource != null) {
308: SourceUtil.parse(this .manager, this .redirectSource,
309: consumer);
310: } else {
311: // We have to buffer the result in order to get
312: // clean environment stack handling.
313: XMLSerializer xmls = (XMLSerializer) this .manager
314: .lookup(XMLSerializer.ROLE);
315: Object fragment;
316: CocoonComponentManager.enterEnvironment(
317: this .environment, this .manager,
318: this .pipelineProcessor);
319: try {
320: this .processingPipeline.process(this .environment,
321: xmls);
322: fragment = xmls.getSAXFragment();
323: } finally {
324: this .manager.release(xmls);
325: CocoonComponentManager.leaveEnvironment();
326: }
327: XMLDeserializer xmld = (XMLDeserializer) this .manager
328: .lookup(XMLDeserializer.ROLE);
329: try {
330: xmld.setConsumer(consumer);
331: xmld.deserialize(fragment);
332: } finally {
333: this .manager.release(xmld);
334: }
335: }
336: } catch (SAXException e) {
337: // Preserve original exception
338: throw e;
339: } catch (Exception e) {
340: throw new SAXException("Exception during processing of "
341: + this .systemId, e);
342: } finally {
343: reset();
344: }
345: }
346:
347: private void reset() {
348: if (this .processingPipeline != null)
349: this .processingPipeline.release();
350: if (this .processKey != null) {
351: CocoonComponentManager.endProcessing(this .environment,
352: this .processKey);
353: this .processKey = null;
354: }
355: this .processingPipeline = null;
356: this .lastModificationDate = 0;
357: this .environment.release(this .redirectSource);
358: this .environment.reset();
359: this .redirectSource = null;
360: this .exception = null;
361: this .needsRefresh = true;
362: this .pipelineProcessor = null;
363: }
364:
365: public void recycle() {
366: reset();
367: }
368: }
|