| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.transformation; |
| |
| import org.apache.avalon.framework.component.ComponentException; |
| import org.apache.avalon.framework.component.ComponentManager; |
| import org.apache.avalon.framework.component.Composable; |
| import org.apache.avalon.framework.parameters.Parameters; |
| |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.caching.CacheValidity; |
| import org.apache.cocoon.caching.Cacheable; |
| import org.apache.cocoon.caching.IncludeCacheValidity; |
| import org.apache.cocoon.components.source.SourceUtil; |
| import org.apache.cocoon.environment.SourceResolver; |
| import org.apache.cocoon.xml.IncludeXMLConsumer; |
| import org.apache.cocoon.xml.XMLUtils; |
| |
| import org.apache.excalibur.source.Source; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| |
| /** |
| * <p>This transformer triggers for the element <code>include</code> in the |
| * namespace "http://apache.org/cocoon/include/1.0". |
| * The <code>src</code> attribute contains the url which points to |
| * an xml resource which is include instead of the element. |
| * With the attributes <code>element</code>, <code>ns</code> and |
| * <code>prefix</code> it is possible to specify an element |
| * which surrounds the included content.</p> |
| * |
| * <p>Validity of cached pipelines is calculated not by comparing old and new |
| * IncludeCacheValidity objects (as in AggregatedCacheValidity) but by comparing |
| * timestamps. Validity object of cached pipeline contain two lists: source urls |
| * and timestamps. When it comes to checking validity of cached pipeline we know |
| * that generation/transformation steps before CIncludeTransformer are valid (otherwise |
| * we would have had discarded cached pipeline already) so source url list |
| * of new validity will be the same as of old one. Only timestamps have to be |
| * recalculated and compared.</p> |
| * |
| * @see IncludeTransformer (scratchpad) |
| * @author <a href="mailto:maciejka@tiger.com.pl">Maciek Kaminski</a> |
| * @deprecated This transformer violates the avalon/cocoon design principles. Use IncludeTransformer. |
| * @version $Id$ |
| */ |
| public class CachingCIncludeTransformer extends AbstractTransformer |
| implements Composable, Cacheable { |
| |
| public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0"; |
| public static final String CINCLUDE_INCLUDE_ELEMENT = "include"; |
| public static final String CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE = "src"; |
| public static final String CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE = "element"; |
| public static final String CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE = "ns"; |
| public static final String CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE = "prefix"; |
| |
| /** The <code>SourceResolver</code> */ |
| protected SourceResolver sourceResolver; |
| |
| /** The current <code>ComponentManager</code>. */ |
| protected ComponentManager manager = null; |
| |
| /** The current <code>IncludeCacheValidity</code>. */ |
| protected IncludeCacheValidity currentCacheValidity; |
| |
| /** The current <code>IncludeXMLConsumer</code> that ommits start and endDocument events. */ |
| protected IncludeXMLConsumer consumer; |
| |
| /** |
| * Setup the component. |
| */ |
| public void setup(SourceResolver resolver, Map objectModel, |
| String source, Parameters parameters) |
| throws ProcessingException, SAXException, IOException { |
| this.sourceResolver = resolver; |
| } |
| |
| /** |
| * Composable Interface |
| */ |
| public final void compose(final ComponentManager manager) throws ComponentException { |
| this.manager = manager; |
| } |
| |
| /** |
| * Recycle the component |
| */ |
| public void recycle() { |
| super.recycle(); |
| this.sourceResolver = null; |
| this.currentCacheValidity = null; |
| } |
| |
| public void startElement(String uri, String name, String raw, Attributes attr) |
| throws SAXException { |
| if (uri != null && name != null |
| && uri.equals(CINCLUDE_NAMESPACE_URI) |
| && name.equals(CINCLUDE_INCLUDE_ELEMENT)) { |
| |
| this.processCIncludeElement(attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE), |
| attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE), |
| attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE), |
| attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE)); |
| |
| } else { |
| super.startElement(uri, name, raw, attr); |
| } |
| } |
| |
| public void endElement(String uri, String name, String raw) throws SAXException { |
| if (uri != null && name != null |
| && uri.equals(CINCLUDE_NAMESPACE_URI) |
| && name.equals(CINCLUDE_INCLUDE_ELEMENT)) { |
| return; |
| } |
| super.endElement(uri, name, raw); |
| } |
| |
| public void endDocument() |
| throws SAXException { |
| super.endDocument(); |
| if(currentCacheValidity != null) { |
| currentCacheValidity.setIsNew2False(); |
| } |
| } |
| |
| protected void processCIncludeElement(String src, String element, String ns, String prefix) |
| throws SAXException { |
| |
| if (element == null) element=""; |
| if (ns == null) ns=""; |
| if (prefix == null) prefix=""; |
| |
| if (this.getLogger().isDebugEnabled()) { |
| getLogger().debug("Processing CInclude element: src=" + src |
| + ", element=" + element |
| + ", ns=" + ns |
| + ", prefix=" + prefix); |
| } |
| |
| // complete validity information |
| if(currentCacheValidity != null ) { |
| Source temp = null; |
| try { |
| temp = sourceResolver.resolveURI(src); |
| currentCacheValidity.add(src, temp.getLastModified()); |
| if (this.getLogger().isDebugEnabled()) { |
| getLogger().debug("currentCacheValidity: " + currentCacheValidity); |
| } |
| } catch (Exception e) { |
| throw new SAXException("CachingCIncludeTransformer could not resolve resource", e); |
| } finally { |
| sourceResolver.release(temp); |
| } |
| } |
| |
| if (!"".equals(element)) { |
| if (!ns.equals("")) { |
| super.startPrefixMapping(prefix, ns); |
| } |
| super.startElement(ns, |
| element, |
| (!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element), |
| XMLUtils.EMPTY_ATTRIBUTES); |
| } |
| |
| Source source = null; |
| try { |
| source = this.sourceResolver.resolveURI(src); |
| SourceUtil.parse(this.manager, source, getConsumer()); |
| } catch (Exception e) { |
| throw new SAXException("CachingCIncludeTransformer could not read resource", e); |
| } finally { |
| sourceResolver.release(source); |
| } |
| |
| if (!"".equals(element)) { |
| super.endElement(ns, element, (!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element)); |
| if (!ns.equals("")) { |
| super.endPrefixMapping(prefix); |
| } |
| } |
| } |
| |
| /** |
| * Generate the unique key. |
| * This key must be unique inside the space of this component. |
| * CachingCIncludeTransformer always generates the same key since which documents |
| * are included depends only on former generation/transformation stages. |
| * |
| * @return The generated key hashes the src |
| */ |
| |
| public long generateKey() { |
| return 1; |
| } |
| |
| /** |
| * Generate the validity object. |
| * CachingCIncludeTransformer generates "empty" IncludeCacheValidity |
| * and completes it with validity data during transformation. |
| * See processCIncludeElement method. |
| * |
| * @return The generated validity object or <code>null</code> if the |
| * component is currently not cacheable. |
| */ |
| |
| public CacheValidity generateValidity() { |
| |
| try { |
| currentCacheValidity = new IncludeCacheValidity(sourceResolver); |
| return currentCacheValidity; |
| } catch (RuntimeException e) { |
| getLogger().warn("CachingCIncludeTransformer: could not generateKey", e); |
| return null; |
| } |
| } |
| |
| protected IncludeXMLConsumer getConsumer() { |
| if(consumer == null) { |
| consumer = new IncludeXMLConsumer(this); |
| } |
| return consumer; |
| } |
| } |