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.component.ComponentException;
020: import org.apache.avalon.framework.component.ComponentManager;
021: import org.apache.avalon.framework.component.Composable;
022: import org.apache.avalon.framework.parameters.Parameters;
023:
024: import org.apache.cocoon.ProcessingException;
025: import org.apache.cocoon.caching.CacheValidity;
026: import org.apache.cocoon.caching.Cacheable;
027: import org.apache.cocoon.caching.IncludeCacheValidity;
028: import org.apache.cocoon.components.source.SourceUtil;
029: import org.apache.cocoon.environment.SourceResolver;
030: import org.apache.cocoon.xml.IncludeXMLConsumer;
031: import org.apache.cocoon.xml.XMLUtils;
032:
033: import org.apache.excalibur.source.Source;
034: import org.xml.sax.Attributes;
035: import org.xml.sax.SAXException;
036:
037: import java.io.IOException;
038: import java.util.Map;
039:
040: /**
041: * <p>This transformer triggers for the element <code>include</code> in the
042: * namespace "http://apache.org/cocoon/include/1.0".
043: * The <code>src</code> attribute contains the url which points to
044: * an xml resource which is include instead of the element.
045: * With the attributes <code>element</code>, <code>ns</code> and
046: * <code>prefix</code> it is possible to specify an element
047: * which surrounds the included content.</p>
048: *
049: * <p>Validity of cached pipelines is calculated not by comparing old and new
050: * IncludeCacheValidity objects (as in AggregatedCacheValidity) but by comparing
051: * timestamps. Validity object of cached pipeline contain two lists: source urls
052: * and timestamps. When it comes to checking validity of cached pipeline we know
053: * that generation/transformation steps before CIncludeTransformer are valid (otherwise
054: * we would have had discarded cached pipeline already) so source url list
055: * of new validity will be the same as of old one. Only timestamps have to be
056: * recalculated and compared.</p>
057: *
058: * @see IncludeTransformer (scratchpad)
059: * @author <a href="mailto:maciejka@tiger.com.pl">Maciek Kaminski</a>
060: * @deprecated This transformer violates the avalon/cocoon design principles. Use IncludeTransformer.
061: * @version $Id: CachingCIncludeTransformer.java 433543 2006-08-22 06:22:54Z crossley $
062: */
063: public class CachingCIncludeTransformer extends AbstractTransformer
064: implements Composable, Cacheable {
065:
066: public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0";
067: public static final String CINCLUDE_INCLUDE_ELEMENT = "include";
068: public static final String CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE = "src";
069: public static final String CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE = "element";
070: public static final String CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE = "ns";
071: public static final String CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE = "prefix";
072:
073: /** The <code>SourceResolver</code> */
074: protected SourceResolver sourceResolver;
075:
076: /** The current <code>ComponentManager</code>. */
077: protected ComponentManager manager = null;
078:
079: /** The current <code>IncludeCacheValidity</code>. */
080: protected IncludeCacheValidity currentCacheValidity;
081:
082: /** The current <code>IncludeXMLConsumer</code> that ommits start and endDocument events. */
083: protected IncludeXMLConsumer consumer;
084:
085: /**
086: * Setup the component.
087: */
088: public void setup(SourceResolver resolver, Map objectModel,
089: String source, Parameters parameters)
090: throws ProcessingException, SAXException, IOException {
091: this .sourceResolver = resolver;
092: }
093:
094: /**
095: * Composable Interface
096: */
097: public final void compose(final ComponentManager manager)
098: throws ComponentException {
099: this .manager = manager;
100: }
101:
102: /**
103: * Recycle the component
104: */
105: public void recycle() {
106: super .recycle();
107: this .sourceResolver = null;
108: this .currentCacheValidity = null;
109: }
110:
111: public void startElement(String uri, String name, String raw,
112: Attributes attr) throws SAXException {
113: if (uri != null && name != null
114: && uri.equals(CINCLUDE_NAMESPACE_URI)
115: && name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
116:
117: this
118: .processCIncludeElement(
119: attr
120: .getValue("",
121: CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
122: attr
123: .getValue("",
124: CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
125: attr
126: .getValue("",
127: CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
128: attr
129: .getValue("",
130: CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE));
131:
132: } else {
133: super .startElement(uri, name, raw, attr);
134: }
135: }
136:
137: public void endElement(String uri, String name, String raw)
138: throws SAXException {
139: if (uri != null && name != null
140: && uri.equals(CINCLUDE_NAMESPACE_URI)
141: && name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
142: return;
143: }
144: super .endElement(uri, name, raw);
145: }
146:
147: public void endDocument() throws SAXException {
148: super .endDocument();
149: if (currentCacheValidity != null) {
150: currentCacheValidity.setIsNew2False();
151: }
152: }
153:
154: protected void processCIncludeElement(String src, String element,
155: String ns, String prefix) throws SAXException {
156:
157: if (element == null)
158: element = "";
159: if (ns == null)
160: ns = "";
161: if (prefix == null)
162: prefix = "";
163:
164: if (this .getLogger().isDebugEnabled()) {
165: getLogger().debug(
166: "Processing CInclude element: src=" + src
167: + ", element=" + element + ", ns=" + ns
168: + ", prefix=" + prefix);
169: }
170:
171: // complete validity information
172: if (currentCacheValidity != null) {
173: Source temp = null;
174: try {
175: temp = sourceResolver.resolveURI(src);
176: currentCacheValidity.add(src, temp.getLastModified());
177: if (this .getLogger().isDebugEnabled()) {
178: getLogger().debug(
179: "currentCacheValidity: "
180: + currentCacheValidity);
181: }
182: } catch (Exception e) {
183: throw new SAXException(
184: "CachingCIncludeTransformer could not resolve resource",
185: e);
186: } finally {
187: sourceResolver.release(temp);
188: }
189: }
190:
191: if (!"".equals(element)) {
192: if (!ns.equals("")) {
193: super .startPrefixMapping(prefix, ns);
194: }
195: super .startElement(ns, element, (!ns.equals("")
196: && !prefix.equals("") ? prefix + ":" + element
197: : element), XMLUtils.EMPTY_ATTRIBUTES);
198: }
199:
200: Source source = null;
201: try {
202: source = this .sourceResolver.resolveURI(src);
203: SourceUtil.parse(this .manager, source, getConsumer());
204: } catch (Exception e) {
205: throw new SAXException(
206: "CachingCIncludeTransformer could not read resource",
207: e);
208: } finally {
209: sourceResolver.release(source);
210: }
211:
212: if (!"".equals(element)) {
213: super .endElement(ns, element, (!ns.equals("")
214: && !prefix.equals("") ? prefix + ":" + element
215: : element));
216: if (!ns.equals("")) {
217: super .endPrefixMapping(prefix);
218: }
219: }
220: }
221:
222: /**
223: * Generate the unique key.
224: * This key must be unique inside the space of this component.
225: * CachingCIncludeTransformer always generates the same key since which documents
226: * are included depends only on former generation/transformation stages.
227: *
228: * @return The generated key hashes the src
229: */
230:
231: public long generateKey() {
232: return 1;
233: }
234:
235: /**
236: * Generate the validity object.
237: * CachingCIncludeTransformer generates "empty" IncludeCacheValidity
238: * and completes it with validity data during transformation.
239: * See processCIncludeElement method.
240: *
241: * @return The generated validity object or <code>null</code> if the
242: * component is currently not cacheable.
243: */
244:
245: public CacheValidity generateValidity() {
246:
247: try {
248: currentCacheValidity = new IncludeCacheValidity(
249: sourceResolver);
250: return currentCacheValidity;
251: } catch (RuntimeException e) {
252: getLogger()
253: .warn(
254: "CachingCIncludeTransformer: could not generateKey",
255: e);
256: return null;
257: }
258: }
259:
260: protected IncludeXMLConsumer getConsumer() {
261: if (consumer == null) {
262: consumer = new IncludeXMLConsumer(this);
263: }
264: return consumer;
265: }
266: }
|