Updated Branches:
  refs/heads/camel-2.11.x 6333fe1f7 -> 690344774
  refs/heads/camel-2.12.x ad6307503 -> c4cb3f5c3
  refs/heads/master 4c70f54b5 -> a4c9617f0


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/c4cb3f5c
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/c4cb3f5c
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/c4cb3f5c

Branch: refs/heads/camel-2.12.x
Commit: c4cb3f5c3812a6807c0478b22f6a380e7eaf3c4f
Parents: ad63075
Author: Willem Jiang <ningji...@apache.org>
Authored: Wed Sep 18 11:32:17 2013 +0800
Committer: Willem Jiang <ningji...@apache.org>
Committed: Wed Sep 18 11:48:31 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/c4cb3f5c/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/c4cb3f5c/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/c4cb3f5c/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

Reply via email to