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