/**
* 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>";
}
}