You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by il...@apache.org on 2013/02/25 14:56:09 UTC

svn commit: r1449709 - in /cocoon/cocoon3/trunk/cocoon-sax/src: main/java/org/apache/cocoon/sax/component/XSLTTransformer.java test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java

Author: ilgrosso
Date: Mon Feb 25 13:56:08 2013
New Revision: 1449709

URL: http://svn.apache.org/r1449709
Log:
[COCOON3-72] #resolve

Modified:
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XSLTTransformer.java
    cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java

Modified: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XSLTTransformer.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XSLTTransformer.java?rev=1449709&r1=1449708&r2=1449709&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XSLTTransformer.java (original)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XSLTTransformer.java Mon Feb 25 13:56:08 2013
@@ -19,8 +19,8 @@ package org.apache.cocoon.sax.component;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.regex.Pattern;
+import javax.xml.transform.Source;
 import javax.xml.transform.Templates;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerConfigurationException;
@@ -33,6 +33,7 @@ import org.apache.cocoon.pipeline.SetupE
 import org.apache.cocoon.pipeline.caching.CacheKey;
 import org.apache.cocoon.pipeline.caching.CompoundCacheKey;
 import org.apache.cocoon.pipeline.caching.ParameterCacheKey;
+import org.apache.cocoon.pipeline.caching.TimestampSourceCacheKey;
 import org.apache.cocoon.pipeline.caching.TimestampURLCacheKey;
 import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
 import org.apache.cocoon.pipeline.util.StringRepresentation;
@@ -67,20 +68,26 @@ public class XSLTTransformer extends Abs
      */
     private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
 
+    private static final String SOURCE = "source";
+
     /**
      * The XSLT parameters reference.
      */
     private Map<String, Object> parameters;
 
     /**
-     * The XSLT URL source.
+     * The XSLT Template reference.
      */
-    private URL source;
+    private Templates templates;
 
     /**
-     * The XSLT Template reference.
+     * The XSLT URL source.
      */
-    private Templates templates;
+    private URL url;
+
+    private long lastModified;
+
+    private Source source;
 
     /**
      * Empty constructor, used in sitemap.
@@ -92,10 +99,10 @@ public class XSLTTransformer extends Abs
     /**
      * Creates a new transformer reading the XSLT from the URL source.
      *
-     * @param source the XSLT URL source
+     * @param url the XSLT URL source
      */
