You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2014/08/27 14:31:47 UTC
[2/5] git commit: CAMEL-7753: xslt component - Store warning/errors
etc as exchange properties so end users can get hold of those. Now works with
camel-saxon also.
CAMEL-7753: xslt component - Store warning/errors etc as exchange properties so end users can get hold of those. Now works with camel-saxon also.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1b38aad3
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1b38aad3
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1b38aad3
Branch: refs/heads/master
Commit: 1b38aad342529e98b5f76e11029ec50b1768949f
Parents: 68d0f14
Author: Claus Ibsen <da...@apache.org>
Authored: Wed Aug 27 13:52:58 2014 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Wed Aug 27 13:52:58 2014 +0200
----------------------------------------------------------------------
camel-core/pom.xml | 2 +
.../apache/camel/builder/xml/XsltBuilder.java | 92 ++++++++++++++++----
.../camel/component/xslt/XsltEndpoint.java | 12 ++-
.../camel-saxon/src/test/data/terminate.xml | 36 ++++++++
.../xslt/SaxonXsltMessageTerminateTest.java | 60 +++++++++++++
.../apache/camel/component/xslt/terminate.xsl | 34 ++++++++
6 files changed, 217 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/camel-core/pom.xml
----------------------------------------------------------------------
diff --git a/camel-core/pom.xml b/camel-core/pom.xml
index 98f6ad8..4c13e0b 100644
--- a/camel-core/pom.xml
+++ b/camel-core/pom.xml
@@ -49,6 +49,8 @@
javax.xml.bind.annotation.adapters;resolution:=optional,
javax.xml.stream;resolution:=optional,
javax.xml.transform.stax;resolution:=optional,
+ net.sf.saxon.event;resolution:=optional,
+ net.sf.saxon.serialize;resolution:=optional,
*
</camel.osgi.import>
<camel.osgi.export.service>
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java b/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java
index 3803def..8ccc9b6 100644
--- a/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java
+++ b/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java
@@ -19,13 +19,13 @@ package org.apache.camel.builder.xml;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
-
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.ErrorListener;
@@ -41,8 +41,8 @@ import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamSource;
-import org.w3c.dom.Node;
-
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
import org.apache.camel.Exchange;
import org.apache.camel.ExpectedBodyTypeException;
import org.apache.camel.Message;
@@ -51,13 +51,15 @@ import org.apache.camel.RuntimeTransformException;
import org.apache.camel.TypeConverter;
import org.apache.camel.converter.jaxp.StaxSource;
import org.apache.camel.converter.jaxp.XmlConverter;
-import org.apache.camel.converter.jaxp.XmlErrorListener;
+import org.apache.camel.support.ServiceSupport;
import org.apache.camel.support.SynchronizationAdapter;
import org.apache.camel.util.ExchangeHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
import static org.apache.camel.util.ObjectHelper.notNull;
@@ -70,8 +72,9 @@ import static org.apache.camel.util.ObjectHelper.notNull;
*
* @version
*/
-public class XsltBuilder implements Processor {
+public class XsltBuilder extends ServiceSupport implements Processor, CamelContextAware {
private static final Logger LOG = LoggerFactory.getLogger(XsltBuilder.class);
+ private CamelContext camelContext;
private Map<String, Object> parameters = new HashMap<String, Object>();
private XmlConverter converter = new XmlConverter();
private Templates template;
@@ -80,8 +83,11 @@ public class XsltBuilder implements Processor {
private boolean failOnNullBody = true;
private URIResolver uriResolver;
private boolean deleteOutputFile;
- private ErrorListener errorListener = new XsltErrorListener();
+ private ErrorListener errorListener;
private boolean allowStAX = true;
+ private volatile Method setMessageEmitterMethod;
+ private volatile Class<?> saxonReceiverClass;
+ private volatile Class<?> saxonWarnerClass;
public XsltBuilder() {
}
@@ -106,7 +112,7 @@ public class XsltBuilder implements Processor {
Transformer transformer = getTransformer();
configureTransformer(transformer, exchange);
- transformer.setErrorListener(new DefaultTransformErrorHandler(exchange));
+
ResultHandler resultHandler = resultHandlerFactory.createResult(exchange);
Result result = resultHandler.getResult();
exchange.setProperty("isXalanTransformer", isXalanTransformer(transformer));
@@ -141,6 +147,10 @@ public class XsltBuilder implements Processor {
return transformer.getClass().getName().startsWith("org.apache.xalan.transformer");
}
+ boolean isSaxonTransformer(Transformer transformer) {
+ return transformer.getClass().getName().startsWith("net.sf.saxon");
+ }
+
// Builder methods
// -------------------------------------------------------------------------
@@ -329,7 +339,8 @@ public class XsltBuilder implements Processor {
*/
public void setTransformerSource(Source source) throws TransformerConfigurationException {
TransformerFactory factory = converter.getTransformerFactory();
- factory.setErrorListener(errorListener);
+ // use a logger error listener so users can see from the logs what the error may be
+ factory.setErrorListener(new XsltErrorListener());
if (getUriResolver() != null) {
factory.setURIResolver(getUriResolver());
}
@@ -407,22 +418,35 @@ public class XsltBuilder implements Processor {
private void releaseTransformer(Transformer transformer) {
if (transformers != null) {
transformer.reset();
- transformer.setErrorListener(errorListener);
transformers.offer(transformer);
}
}
- private Transformer getTransformer() throws TransformerConfigurationException {
+ private Transformer getTransformer() throws Exception {
Transformer t = null;
if (transformers != null) {
t = transformers.poll();
}
if (t == null) {
- t = getTemplate().newTransformer();
+ t = createTransformer();
}
return t;
}
+ protected Transformer createTransformer() throws Exception {
+ Transformer t = getTemplate().newTransformer();
+
+ // special for saxon as we need to call setMessageEmitter on the transformer to hook from saxon to the JAXP errorListener
+ // so we can get notified if any errors happen during transformation
+ // see details at: https://stackoverflow.com/questions/4695489/capture-xslmessage-output-in-java
+ if (isSaxonTransformer(t) && setMessageEmitterMethod != null) {
+ Object warner = getCamelContext().getInjector().newInstance(saxonWarnerClass);
+ setMessageEmitterMethod.invoke(t, warner);
+ }
+
+ return t;
+ }
+
/**
* Checks whether we need an {@link InputStream} to access the message body.
* <p/>
@@ -525,19 +549,23 @@ public class XsltBuilder implements Processor {
/**
* Configures the transformer with exchange specific parameters
*/
- protected void configureTransformer(Transformer transformer, Exchange exchange) {
+ protected void configureTransformer(Transformer transformer, Exchange exchange) throws Exception {
if (uriResolver == null) {
uriResolver = new XsltUriResolver(exchange.getContext().getClassResolver(), null);
}
transformer.setURIResolver(uriResolver);
- transformer.setErrorListener(new XmlErrorListener());
+ if (errorListener == null) {
+ // set our error listener so we can capture errors and report them back on the exchange
+ transformer.setErrorListener(new DefaultTransformErrorHandler(exchange));
+ } else {
+ // use custom error listener
+ transformer.setErrorListener(errorListener);
+ }
transformer.clearParameters();
-
addParameters(transformer, exchange.getProperties());
addParameters(transformer, exchange.getIn().getHeaders());
addParameters(transformer, getParameters());
-
transformer.setParameter("exchange", exchange);
transformer.setParameter("in", exchange.getIn());
transformer.setParameter("out", exchange.getOut());
@@ -555,6 +583,40 @@ public class XsltBuilder implements Processor {
}
}
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ ObjectHelper.notNull(camelContext, "camelContext", this);
+
+ // create a transformer to see if its saxon, as we then need to do some initial preparation
+ Transformer t = getTemplate().newTransformer();
+
+ if (isSaxonTransformer(t)) {
+ // pre-load saxon classes as we need to call the setMessageEmitter on the transformer to hook saxon to use the JAXP
+ // error listener, so we can capture errors and xsl:message outputs which end users may define in the xslt files
+ try {
+ saxonReceiverClass = getCamelContext().getClassResolver().resolveMandatoryClass("net.sf.saxon.event.Receiver");
+ saxonWarnerClass = getCamelContext().getClassResolver().resolveMandatoryClass("net.sf.saxon.serialize.MessageWarner");
+ setMessageEmitterMethod = t.getClass().getMethod("setMessageEmitter", saxonReceiverClass);
+ } catch (Exception e) {
+ throw new IllegalStateException("Error pre-loading Saxon classes. Make sure you have saxon on the classpath,"
+ + " and the classloader can load the following two classes: net.sf.saxon.event.Receiver, net.sf.saxon.serialize.MessageWarner.", e);
+ }
+ }
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ // noop
+ }
+
private static final class XsltBuilderOnCompletion extends SynchronizationAdapter {
private final String fileName;
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/camel-core/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java b/camel-core/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
index 240118a..ea0fa26 100644
--- a/camel-core/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
+++ b/camel-core/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
@@ -17,7 +17,6 @@
package org.apache.camel.component.xslt;
import java.io.IOException;
-
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
@@ -30,6 +29,7 @@ import org.apache.camel.builder.xml.XsltBuilder;
import org.apache.camel.impl.ProcessorEndpoint;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
+import org.apache.camel.util.ServiceHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -87,19 +87,16 @@ public class XsltEndpoint extends ProcessorEndpoint {
@Override
protected void onExchange(Exchange exchange) throws Exception {
-
if (!cacheStylesheet || cacheCleared) {
loadResource(resourceUri);
}
super.onExchange(exchange);
-
}
/**
* Loads the resource.
*
* @param resourceUri the resource to load
- *
* @throws TransformerException is thrown if error loading resource
* @throws IOException is thrown if error loading resource
*/
@@ -118,11 +115,18 @@ public class XsltEndpoint extends ProcessorEndpoint {
@Override
protected void doStart() throws Exception {
super.doStart();
+
+ // must load resource first which sets a template and do a stylesheet compilation to catch errors early
loadResource(resourceUri);
+
+ // and then inject camel context and start service
+ xslt.setCamelContext(getCamelContext());
+ ServiceHelper.startService(xslt);
}
@Override
protected void doStop() throws Exception {
super.doStop();
+ ServiceHelper.stopService(xslt);
}
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/components/camel-saxon/src/test/data/terminate.xml
----------------------------------------------------------------------
diff --git a/components/camel-saxon/src/test/data/terminate.xml b/components/camel-saxon/src/test/data/terminate.xml
new file mode 100644
index 0000000..6a9b5cb
--- /dev/null
+++ b/components/camel-saxon/src/test/data/terminate.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<staff>
+
+ <programmer>
+ <name>Bugs Bunny</name>
+ <dob>03/21/1970</dob>
+ <age>31</age>
+ <address>4895 Wabbit Hole Road</address>
+ <phone>865-111-1111</phone>
+ </programmer>
+
+ <programmer>
+ <name>Daisy Duck</name>
+ <dob></dob>
+ <age></age>
+ <address>748 Golden Pond</address>
+ <phone>865-222-2222</phone>
+ </programmer>
+
+</staff>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/components/camel-saxon/src/test/java/org/apache/camel/component/xslt/SaxonXsltMessageTerminateTest.java
----------------------------------------------------------------------
diff --git a/components/camel-saxon/src/test/java/org/apache/camel/component/xslt/SaxonXsltMessageTerminateTest.java b/components/camel-saxon/src/test/java/org/apache/camel/component/xslt/SaxonXsltMessageTerminateTest.java
new file mode 100644
index 0000000..62d5d15
--- /dev/null
+++ b/components/camel-saxon/src/test/java/org/apache/camel/component/xslt/SaxonXsltMessageTerminateTest.java
@@ -0,0 +1,60 @@
+/**
+ * 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.camel.component.xslt;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class SaxonXsltMessageTerminateTest extends CamelTestSupport {
+
+ @Test
+ public void testXsltTerminate() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(0);
+ getMockEndpoint("mock:dead").expectedMessageCount(1);
+
+ assertMockEndpointsSatisfied();
+
+ Exchange out = getMockEndpoint("mock:dead").getReceivedExchanges().get(0);
+ assertNotNull(out);
+ // this exception is just a generic xslt error
+ Exception cause = out.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
+ assertNotNull(cause);
+
+ // we have the xsl termination message as a error property on the exchange as we set terminate=true
+ Exception error = out.getProperty(Exchange.XSLT_ERROR, Exception.class);
+ assertNotNull(error);
+ assertEquals("Error: DOB is an empty string!", error.getMessage());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ errorHandler(deadLetterChannel("mock:dead"));
+
+ from("file:src/test/data/?fileName=terminate.xml&noop=true")
+ .to("xslt:org/apache/camel/component/xslt/terminate.xsl?saxon=true")
+ .to("log:foo")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1b38aad3/components/camel-saxon/src/test/resources/org/apache/camel/component/xslt/terminate.xsl
----------------------------------------------------------------------
diff --git a/components/camel-saxon/src/test/resources/org/apache/camel/component/xslt/terminate.xsl b/components/camel-saxon/src/test/resources/org/apache/camel/component/xslt/terminate.xsl
new file mode 100644
index 0000000..d4d9860
--- /dev/null
+++ b/components/camel-saxon/src/test/resources/org/apache/camel/component/xslt/terminate.xsl
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:template match="/">
+ <html>
+ <body>
+ <xsl:for-each select="staff/programmer">
+ <p>Name: <xsl:value-of select="name"/><br />
+ <xsl:if test="dob=''">
+ <xsl:message terminate="yes">Error: DOB is an empty string!</xsl:message>
+ </xsl:if>
+ </p>
+ </xsl:for-each>
+ </body>
+ </html>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file