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.transformation;
018:
019: import org.apache.avalon.framework.parameters.Parameters;
020: import org.apache.avalon.framework.service.ServiceException;
021: import org.apache.avalon.framework.service.ServiceManager;
022:
023: import org.apache.cocoon.ProcessingException;
024: import org.apache.cocoon.caching.CacheableProcessingComponent;
025: import org.apache.cocoon.components.sax.XMLDeserializer;
026: import org.apache.cocoon.components.sax.XMLSerializer;
027: import org.apache.cocoon.components.source.SourceUtil;
028: import org.apache.cocoon.environment.SourceResolver;
029: import org.apache.cocoon.transformation.helpers.IncludeCacheManager;
030: import org.apache.cocoon.transformation.helpers.IncludeCacheManagerSession;
031: import org.apache.cocoon.xml.IncludeXMLConsumer;
032: import org.apache.cocoon.xml.XMLConsumer;
033: import org.apache.cocoon.xml.XMLUtils;
034:
035: import org.apache.commons.lang.BooleanUtils;
036: import org.apache.commons.lang.StringUtils;
037: import org.apache.excalibur.source.Source;
038: import org.apache.excalibur.source.SourceException;
039: import org.apache.excalibur.source.SourceParameters;
040: import org.apache.excalibur.source.SourceValidity;
041: import org.apache.excalibur.xml.dom.DOMParser;
042: import org.apache.excalibur.xml.xpath.XPathProcessor;
043: import org.w3c.dom.Document;
044: import org.w3c.dom.NodeList;
045: import org.xml.sax.Attributes;
046: import org.xml.sax.InputSource;
047: import org.xml.sax.SAXException;
048: import org.xml.sax.helpers.AttributesImpl;
049:
050: import java.io.IOException;
051: import java.io.Serializable;
052: import java.util.Map;
053:
054: /**
055: * @cocoon.sitemap.component.documentation
056: * This transformer triggers for the element <code>include</code> in the
057: * namespace "http://apache.org/cocoon/include/1.0".
058: * The <code>src</code> attribute contains the url which points to
059: * an xml resource which is include instead of the element.
060: * With the attributes <code>element</code>, <code>ns</code> and
061: * <code>prefix</code> it is possible to specify an element
062: * which surrounds the included content.
063: *
064: * @cocoon.sitemap.component.name cinclude
065: * @cocoon.sitemap.component.logger sitemap.transformer.cinclude
066: * @cocoon.sitemap.component.documentation.caching
067: * See documentation for further information.
068: *
069: * @cocoon.sitemap.component.pooling.max 16
070: *
071: * This transformer also supports a more verbose but flexible version:
072: * <cinclude:includexml xmlns:cinclude="http://apache.org/cocoon/include/1.0" ignoreErrors="false">
073: * <cinclude:src>THE SRC URI</cinclude:src>
074: * <!-- This is an optional configuration block -->
075: * <cinclude:configuration>
076: * <!-- For example if you want to make a HTTP POST -->
077: * <cinclude:parameter>
078: * <cinclude:name>method</cinclude:name>
079: * <cinclude:value>POST</cinclude:value>
080: * </cinclude:parameter>
081: * </cinclude:configuration>
082: * <!-- The following are optional parameters appended to the URI -->
083: * <cinclude:parameters>
084: * <cinclude:parameter>
085: * <cinclude:name>a name</cinclude:name>
086: * <cinclude:value>a value</cinclude:value>
087: * </cinclude:parameter>
088: * <!-- more can follow -->
089: * </cinclude:parameters>
090: * </cinclude:includexml>
091: *
092: *
093: * This transformer also supports caching of the included content.
094: * Therefore it triggers for the element <code>cached-include</code> in the
095: * namespace "http://apache.org/cocoon/include/1.0".
096: * The <code>src</code> attribute contains the url which points to
097: * an xml resource which is include instead of the element.
098: * First, it works like the usual include command. But it can be
099: * configured with various parameters:
100: * The most important one is the <code>expires</code> parameter.
101: * If (and only if) this is set to a value greater than zero,
102: * all included content is cached for the given period of time.
103: * So if any other request includes the same URI, the content
104: * is fetched from the cache. The expires value is in seconds.
105: * Usually the content is cached in the usual store, but you
106: * can also define a writeable source with the <code>source</code> parameter,
107: * e.g. "file:/c:/temp". Then the cached content is written into this
108: * directory.
109: * With the optional <code>purge</code> set to <code>true</code>
110: * the cache is purged which means the cached content is regarded as
111: * invalid nevertheless if it has expired or not.
112: * With the optional parameter <code>parallel</code> the various
113: * included contents are processed (included) in parallel rather than
114: * in a series.
115: * With the optional parameter <code>preemptive</code> set to <code>true</code>
116: * a pre-emptive caching is activated. When a resource is requested with
117: * pre-emptive caching, this transformer always attempts to get the
118: * content from the cache. If the content is not in the cache, it is
119: * of course retrieved from the original source and cached.
120: * If the cached resource has expired, it is still provided. The cache
121: * is updated by a background task. This task has to be started
122: * beforehand.
123: *
124: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
125: * @author <a href="mailto:acoliver@apache.org">Andrew C. Oliver</a>
126: * @version $Id: CIncludeTransformer.java 433543 2006-08-22 06:22:54Z crossley $
127: */
128: public class CIncludeTransformer extends AbstractSAXTransformer
129: implements CacheableProcessingComponent {
130:
131: public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0";
132: public static final String CINCLUDE_INCLUDE_ELEMENT = "include";
133: public static final String CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE = "src";
134: public static final String CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE = "element";
135: public static final String CINCLUDE_INCLUDE_ELEMENT_SELECT_ATTRIBUTE = "select";
136: public static final String CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE = "ns";
137: public static final String CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE = "prefix";
138: public static final String CINCLUDE_INCLUDE_ELEMENT_STRIP_ROOT_ATTRIBUTE = "strip-root";
139:
140: public static final String CINCLUDE_INCLUDEXML_ELEMENT = "includexml";
141: public static final String CINCLUDE_INCLUDEXML_ELEMENT_IGNORE_ERRORS_ATTRIBUTE = "ignoreErrors";
142: public static final String CINCLUDE_SRC_ELEMENT = "src";
143: public static final String CINCLUDE_CONFIGURATION_ELEMENT = "configuration";
144: public static final String CINCLUDE_PARAMETERS_ELEMENT = "parameters";
145: public static final String CINCLUDE_PARAMETER_ELEMENT = "parameter";
146: public static final String CINCLUDE_NAME_ELEMENT = "name";
147: public static final String CINCLUDE_VALUE_ELEMENT = "value";
148:
149: public static final String CINCLUDE_CACHED_INCLUDE_ELEMENT = "cached-include";
150: protected static final String CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT = "cached-includep";
151:
152: private static final int STATE_OUTSIDE = 0;
153: private static final int STATE_INCLUDE = 1;
154:
155: /** The configuration of includexml */
156: protected Parameters configurationParameters;
157:
158: /** The parameters for includexml */
159: protected SourceParameters resourceParameters;
160:
161: /** The current state: STATE_ */
162: protected int state;
163:
164: protected IncludeCacheManager cacheManager;
165:
166: protected IncludeCacheManagerSession cachingSession;
167:
168: protected boolean compiling;
169:
170: protected IncludeXMLConsumer filter;
171:
172: protected XMLSerializer recorder;
173:
174: protected AttributesImpl srcAttributes = new AttributesImpl();
175:
176: protected boolean supportCaching;
177:
178: /** Remember the start time of the request for profiling */
179: protected long startTime;
180:
181: /**
182: * Constructor
183: * Set the namespace
184: */
185: public CIncludeTransformer() {
186: this .defaultNamespaceURI = CINCLUDE_NAMESPACE_URI;
187: }
188:
189: /**
190: * Setup the component.
191: */
192: public void setup(SourceResolver resolver, Map objectModel,
193: String source, Parameters parameters)
194: throws ProcessingException, SAXException, IOException {
195: super .setup(resolver, objectModel, source, parameters);
196: this .state = STATE_OUTSIDE;
197: if (null != this .cacheManager) {
198: this .cachingSession = this .cacheManager
199: .getSession(this .parameters);
200: }
201: this .compiling = false;
202: this .supportCaching = parameters.getParameterAsBoolean(
203: "support-caching", false);
204: if (getLogger().isDebugEnabled()) {
205: getLogger().debug(
206: "Starting, session " + this .cachingSession);
207: this .startTime = System.currentTimeMillis();
208: }
209: }
210:
211: /**
212: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
213: */
214: public void service(ServiceManager manager) throws ServiceException {
215: super .service(manager);
216: if (this .manager.hasService(IncludeCacheManager.ROLE)) {
217: this .cacheManager = (IncludeCacheManager) this .manager
218: .lookup(IncludeCacheManager.ROLE);
219: } else {
220: getLogger()
221: .warn(
222: "The cinclude transformer cannot find the IncludeCacheManager. "
223: + "Therefore caching is turned off for the include transformer.");
224: }
225: }
226:
227: /**
228: * @see org.apache.avalon.framework.activity.Disposable#dispose()
229: */
230: public void dispose() {
231: if (null != this .manager) {
232: this .manager.release(this .cacheManager);
233: this .manager = null;
234: }
235: super .dispose();
236: }
237:
238: /**
239: * Recycle the component
240: */
241: public void recycle() {
242: if (null != this .cachingSession) {
243: this .cacheManager.terminateSession(this .cachingSession);
244: }
245: this .cachingSession = null;
246: if (null != this .recorder) {
247: this .manager.release(this .recorder);
248: this .recorder = null;
249: }
250:
251: this .configurationParameters = null;
252: this .resourceParameters = null;
253: if (getLogger().isDebugEnabled()) {
254: getLogger()
255: .debug(
256: "Finishing, time: "
257: + (System.currentTimeMillis() - this .startTime));
258: this .startTime = 0;
259: }
260: this .filter = null;
261:
262: super .recycle();
263: }
264:
265: public void startTransformingElement(String uri, String name,
266: String raw, Attributes attr) throws ProcessingException,
267: IOException, SAXException {
268: // Element: include
269: if (name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
270: String stripRootValue = attr.getValue("",
271: CINCLUDE_INCLUDE_ELEMENT_STRIP_ROOT_ATTRIBUTE);
272: boolean stripRoot = StringUtils.equals(stripRootValue,
273: "true");
274:
275: processCIncludeElement(
276: attr.getValue("",
277: CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
278: attr.getValue("",
279: CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
280: attr.getValue("",
281: CINCLUDE_INCLUDE_ELEMENT_SELECT_ATTRIBUTE),
282: attr.getValue("",
283: CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
284: attr.getValue("",
285: CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE),
286: stripRoot, false);
287:
288: // Element: includexml
289: } else if (name.equals(CINCLUDE_INCLUDEXML_ELEMENT)
290: && this .state == STATE_OUTSIDE) {
291: this .state = STATE_INCLUDE;
292: String ignoreErrors = attr
293: .getValue("",
294: CINCLUDE_INCLUDEXML_ELEMENT_IGNORE_ERRORS_ATTRIBUTE);
295: if (ignoreErrors == null || ignoreErrors.length() == 0) {
296: ignoreErrors = "false";
297: }
298: this .stack.push(BooleanUtils
299: .toBooleanObject(this .ignoreEmptyCharacters));
300: this .stack.push(BooleanUtils
301: .toBooleanObject(this .ignoreWhitespaces));
302: this .stack.push(ignoreErrors);
303:
304: this .ignoreEmptyCharacters = false;
305: this .ignoreWhitespaces = true;
306:
307: // target
308: } else if (name.equals(CINCLUDE_SRC_ELEMENT)
309: && this .state == STATE_INCLUDE) {
310: this .startTextRecording();
311:
312: // configparameters
313: } else if (name.equals(CINCLUDE_CONFIGURATION_ELEMENT)
314: && this .state == STATE_INCLUDE) {
315: stack.push("end");
316:
317: // parameters
318: } else if (name.equals(CINCLUDE_PARAMETERS_ELEMENT)
319: && this .state == STATE_INCLUDE) {
320: stack.push("end");
321:
322: // parameter
323: } else if (name.equals(CINCLUDE_PARAMETER_ELEMENT)
324: && this .state == STATE_INCLUDE) {
325:
326: // parameter name
327: } else if (name.equals(CINCLUDE_NAME_ELEMENT)
328: && this .state == STATE_INCLUDE) {
329: this .startTextRecording();
330:
331: // parameter value
332: } else if (name.equals(CINCLUDE_VALUE_ELEMENT)
333: && this .state == STATE_INCLUDE) {
334: this .startSerializedXMLRecording(XMLUtils
335: .createPropertiesForXML(true));
336:
337: } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
338:
339: String src = processCIncludeElement(attr.getValue("",
340: CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE), null,
341: null, null, null, false, this .cacheManager != null);
342: if (this .compiling) {
343: this .srcAttributes.addAttribute("",
344: CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE,
345: CINCLUDE_SRC_ELEMENT, "CDATA", src);
346: super .startTransformingElement(uri,
347: CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT,
348: raw + "p", this .srcAttributes);
349: this .srcAttributes.clear();
350: }
351: } else {
352: super .startTransformingElement(uri, name, raw, attr);
353: }
354: }
355:
356: public void endTransformingElement(String uri, String name,
357: String raw) throws ProcessingException, IOException,
358: SAXException {
359: if (name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
360: // do nothing
361: return;
362:
363: } else if (name.equals(CINCLUDE_INCLUDEXML_ELEMENT)
364: && this .state == STATE_INCLUDE) {
365: // Element: includexml
366:
367: this .state = STATE_OUTSIDE;
368:
369: final String resource = (String) stack.pop();
370:
371: final boolean ignoreErrors = stack.pop().equals("true");
372:
373: if (getLogger().isDebugEnabled()) {
374: getLogger().debug(
375: "Processing includexml element: src="
376: + resource + ", ignoreErrors="
377: + ignoreErrors + ", configuration="
378: + this .configurationParameters
379: + ", parameters="
380: + this .resourceParameters);
381: }
382: Source source = null;
383:
384: try {
385: source = SourceUtil.getSource(resource,
386: this .configurationParameters,
387: this .resourceParameters, this .resolver);
388:
389: XMLSerializer serializer = null;
390: XMLDeserializer deserializer = null;
391: try {
392: if (ignoreErrors) {
393: serializer = (XMLSerializer) this .manager
394: .lookup(XMLSerializer.ROLE);
395: deserializer = (XMLDeserializer) this .manager
396: .lookup(XMLDeserializer.ROLE);
397: SourceUtil.toSAX(source, serializer,
398: this .configurationParameters, true);
399: deserializer.setConsumer(this .xmlConsumer);
400: deserializer.deserialize(serializer
401: .getSAXFragment());
402: } else {
403: SourceUtil.toSAX(source, this .xmlConsumer,
404: this .configurationParameters, true);
405: }
406: } catch (ProcessingException pe) {
407: if (!ignoreErrors)
408: throw pe;
409: } catch (ServiceException ignore) {
410: } finally {
411: this .manager.release(serializer);
412: this .manager.release(deserializer);
413: }
414: } catch (SourceException se) {
415: if (!ignoreErrors)
416: throw SourceUtil.handle(se);
417: } catch (SAXException se) {
418: if (!ignoreErrors)
419: throw se;
420: } catch (IOException ioe) {
421: if (!ignoreErrors)
422: throw ioe;
423: } finally {
424: this .resolver.release(source);
425: }
426:
427: // restore values
428: this .ignoreWhitespaces = ((Boolean) stack.pop())
429: .booleanValue();
430: this .ignoreEmptyCharacters = ((Boolean) stack.pop())
431: .booleanValue();
432:
433: // src element
434: } else if (name.equals(CINCLUDE_SRC_ELEMENT)
435: && this .state == STATE_INCLUDE) {
436:
437: this .stack.push(this .endTextRecording());
438:
439: } else if (name.equals(CINCLUDE_PARAMETERS_ELEMENT)
440: && this .state == STATE_INCLUDE) {
441: this .resourceParameters = new SourceParameters();
442: // Now get the parameters off the stack
443: String label = (String) stack.pop();
444: String key = null;
445: String value = null;
446: while (!label.equals("end")) {
447: if (label.equals("name"))
448: key = (String) stack.pop();
449: if (label.equals("value"))
450: value = (String) stack.pop();
451: if (key != null && value != null) {
452: this .resourceParameters.setParameter(key, value);
453: key = null;
454: value = null;
455: }
456: label = (String) stack.pop();
457: }
458:
459: } else if (name.equals(CINCLUDE_CONFIGURATION_ELEMENT) == true
460: && this .state == STATE_INCLUDE) {
461: this .configurationParameters = new Parameters();
462: // Now get the parameters off the stack
463: String label = (String) stack.pop();
464: String key = null;
465: String value = null;
466: while (!label.equals("end")) {
467: if (label.equals("name"))
468: key = (String) stack.pop();
469: if (label.equals("value"))
470: value = (String) stack.pop();
471: if (key != null && value != null) {
472: this .configurationParameters.setParameter(key,
473: value);
474: key = null;
475: value = null;
476: }
477: label = (String) stack.pop();
478: }
479:
480: } else if (name.equals(CINCLUDE_PARAMETER_ELEMENT) == true
481: && this .state == STATE_INCLUDE) {
482:
483: } else if (name.equals(CINCLUDE_NAME_ELEMENT) == true
484: && this .state == STATE_INCLUDE) {
485: stack.push(this .endTextRecording());
486: stack.push("name");
487:
488: // parameter value
489: } else if (name.equals(CINCLUDE_VALUE_ELEMENT) == true
490: && this .state == STATE_INCLUDE) {
491: stack.push(this .endSerializedXMLRecording());
492: stack.push("value");
493:
494: } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
495: if (this .compiling) {
496: super .endTransformingElement(uri,
497: CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT,
498: raw + "p");
499: }
500: // do nothing else
501: } else {
502: super .endTransformingElement(uri, name, raw);
503: }
504: }
505:
506: protected String processCIncludeElement(String src, String element,
507: String select, String ns, String prefix, boolean stripRoot,
508: boolean cache) throws SAXException, IOException {
509:
510: if (src == null) {
511: throw new SAXException(
512: "Missing 'src' attribute on cinclude:include element");
513: }
514:
515: if (element == null)
516: element = "";
517: if (select == null)
518: select = "";
519: if (ns == null)
520: ns = "";
521: if (prefix == null)
522: prefix = "";
523:
524: if (getLogger().isDebugEnabled()) {
525: getLogger().debug(
526: "Processing include element: src=" + src
527: + ", element=" + element + ", select="
528: + select + ", ns=" + ns + ", prefix="
529: + prefix + ", stripRoot=" + stripRoot
530: + ", caching=" + cache);
531: }
532:
533: if (cache) {
534: src = this .cacheManager.load(src, this .cachingSession);
535:
536: if (this .cachingSession.isParallel()
537: && !this .cachingSession.isPreemptive()) {
538: if (!this .compiling) {
539: this .compiling = true;
540: this .startCompiledXMLRecording();
541: }
542: } else {
543: this .cacheManager.stream(src, this .cachingSession,
544: this .filter);
545: }
546:
547: return src;
548: }
549:
550: // usual no caching stuff
551: if (!"".equals(element)) {
552: if (!ns.equals("")) {
553: super .startPrefixMapping(prefix, ns);
554: }
555: super .startElement(ns, element, (!ns.equals("")
556: && !prefix.equals("") ? prefix + ":" + element
557: : element), XMLUtils.EMPTY_ATTRIBUTES);
558: }
559:
560: Source source = null;
561: try {
562: source = this .resolver.resolveURI(src);
563:
564: if (!"".equals(select)) {
565:
566: DOMParser parser = null;
567: XPathProcessor processor = null;
568:
569: try {
570: parser = (DOMParser) this .manager
571: .lookup(DOMParser.ROLE);
572: processor = (XPathProcessor) this .manager
573: .lookup(XPathProcessor.ROLE);
574:
575: InputSource input = SourceUtil
576: .getInputSource(source);
577:
578: Document document = parser.parseDocument(input);
579: NodeList list = processor.selectNodeList(document,
580: select);
581: int length = list.getLength();
582: for (int i = 0; i < length; i++) {
583: IncludeXMLConsumer.includeNode(list.item(i),
584: this .filter, this .filter);
585: }
586: } finally {
587: this .manager.release(parser);
588: this .manager.release(processor);
589: }
590: } else {
591: String mimeType = null;
592: if (null != this .configurationParameters) {
593: mimeType = this .configurationParameters
594: .getParameter("mime-type", mimeType);
595: }
596: if (this .compiling) {
597: SourceUtil.toSAX(source, mimeType,
598: new IncludeXMLConsumer(this .contentHandler,
599: this .lexicalHandler));
600: } else {
601: this .filter.setIgnoreRootElement(stripRoot);
602: SourceUtil.toSAX(source, mimeType, this .filter);
603: }
604: }
605:
606: } catch (SourceException se) {
607: throw new SAXException("Exception in CIncludeTransformer",
608: se);
609: } catch (IOException e) {
610: throw new SAXException(
611: "CIncludeTransformer could not read resource", e);
612: } catch (ProcessingException e) {
613: throw new SAXException("Exception in CIncludeTransformer",
614: e);
615: } catch (ServiceException e) {
616: throw new SAXException(e);
617: } finally {
618: this .resolver.release(source);
619: }
620:
621: if (!"".equals(element)) {
622: super .endElement(ns, element, (!ns.equals("")
623: && !prefix.equals("") ? prefix + ":" + element
624: : element));
625: if (!ns.equals("")) {
626: super .endPrefixMapping(prefix);
627: }
628: }
629: return src;
630: }
631:
632: /**
633: * Start recording of compiled xml.
634: * The incomming SAX events are recorded and a compiled representation
635: * is created. These events are not forwarded to the next component in
636: * the pipeline.
637: */
638: protected void startCompiledXMLRecording() throws SAXException {
639: if (this .getLogger().isDebugEnabled()) {
640: this .getLogger().debug("BEGIN startCompiledXMLRecording");
641: }
642:
643: try {
644: this .recorder = (XMLSerializer) this .manager
645: .lookup(XMLSerializer.ROLE);
646:
647: this .addRecorder(recorder);
648:
649: } catch (ServiceException ce) {
650: throw new SAXException(
651: "Unable to lookup xml serializer for compiling xml.",
652: ce);
653: }
654: if (this .getLogger().isDebugEnabled()) {
655: this .getLogger().debug("END startCompiledXMLRecording");
656: }
657: }
658:
659: /**
660: * Stop recording of compiled XML.
661: * @return The compiled XML.
662: */
663: protected Object endCompiledXMLRecording() throws SAXException {
664: if (this .getLogger().isDebugEnabled()) {
665: this .getLogger().debug("BEGIN endCompiledXMLRecording");
666: }
667:
668: XMLSerializer recorder = (XMLSerializer) this .removeRecorder();
669: Object text = recorder.getSAXFragment();
670:
671: if (this .getLogger().isDebugEnabled()) {
672: this .getLogger().debug(
673: "END endCompiledXMLRecording text=" + text);
674: }
675: return text;
676: }
677:
678: /**
679: * @see org.xml.sax.ContentHandler#startDocument()
680: */
681: public void startDocument() throws SAXException {
682: this .filter = new MyFilter(this .xmlConsumer, this );
683: super .startDocument();
684: }
685:
686: /**
687: * @see org.xml.sax.ContentHandler#endDocument()
688: */
689: public void endDocument() throws SAXException {
690: if (this .compiling) {
691: Object compiledXML = this .endCompiledXMLRecording();
692: XMLDeserializer deserializer = null;
693: try {
694: deserializer = (XMLDeserializer) this .manager
695: .lookup(XMLDeserializer.ROLE);
696: deserializer.setConsumer(this .filter);
697: deserializer.deserialize(compiledXML);
698: } catch (ServiceException ce) {
699: throw new SAXException(
700: "Unable to lookup xml deserializer.", ce);
701: } finally {
702: this .manager.release(deserializer);
703: }
704: }
705: super .endDocument();
706: }
707:
708: /**
709: * @see org.apache.cocoon.caching.CacheableProcessingComponent#getKey()
710: */
711: public Serializable getKey() {
712: if (this .supportCaching && null != this .cacheManager
713: && this .cachingSession.getExpires() > 0) {
714: return "1";
715: }
716: return null;
717: }
718:
719: /**
720: * @see org.apache.cocoon.caching.CacheableProcessingComponent#getValidity()
721: */
722: public SourceValidity getValidity() {
723: if (this .supportCaching && null != this .cacheManager
724: && this .cachingSession.getExpires() > 0
725: && !this .cachingSession.isPurging()) {
726: return this .cachingSession.getExpiresValidity();
727: }
728: return null;
729: }
730:
731: }
732:
733: final class MyFilter extends IncludeXMLConsumer {
734:
735: private final CIncludeTransformer transformer;
736:
737: /**
738: * This filter class post-processes the parallel fetching
739: * @param consumer
740: */
741: public MyFilter(XMLConsumer consumer,
742: CIncludeTransformer transformer) {
743: super (consumer);
744: this .transformer = transformer;
745: }
746:
747: /**
748: * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
749: */
750: public void endElement(String uri, String local, String qName)
751: throws SAXException {
752: if (uri != null
753: && uri
754: .equals(CIncludeTransformer.CINCLUDE_NAMESPACE_URI)
755: && local
756: .equals(CIncludeTransformer.CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
757: // this is the placeholder element: do nothing
758: } else {
759: super .endElement(uri, local, qName);
760: }
761: }
762:
763: /**
764: * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
765: */
766: public void startElement(String uri, String local, String qName,
767: Attributes attr) throws SAXException {
768: if (uri != null
769: && uri
770: .equals(CIncludeTransformer.CINCLUDE_NAMESPACE_URI)
771: && local
772: .equals(CIncludeTransformer.CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
773: // this is a placeholder
774: try {
775: final String src = attr
776: .getValue(
777: "",
778: CIncludeTransformer.CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE);
779: this .transformer.cacheManager.stream(src,
780: this .transformer.cachingSession, this );
781: } catch (IOException ioe) {
782: throw new SAXException("IOException", ioe);
783: }
784: } else {
785: super.startElement(uri, local, qName, attr);
786: }
787: }
788:
789: }
|