-    public XSLTTransformer(final URL source) {
-        this(source, null);
+    public XSLTTransformer(final URL url) {
+        this(url, null);
     }
 
     /**
@@ -104,12 +111,37 @@ public class XSLTTransformer extends Abs
      * This constructor is useful when users want to perform XSLT transformation using <a
      * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
      *
+     * @param url the XSLT URL source
+     * @param attributes the Transformer Factory attributes
+     */
+    public XSLTTransformer(final URL url, final Map<String, Object> attributes) {
+        super();
+        this.load(url, attributes);
+    }
+
+    /**
+     * Creates a new transformer reading the XSLT from the source.
+     *
      * @param source the XSLT URL source
+     * @param lastModified timestamp
+     */
+    public XSLTTransformer(final Source source, final long lastModified) {
+        this(source, lastModified, null);
+    }
+
+    /**
+     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
+     *
+     * This constructor is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param lastModified timestamp
      * @param attributes the Transformer Factory attributes
      */
-    public XSLTTransformer(final URL source, final Map<String, Object> attributes) {
+    public XSLTTransformer(final Source source, final long lastModified, final Map<String, Object> attributes) {
         super();
-        this.loadXSLT(source, attributes);
+        this.load(source, lastModified, attributes);
     }
 
     /**
@@ -119,54 +151,78 @@ public class XSLTTransformer extends Abs
      * This method is useful when users want to perform XSLT transformation using <a
      * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
      *
-     * @param source the XSLT URL source
+     * @param url the XSLT URL source
      * @param attributes the Transformer Factory attributes
      */
-    private void loadXSLT(final URL source, final Map<String, Object> attributes) {
+    private void load(final URL url, final Map<String, Object> attributes) {
+        if (url == null) {
+            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
+        }
+
+        this.url = url;
+        this.lastModified = URLConnectionUtils.getLastModified(this.url);
+
+        this.load(new StreamSource(this.url.toExternalForm()), this.url.toExternalForm(), attributes);
+    }
+
+    /**
+     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
+     * Factory attributes.
+     *
+     * This method is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param lastModified timestamp
+     * @param attributes the Transformer Factory attributes
+     */
+    private void load(final Source source, final long lastModified, final Map<String, Object> attributes) {
         if (source == null) {
             throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
         }
 
         this.source = source;
+        this.lastModified = lastModified;
 
-        Long lastModified = URLConnectionUtils.getLastModified(this.source);
+        this.load(this.source, this.source.toString(), attributes);
+    }
 
-        this.templates = null;
+    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
         // check the XSLT is in the cache first
-        if (XSLT_CACHE.containsKey(this.source.toExternalForm())) {
+        if (XSLT_CACHE.containsKey(localCacheKey)) {
             // get the XSLT directly from the cache
-            ValidityValue<Templates> cacheEntry = XSLT_CACHE.get(this.source.toExternalForm());
-            if (lastModified == null || cacheEntry.getLastModified() >= lastModified) {
-                LOG.debug("{} local cache hit: {}", getClass().getSimpleName(), this.source.toExternalForm());
+            final ValidityValue<Templates> cacheEntry = XSLT_CACHE.get(localCacheKey);
+            if (cacheEntry.getLastModified() >= lastModified) {
+                LOG.debug("{} local cache hit: {}", getClass().getSimpleName(), localCacheKey);
 
                 this.templates = cacheEntry.getValue();
             }
         }
         if (this.templates == null) {
-            LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), this.source.toExternalForm());
+            LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
 
             // XSLT has to be parsed
-            SAXTransformerFactory transformerFactory;
-            if (attributes != null && !attributes.isEmpty()) {
+            final SAXTransformerFactory transformerFactory;
+            if (attributes == null || attributes.isEmpty()) {
+                transformerFactory = TRAX_FACTORY;
+            } else {
                 transformerFactory = createNewSAXTransformerFactory();
-                for (Entry<String, Object> attribute : attributes.entrySet()) {
+                for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
                     transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
                 }
-            } else {
-                transformerFactory = TRAX_FACTORY;
             }
 
             try {
-                this.templates = transformerFactory.newTemplates(new StreamSource(this.source.toExternalForm()));
+                this.templates = transformerFactory.newTemplates(source);
 
                 // store the XSLT into the cache for future reuse
-                LOG.debug("{} local cache put: {}", getClass().getSimpleName(), this.source.toExternalForm());
+                LOG.debug("{} local cache put: {}", getClass().getSimpleName(), localCacheKey);
 
-                ValidityValue<Templates> cacheEntry = new ValidityValue<Templates>(this.templates, lastModified);
-                XSLT_CACHE.put(this.source.toExternalForm(), cacheEntry);
+                final ValidityValue<Templates> cacheEntry =
+                        new ValidityValue<Templates>(this.templates, this.lastModified);
+                XSLT_CACHE.put(localCacheKey, cacheEntry);
             } catch (TransformerConfigurationException e) {
-                throw new SetupException("Impossible to read XSLT from '" + this.source.toExternalForm()
-                        + "', see nested exception", e);
+                throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
             }
         }
     }
@@ -177,10 +233,10 @@ public class XSLTTransformer extends Abs
      * @param parameters the XSLT parameters to be applied to XSLT stylesheet
      */
     public void setParameters(final Map<String, ? extends Object> parameters) {
-        if (parameters != null) {
-            this.parameters = new HashMap<String, Object>(parameters);
-        } else {
+        if (parameters == null) {
             this.parameters = null;
+        } else {
+            this.parameters = new HashMap<String, Object>(parameters);
         }
     }
 
@@ -190,23 +246,23 @@ public class XSLTTransformer extends Abs
     @Override
     public void setConfiguration(final Map<String, ? extends Object> configuration) {
         try {
-            this.source = (URL) configuration.get("source");
+            this.url = (URL) configuration.get(SOURCE);
         } catch (ClassCastException cce) {
-            throw new SetupException("The configuration value of 'source' can't be cast to java.net.URL.", cce);
+            throw new SetupException(
+                    "The configuration value of '" + SOURCE + "' can't be cast to " + URL.class.getName(), cce);
         }
 
-        if (this.source != null) {
-            Object attributesObj = configuration.get("attributes");
-            if (attributesObj != null && attributesObj instanceof Map) {
+        if (this.url == null) {
+            LOG.debug("Impossible to load XSLT parameters from null source");
+        } else {
+            final Object attributesObj = configuration.get("attributes");
+            if (attributesObj instanceof Map) {
                 @SuppressWarnings("unchecked")
-                Map<String, Object> attributesMap = (Map<String, Object>) attributesObj;
-                this.loadXSLT(this.source, attributesMap);
+                final Map<String, Object> attributesMap = (Map<String, Object>) attributesObj;
+                this.load(this.url, attributesMap);
             } else {
-                this.loadXSLT(this.source, null);
+                this.load(this.url, null);
             }
-        } else {
-            LOG.debug("Impossible to load XSLT parameters from '{}' source, "
-                    + "make sure it is NOT null and is a valid URL", this.source.toExternalForm());
         }
 
         this.setParameters(configuration);
@@ -227,8 +283,8 @@ public class XSLTTransformer extends Abs
         if (this.parameters != null) {
             final Transformer transformer = transformerHandler.getTransformer();
 
-            for (Entry<String, Object> entry : this.parameters.entrySet()) {
-                String name = entry.getKey();
+            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
+                final String name = entry.getKey();
 
                 // is valid XSLT parameter name
                 if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
@@ -243,10 +299,11 @@ public class XSLTTransformer extends Abs
         result.setLexicalHandler(consumer);
         transformerHandler.setResult(result);
 
-        TraxErrorListener traxErrorListener = new TraxErrorListener(LOG, this.source.toExternalForm());
+        final TraxErrorListener traxErrorListener = new TraxErrorListener(LOG,
+                this.url == null ? this.source.toString() : this.url.toExternalForm());
         transformerHandler.getTransformer().setErrorListener(traxErrorListener);
 
-        SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
+        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
         saxConsumerAdapter.setContentHandler(transformerHandler);
         super.setSAXConsumer(saxConsumerAdapter);
     }
@@ -258,12 +315,14 @@ public class XSLTTransformer extends Abs
      */
     @Override
     public CacheKey constructCacheKey() {
-        if (this.source == null) {
+        if (this.url == null && this.source == null) {
             throw new SetupException(this.getClass().getSimpleName() + " has no source.");
         }
 
         final CompoundCacheKey cacheKey = new CompoundCacheKey();
-        cacheKey.addCacheKey(new TimestampURLCacheKey(this.source, URLConnectionUtils.getLastModified(this.source)));
+        cacheKey.addCacheKey(this.url == null
+                ? new TimestampSourceCacheKey(this.source, this.lastModified)
+                : new TimestampURLCacheKey(this.url, this.lastModified));
         cacheKey.addCacheKey(ParameterCacheKey.getSitemapSafeInstance(this.parameters));
 
         return cacheKey;
@@ -280,6 +339,8 @@ public class XSLTTransformer extends Abs
 
     @Override
     public String toString() {
-        return StringRepresentation.buildString(this, "src=" + this.source);
+        return StringRepresentation.buildString(this, "src=" + (this.url == null
+                ? "<" + this.source + "," + this.lastModified + ">"
+                : this.url.toExternalForm()));
     }
 }

Modified: cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java?rev=1449709&r1=1449708&r2=1449709&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java (original)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XSLTTransformerTest.java Mon Feb 25 13:56:08 2013
@@ -16,25 +16,38 @@
  */
 package org.apache.cocoon.sax.component;
 
-import static junit.framework.Assert.*;
+import static org.junit.Assert.assertFalse;
 
 import java.io.ByteArrayOutputStream;
 import java.util.Collections;
+import javax.xml.transform.stream.StreamSource;
 import org.apache.cocoon.pipeline.CachingPipeline;
 import org.apache.cocoon.pipeline.caching.Cache;
 import org.apache.cocoon.pipeline.caching.SimpleCache;
+import org.apache.cocoon.pipeline.util.URLConnectionUtils;
 import org.apache.cocoon.sax.SAXPipelineComponent;
 import org.junit.Test;
 
 public class XSLTTransformerTest {
 
-    private CachingPipeline<SAXPipelineComponent> makePipeline(final Cache cache, final String paramValue) {
-        CachingPipeline<SAXPipelineComponent> pipeline = new CachingPipeline<SAXPipelineComponent>();
+    private CachingPipeline<SAXPipelineComponent> makeURLPipeline(final Cache cache, final String paramValue) {
+        return makePipelineInternal(cache, paramValue, new XSLTTransformer(getClass().getResource("/test.xslt")));
+    }
+
+    private CachingPipeline<SAXPipelineComponent> makeSourcePipeline(final Cache cache, final String paramValue) {
+        return makePipelineInternal(cache, paramValue, new XSLTTransformer(
+                new StreamSource(getClass().getResourceAsStream("/test.xslt")),
+                URLConnectionUtils.getLastModified(getClass().getResource("/test.xslt"))));
+    }
+
+    private CachingPipeline<SAXPipelineComponent> makePipelineInternal(final Cache cache, final String paramValue,
+            final XSLTTransformer xslt) {
+
+        final CachingPipeline<SAXPipelineComponent> pipeline = new CachingPipeline<SAXPipelineComponent>();
         pipeline.setCache(cache);
 
         pipeline.addComponent(new XMLGenerator(getClass().getResource("/test.xml")));
 
-        XSLTTransformer xslt = new XSLTTransformer(getClass().getResource("/test.xslt"));
         xslt.setParameters(Collections.singletonMap("myParam", paramValue));
         pipeline.addComponent(xslt);
 
@@ -43,22 +56,28 @@ public class XSLTTransformerTest {
         return pipeline;
     }
 
-    @Test
-    public void cache() throws Exception {
-        Cache cache = new SimpleCache();
+    private void cacheInternal(final CachingPipeline<SAXPipelineComponent> pipeline1,
+            final CachingPipeline<SAXPipelineComponent> pipeline2) throws Exception {
 
-        CachingPipeline<SAXPipelineComponent> pipeline1 = makePipeline(cache, "value1");
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         pipeline1.setup(baos);
         pipeline1.execute();
-        String result1 = new String(baos.toByteArray(), "UTF-8");
+        final String result1 = new String(baos.toByteArray(), "UTF-8");
 
-        CachingPipeline<SAXPipelineComponent> pipeline2 = makePipeline(cache, "value2");
         baos = new ByteArrayOutputStream();
         pipeline2.setup(baos);
         pipeline2.execute();
-        String result2 = new String(baos.toByteArray(), "UTF-8");
+        final String result2 = new String(baos.toByteArray(), "UTF-8");
 
         assertFalse("Pipeline caching is not working as expected", result1.equals(result2));
     }
+
+    @Test
+    public void cache() throws Exception {
+        Cache cache = new SimpleCache();
+        cacheInternal(makeURLPipeline(cache, "value1"), makeURLPipeline(cache, "value2"));
+
+        cache = new SimpleCache();
+        cacheInternal(makeSourcePipeline(cache, "value1"), makeSourcePipeline(cache, "value2"));
+    }
 }