/** * 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.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spring.SpringCamelContext; import org.apache.camel.test.AvailablePortFinder; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.camel.util.IOHelper; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class CxfPayloadRouterContentLengthTest extends CamelTestSupport { /* * The response message is generated directly. The issue here is that the * xsi and xs namespaces are defined on the SOAP envelope but are used * within the payload. This can cause issues with some type conversions in * PAYLOAD mode, as the Camel-CXF endpoint will return some kind of window * within the StAX parsing (and the namespace definitions are outside). * * If some CXF implementation bean is used as the service the namespaces * will be defined within the payload (and everything works fine). */ private static final String RESPONSE_STRING = "This is the response string"; private static final String RESPONSE_MESSAGE = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>" + "<ns0:payload xmlns:ns0=\"http://schema.apache.org/test\"><ns0:response>" + RESPONSE_STRING + "</ns0:response></ns0:payload>" + "</s:Body></s:Envelope>"; private static final String REQUEST_MESSAGE = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>" + "<ns0:payload xmlns:ns0=\"http://schema.apache.org/test\"><ns0:request>foo</ns0:request></ns0:payload>" + "</s:Body></s:Envelope>"; // The Camel-Test with CXF will re-use jetty instances, so the ports1 to 6 are already blocked private static final int JETTY_PORT = AvailablePortFinder.getNextAvailable(); private AbstractXmlApplicationContext applicationContext; private Server server; static { System.setProperty("CXFTestSupport.jettyPort", Integer.toString(JETTY_PORT)); } @Override protected CamelContext createCamelContext() throws Exception { return SpringCamelContext.springCamelContext(applicationContext); } @Before public void setUp() throws Exception { /* * We start a Jetty for the service in order to have better control over * the response The response must contain only a Content-Type and a * Content-Length but no other header */ log.info("Starting jetty server at port {}", JETTY_PORT); server = new Server(); // Do not send a Server header HttpConfiguration httpconf = new HttpConfiguration(); httpconf.setSendServerVersion(false); ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpconf)); http.setPort(JETTY_PORT); server.addConnector(http); server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/xml"); // the Content-Length is correct for this response message response.setContentLength(RESPONSE_MESSAGE.length()); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); PrintWriter pw = response.getWriter(); pw.write(RESPONSE_MESSAGE); pw.close(); } }); server.start(); // Load the CXF endpoints for the route log.info("Start Routing Scenario at port {}", CXFTestSupport.getPort1()); applicationContext = new ClassPathXmlApplicationContext("org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml"); super.setUp(); assertNotNull("Should have created a valid spring context", applicationContext); } @After public void tearDown() throws Exception { // close the spring context IOHelper.close(applicationContext); // stop the jetty server if (server != null && server.isRunning()) { server.stop(); server.join(); } super.tearDown(); } @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { from("cxf:bean:proxyEndpoint?dataFormat=PAYLOAD") // .removeHeaders(".*") // call an external Web service in payload mode .to("cxf:bean:serviceEndpoint?dataFormat=PAYLOAD"); } }; } @Test public void testInvokeRouter() throws IOException { CloseableHttpClient httpclient = HttpClients.createDefault(); long contentLength = 0; boolean isChunked = false; String receivedContent = null; try { HttpPost httppost = new HttpPost("http://localhost:" + CXFTestSupport.getPort1() + "/TEST/PROXY"); StringEntity reqEntity = new StringEntity(REQUEST_MESSAGE, ContentType.TEXT_XML); reqEntity.setChunked(false); httppost.setEntity(reqEntity); CloseableHttpResponse response = httpclient.execute(httppost); try { HttpEntity respEntity = response.getEntity(); contentLength = respEntity.getContentLength(); isChunked = respEntity.isChunked(); receivedContent = EntityUtils.toString(respEntity); EntityUtils.consume(response.getEntity()); } finally { response.close(); } } finally { httpclient.close(); } assertNotNull(receivedContent); // chunked encoding is fine, we don't need to check the content length if (!isChunked) { assertEquals(receivedContent.length(), contentLength); } assertTrue("[" + receivedContent + "] does not contain [" + RESPONSE_STRING + "]", receivedContent.contains(RESPONSE_STRING)); // check whether the response was cut off by the client because the // Content-Length was wrong assertTrue("[" + receivedContent + "] does not contain the closing Envelope tag.", receivedContent.matches(".*\\</.*:Envelope\\>")); } }