/** * 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.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()); } @Test public void size1000DomFirstTest() throws Exception { simpleTest(1000, new TestRouteWithDomFirstOneOnlyBuilder()); } 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(); @SuppressWarnings("unchecked") CxfPayload<SoapHeader> payload = (CxfPayload<SoapHeader>) obj; Element el = 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(); @SuppressWarnings("unchecked") CxfPayload<SoapHeader> payload = (CxfPayload<SoapHeader>) obj; Element el = 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>"; } }