You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2013/09/18 06:00:23 UTC
[2/3] git commit: CAMEL-6440 Fixed the issue of data loss on xpath
after cxf(payload-mode)
CAMEL-6440 Fixed the issue of data loss on xpath after cxf(payload-mode)
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/69034477
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/69034477
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/69034477
Branch: refs/heads/camel-2.11.x
Commit: 6903447743b5cbe99a68d92090fd576b5aa7affc
Parents: 6333fe1
Author: Willem Jiang <ni...@apache.org>
Authored: Wed Sep 18 11:32:17 2013 +0800
Committer: Willem Jiang <ni...@apache.org>
Committed: Wed Sep 18 11:58:27 2013 +0800
----------------------------------------------------------------------
.../camel/converter/jaxp/DomConverter.java | 24 +-
...CxfConsumerPayloadXPathClientServerTest.java | 143 +++++++++++
.../cxf/CxfConsumerPayloadXPathTest.java | 250 +++++++++++++++++++
3 files changed, 415 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/69034477/camel-core/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java b/camel-core/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
index 725db0b..bbd7d7a 100644
--- a/camel-core/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
+++ b/camel-core/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
@@ -59,7 +59,7 @@ public final class DomConverter {
boolean found = false;
if (nodeList instanceof Node) {
Node node = (Node) nodeList;
- String s = xml.toString(node, exchange);
+ String s = toString(node, exchange);
if (ObjectHelper.isNotEmpty(s)) {
found = true;
buffer.append(s);
@@ -69,7 +69,7 @@ public final class DomConverter {
int size = nodeList.getLength();
for (int i = 0; i < size; i++) {
Node node = nodeList.item(i);
- String s = xml.toString(node, exchange);
+ String s = toString(node, exchange);
if (ObjectHelper.isNotEmpty(s)) {
found = true;
buffer.append(s);
@@ -85,6 +85,26 @@ public final class DomConverter {
return buffer.toString();
}
+
+ private String toString(Node node, Exchange exchange) throws TransformerException {
+ String s;
+ if (node instanceof Text) {
+ Text textnode = (Text) node;
+
+ StringBuilder b = new StringBuilder();
+ b.append(textnode.getNodeValue());
+ textnode = (Text) textnode.getNextSibling();
+ while (textnode != null) {
+ b.append(textnode.getNodeValue());
+ textnode = (Text) textnode.getNextSibling();
+ }
+ s = b.toString();
+ } else {
+ s = xml.toString(node, exchange);
+
+ }
+ return s;
+ }
@Converter
public static Integer toInteger(NodeList nodeList) {
http://git-wip-us.apache.org/repos/asf/camel/blob/69034477/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathClientServerTest.java
----------------------------------------------------------------------
diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathClientServerTest.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathClientServerTest.java
new file mode 100644
index 0000000..64980a9
--- /dev/null
+++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathClientServerTest.java
@@ -0,0 +1,143 @@
+/**
+ * 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.cxf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.xml.XPathBuilder;
+import org.apache.camel.converter.jaxp.XmlConverter;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.commons.lang.StringUtils;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.binding.soap.SoapHeader;
+import org.apache.cxf.frontend.ClientFactoryBean;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.junit.Test;
+
+
+
+public class CxfConsumerPayloadXPathClientServerTest extends CamelTestSupport {
+ private static final String ECHO_RESPONSE = "<ns1:echoResponse xmlns:ns1=\"http://cxf.component.camel.apache.org/\">"
+ + "<return xmlns=\"http://cxf.component.camel.apache.org/\">echo Hello World!</return>"
+ + "</ns1:echoResponse>";
+
+
+ protected final String simpleEndpointAddress = "http://localhost:"
+ + CXFTestSupport.getPort1() + "/" + getClass().getSimpleName() + "/test";
+ protected final String simpleEndpointURI = "cxf://" + simpleEndpointAddress
+ + "?serviceClass=org.apache.camel.component.cxf.HelloService";
+
+ private String testMessage;
+ private String receivedMessageCxfPayloadApplyingXPath;
+ private String receivedMessageByDom;
+ private String receivedMessageStringApplyingXPath;
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from(simpleEndpointURI + "&dataFormat=PAYLOAD").to("log:info").process(new Processor() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void process(final Exchange exchange) throws Exception {
+ Object request = exchange.getIn().getBody();
+ assertIsInstanceOf(CxfPayload.class, request);
+
+ //attempt 1) applying XPath to exchange.getIn().getBody()
+ receivedMessageCxfPayloadApplyingXPath = XPathBuilder.xpath("//*[name()='arg0']/text()").evaluate(context, request, String.class);
+
+ //attempt 2) in stead of XPATH, browse the DOM-tree
+ CxfPayload<SoapHeader> payload = (CxfPayload<SoapHeader>) request;
+ Element el = (Element) payload.getBody().get(0);
+ Element el2 = (Element) el.getFirstChild();
+ Text textnode = (Text) el2.getFirstChild();
+ receivedMessageByDom = textnode.getNodeValue();
+
+ textnode = (Text) textnode.getNextSibling();
+ while (textnode != null) {
+ //the textnode appears to have siblings!
+ receivedMessageByDom = receivedMessageByDom + textnode.getNodeValue();
+ textnode = (Text) textnode.getNextSibling();
+ }
+
+ //attempt 3) apply XPATH after converting CxfPayload to String
+ request = exchange.getIn().getBody(String.class);
+ assertIsInstanceOf(String.class, request);
+ receivedMessageStringApplyingXPath = XPathBuilder.xpath("//*[name()='arg0']/text()").evaluate(context, request, String.class);
+
+ //build some dummy response
+ XmlConverter converter = new XmlConverter();
+ Document outDocument = converter.toDOMDocument(ECHO_RESPONSE);
+ List<Source> outElements = new ArrayList<Source>();
+ outElements.add(new DOMSource(outDocument.getDocumentElement()));
+ // set the payload header with null
+ CxfPayload<SoapHeader> responsePayload = new CxfPayload<SoapHeader>(null, outElements, null);
+ exchange.getOut().setBody(responsePayload);
+ }
+ });
+ }
+ };
+ }
+
+ private void buildTestMessage(int size) {
+ testMessage = StringUtils.repeat("x", size);
+ }
+
+ @Test
+ public void testMessageWithIncreasingSize() throws Exception {
+
+ execTest(1);
+ execTest(10);
+ execTest(100);
+ execTest(1000);
+ execTest(10000);
+ execTest(100000);
+
+ }
+
+ private void execTest(int size) throws Exception {
+ buildTestMessage(size);
+
+ ClientProxyFactoryBean proxyFactory = new ClientProxyFactoryBean();
+ ClientFactoryBean clientBean = proxyFactory.getClientFactoryBean();
+ clientBean.setAddress(simpleEndpointAddress);
+ clientBean.setServiceClass(HelloService.class);
+ clientBean.setBus(BusFactory.getDefaultBus());
+
+ HelloService client = (HelloService) proxyFactory.create();
+
+ String result = client.echo(testMessage);
+ assertEquals("We should get the echo string result from router", "echo Hello World!", result);
+
+ //check received requests
+ assertEquals("Lengths of testMessage and receiveMessage should be equal (conversion body to String),", testMessage.length(), receivedMessageStringApplyingXPath.length());
+ assertEquals("Lengths of receivedMessageByDom and receivedMessageCxfPayloadApplyingXPath should be equal", receivedMessageCxfPayloadApplyingXPath.length(), receivedMessageByDom.length());
+ assertEquals("Lengths of testMessage and receiveMessage should be equal (body is CxfPayload),", testMessage.length(), receivedMessageCxfPayloadApplyingXPath.length());
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/69034477/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathTest.java
----------------------------------------------------------------------
diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathTest.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathTest.java
new file mode 100644
index 0000000..5ee5103
--- /dev/null
+++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadXPathTest.java
@@ -0,0 +1,250 @@
+/**
+ * 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.cxf;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.xml.XPathBuilder;
+import org.apache.camel.impl.DefaultExchange;
+import org.apache.camel.test.AvailablePortFinder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.commons.lang.StringUtils;
+import org.apache.cxf.binding.soap.SoapHeader;
+import org.junit.ComparisonFailure;
+import org.junit.Test;
+
+public class CxfConsumerPayloadXPathTest extends CamelTestSupport {
+
+ public static final String HEADER_SIZE = "tstsize";
+
+ @Test
+ public void size1XPathStringResultTest() throws Exception {
+ simpleTest(1, new TestRouteWithXPathStringResultBuilder());
+ }
+
+ @Test
+ public void size100XPathStringResultTest() throws Exception {
+ simpleTest(100, new TestRouteWithXPathStringResultBuilder());
+ }
+
+ @Test
+ public void size1000XPathStringResultTest() throws Exception {
+ simpleTest(1000, new TestRouteWithXPathStringResultBuilder());
+ }
+
+ @Test
+ public void size10000XPathStringResultTest() throws Exception {
+ simpleTest(10000, new TestRouteWithXPathStringResultBuilder());
+ }
+
+ @Test
+ public void size1XPathTest() throws Exception {
+ simpleTest(1, new TestRouteWithXPathBuilder());
+ }
+
+ @Test
+ public void size100XPathTest() throws Exception {
+ simpleTest(100, new TestRouteWithXPathBuilder());
+ }
+
+ @Test
+ public void size1000XPathTest() throws Exception {
+ simpleTest(1000, new TestRouteWithXPathBuilder());
+ }
+
+ @Test
+ public void size10000XPathTest() throws Exception {
+ simpleTest(10000, new TestRouteWithXPathBuilder());
+ }
+
+
+
+ //the textnode appears to have siblings!
+ @Test
+ public void size10000DomTest() throws Exception {
+ simpleTest(10000, new TestRouteWithDomBuilder());
+ }
+
+ private class TestRouteWithXPathBuilder extends BaseRouteBuilder {
+ @Override
+ public void configure() {
+ from("cxf://" + testAddress + "?dataFormat=PAYLOAD")
+ .streamCaching()
+ .process(new XPathProcessor())
+ .process(new ResponseProcessor());
+ }
+ }
+
+ private class TestRouteWithXPathStringResultBuilder extends BaseRouteBuilder {
+ @Override
+ public void configure() {
+ from("cxf://" + testAddress + "?dataFormat=PAYLOAD")
+ .streamCaching()
+ .process(new XPathStringResultProcessor())
+ .process(new ResponseProcessor());
+ }
+ }
+
+ private class TestRouteWithDomFirstOneOnlyBuilder extends BaseRouteBuilder {
+ @Override
+ public void configure() {
+ from("cxf://" + testAddress + "?dataFormat=PAYLOAD")
+ .streamCaching()
+ .process(new DomFirstOneOnlyProcessor())
+ .process(new ResponseProcessor());
+ }
+ }
+
+ private class TestRouteWithDomBuilder extends BaseRouteBuilder {
+ @Override
+ public void configure() {
+ from("cxf://" + testAddress + "?dataFormat=PAYLOAD")
+ .streamCaching()
+ .process(new DomProcessor())
+ .process(new ResponseProcessor());
+ }
+ }
+
+ //implementation simular to xpath() in route: no data loss
+ private class XPathStringResultProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Object obj = exchange.getIn().getBody();
+ //xpath expression directly results in a: String
+ String content = (String) XPathBuilder.xpath("//xml/text()").stringResult().evaluate(context, obj, Object.class);
+ exchange.getOut().setBody(content);
+ exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+ }
+ }
+
+ //this version leads to data loss
+ private class XPathProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Object obj = exchange.getIn().getBody();
+ //xpath expression results in a: net.sf.saxon.dom.DOMNodeList
+ //after which it is converted to a String
+ String content = XPathBuilder.xpath("//xml/text()").evaluate(context, obj, String.class);
+ exchange.getOut().setBody(content);
+ exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+ }
+ }
+
+ //this version leads to data loss
+ private class DomFirstOneOnlyProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Object obj = exchange.getIn().getBody();
+ CxfPayload<SoapHeader> payload = (CxfPayload<SoapHeader>) obj;
+ Element el = (Element) payload.getBody().get(0);
+ Text textnode = (Text) el.getFirstChild();
+ exchange.getOut().setBody(textnode.getNodeValue());
+ exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+ }
+ }
+
+ private class DomProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Object obj = exchange.getIn().getBody();
+ CxfPayload<SoapHeader> payload = (CxfPayload<SoapHeader>) obj;
+ Element el = (Element) payload.getBody().get(0);
+ Text textnode = (Text) el.getFirstChild();
+
+ StringBuilder b = new StringBuilder();
+ b.append(textnode.getNodeValue());
+ textnode = (Text) textnode.getNextSibling();
+ while (textnode != null) {
+ //the textnode appears to have siblings!
+ b.append(textnode.getNodeValue());
+ textnode = (Text) textnode.getNextSibling();
+ }
+
+ exchange.getOut().setBody(b.toString());
+ exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+ }
+ }
+
+ private class ResponseProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Object obj = exchange.getIn().getBody();
+ String content = (String) obj;
+ String msgOut = constructSoapMessage(content);
+ exchange.getOut().setBody(msgOut);
+ exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+ exchange.getOut().setHeader(HEADER_SIZE, "" + content.length());
+ }
+ }
+
+ private void simpleTest(int repeat, BaseRouteBuilder builder) throws Exception {
+ setUseRouteBuilder(false);
+ context.addRoutes(builder);
+ startCamelContext();
+
+ String content = StringUtils.repeat("x", repeat);
+ String msgIn = constructSoapMessage(content);
+
+ Exchange exchgIn = new DefaultExchange(context);
+ exchgIn.setPattern(ExchangePattern.InOut);
+ exchgIn.getIn().setBody(msgIn);
+
+ //Execute
+ Exchange exchgOut = template.send(builder.getTestAddress(), exchgIn);
+
+ //Verify
+ String result = exchgOut.getOut().getBody(String.class);
+ assertNotNull("response on http call", result);
+
+ //check for data loss in received input (after xpath)
+ String headerSize = exchgOut.getOut().getHeader(HEADER_SIZE, String.class);
+ assertEquals("" + repeat, headerSize);
+
+ assertTrue("dataloss in output occurred", result.length() > repeat);
+
+ stopCamelContext();
+ }
+
+
+ private abstract class BaseRouteBuilder extends RouteBuilder {
+ protected final String testAddress = getAvailableUrl("test");
+
+ public String getTestAddress() {
+ return testAddress;
+ }
+ }
+
+ private String getAvailableUrl(String pathEnd) {
+ int availablePort = AvailablePortFinder.getNextAvailable();
+ String url = "http://localhost:" + availablePort
+ + "/" + getClass().getSimpleName();
+ return url + "/" + pathEnd;
+ }
+
+ private String constructSoapMessage(String content) {
+ return
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+ + "<soapenv:Body><xml>" + content + "</xml></soapenv:Body>"
+ + "</soapenv:Envelope>";
+ }
+}
\ No newline at end of file