1   /*
2    * Copyright 2005-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.transport.http;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.util.zip.GZIPOutputStream;
24  import javax.servlet.Servlet;
25  import javax.servlet.ServletException;
26  import javax.servlet.http.HttpServlet;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  import javax.xml.soap.MessageFactory;
30  import javax.xml.soap.MimeHeaders;
31  import javax.xml.soap.SOAPConstants;
32  import javax.xml.soap.SOAPException;
33  import javax.xml.soap.SOAPMessage;
34  import javax.xml.transform.Transformer;
35  import javax.xml.transform.TransformerException;
36  import javax.xml.transform.TransformerFactory;
37  import javax.xml.transform.dom.DOMResult;
38  
39  import org.springframework.beans.factory.InitializingBean;
40  import org.springframework.util.FileCopyUtils;
41  import org.springframework.ws.WebServiceMessage;
42  import org.springframework.ws.WebServiceMessageFactory;
43  import org.springframework.ws.soap.saaj.SaajSoapMessage;
44  import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
45  import org.springframework.ws.transport.FaultAwareWebServiceConnection;
46  import org.springframework.ws.transport.WebServiceConnection;
47  import org.springframework.ws.transport.support.FreePortScanner;
48  import org.springframework.xml.transform.StringResult;
49  import org.springframework.xml.transform.StringSource;
50  
51  import org.custommonkey.xmlunit.XMLUnit;
52  import org.junit.After;
53  import org.junit.Assert;
54  import org.junit.Before;
55  import org.junit.Test;
56  import org.mortbay.jetty.Server;
57  import org.mortbay.jetty.servlet.Context;
58  import org.mortbay.jetty.servlet.ServletHolder;
59  
60  import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
61  import static org.junit.Assert.assertEquals;
62  
63  public abstract class AbstractHttpWebServiceMessageSenderIntegrationTestCase {
64  
65      private Server jettyServer;
66  
67      private static final String REQUEST_HEADER_NAME = "RequestHeader";
68  
69      private static final String REQUEST_HEADER_VALUE = "RequestHeaderValue";
70  
71      private static final String RESPONSE_HEADER_NAME = "ResponseHeader";
72  
73      private static final String RESPONSE_HEADER_VALUE = "ResponseHeaderValue";
74  
75      private static final String REQUEST = "<Request xmlns='http://springframework.org/spring-ws/' />";
76  
77      private static final String SOAP_REQUEST =
78              "<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body>" +
79                      REQUEST + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
80  
81      private static final String RESPONSE = "<Response  xmlns='http://springframework.org/spring-ws/' />";
82  
83      private static final String SOAP_RESPONSE =
84              "<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body>" +
85                      RESPONSE + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
86  
87      private AbstractHttpWebServiceMessageSender messageSender;
88  
89      private Context jettyContext;
90  
91      private MessageFactory saajMessageFactory;
92  
93      private TransformerFactory transformerFactory;
94  
95      private WebServiceMessageFactory messageFactory;
96  
97      private URI connectionUri;
98  
99      @Before
100     public final void setUp() throws Exception {
101         int port = FreePortScanner.getFreePort();
102         connectionUri = new URI("http", null, "localhost", port, null, null, null);
103         jettyServer = new Server(port);
104         jettyContext = new Context(jettyServer, "/");
105         messageSender = createMessageSender();
106         if (messageSender instanceof InitializingBean) {
107             ((InitializingBean) messageSender).afterPropertiesSet();
108         }
109         XMLUnit.setIgnoreWhitespace(true);
110         saajMessageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
111         messageFactory = new SaajSoapMessageFactory(saajMessageFactory);
112         transformerFactory = TransformerFactory.newInstance();
113     }
114 
115     protected abstract AbstractHttpWebServiceMessageSender createMessageSender();
116 
117     @After
118     public final void tearDown() throws Exception {
119         if (jettyServer.isRunning()) {
120             jettyServer.stop();
121         }
122     }
123 
124     @Test
125     public void testSupports() throws URISyntaxException {
126         Assert.assertTrue("Message sender does not support HTTP url", messageSender.supports(connectionUri));
127     }
128 
129     @Test
130     public void testSendAndReceiveResponse() throws Exception {
131         MyServlet servlet = new MyServlet();
132         servlet.setResponse(true);
133         validateResponse(servlet);
134     }
135 
136     @Test
137     public void testSendAndReceiveNoResponse() throws Exception {
138         validateNonResponse(new MyServlet());
139     }
140 
141     @Test
142     public void testSendAndReceiveNoResponseAccepted() throws Exception {
143         MyServlet servlet = new MyServlet();
144         servlet.setResponseStatus(HttpServletResponse.SC_ACCEPTED);
145         validateNonResponse(servlet);
146     }
147 
148     @Test
149     public void testSendAndReceiveCompressed() throws Exception {
150         MyServlet servlet = new MyServlet();
151         servlet.setResponse(true);
152         servlet.setGzip(true);
153         validateResponse(servlet);
154     }
155 
156     @Test
157     public void testSendAndReceiveInvalidContentSize() throws Exception {
158         MyServlet servlet = new MyServlet();
159         servlet.setResponse(true);
160         servlet.setContentLength(-1);
161         validateResponse(servlet);
162     }
163 
164     @Test
165     public void testSendAndReceiveCompressedInvalidContentSize() throws Exception {
166         MyServlet servlet = new MyServlet();
167         servlet.setResponse(true);
168         servlet.setGzip(true);
169         servlet.setContentLength(-1);
170         validateResponse(servlet);
171     }
172 
173     @Test
174     public void testSendAndReceiveFault() throws Exception {
175         MyServlet servlet = new MyServlet();
176         servlet.setResponseStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
177         servlet.setResponse(true);
178         jettyContext.addServlet(new ServletHolder(servlet), "/");
179         jettyServer.start();
180         FaultAwareWebServiceConnection connection =
181                 (FaultAwareWebServiceConnection) messageSender.createConnection(connectionUri);
182         SOAPMessage request = createRequest();
183         try {
184             connection.send(new SaajSoapMessage(request));
185             connection.receive(messageFactory);
186             Assert.assertTrue("Response has no fault", connection.hasFault());
187         }
188         finally {
189             connection.close();
190         }
191     }
192 
193     private void validateResponse(Servlet servlet) throws Exception {
194         jettyContext.addServlet(new ServletHolder(servlet), "/");
195         jettyServer.start();
196         FaultAwareWebServiceConnection connection =
197                 (FaultAwareWebServiceConnection) messageSender.createConnection(connectionUri);
198         SOAPMessage request = createRequest();
199         try {
200             connection.send(new SaajSoapMessage(request));
201             SaajSoapMessage response = (SaajSoapMessage) connection.receive(messageFactory);
202             Assert.assertNotNull("No response", response);
203             Assert.assertFalse("Response has fault", connection.hasFault());
204             SOAPMessage saajResponse = response.getSaajMessage();
205             String[] headerValues = saajResponse.getMimeHeaders().getHeader(RESPONSE_HEADER_NAME);
206             Assert.assertNotNull("Response has no header", headerValues);
207             Assert.assertEquals("Response has invalid header", 1, headerValues.length);
208             Assert.assertEquals("Response has invalid header values", RESPONSE_HEADER_VALUE, headerValues[0]);
209             StringResult result = new StringResult();
210             Transformer transformer = transformerFactory.newTransformer();
211             transformer.transform(response.getPayloadSource(), result);
212             assertXMLEqual("Invalid response", RESPONSE, result.toString());
213         }
214         finally {
215             connection.close();
216         }
217     }
218 
219     private void validateNonResponse(Servlet servlet) throws Exception {
220         jettyContext.addServlet(new ServletHolder(servlet), "/");
221         jettyServer.start();
222 
223         WebServiceConnection connection = messageSender.createConnection(connectionUri);
224         SOAPMessage request = createRequest();
225         try {
226             connection.send(new SaajSoapMessage(request));
227             WebServiceMessage response = connection.receive(messageFactory);
228             Assert.assertNull("Response", response);
229         }
230         finally {
231             connection.close();
232         }
233     }
234 
235     private SOAPMessage createRequest() throws TransformerException, SOAPException {
236         SOAPMessage request = saajMessageFactory.createMessage();
237         MimeHeaders mimeHeaders = request.getMimeHeaders();
238         mimeHeaders.addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE);
239         Transformer transformer = transformerFactory.newTransformer();
240         transformer.transform(new StringSource(REQUEST), new DOMResult(request.getSOAPBody()));
241         return request;
242     }
243 
244     private class MyServlet extends HttpServlet {
245 
246         private int responseStatus = HttpServletResponse.SC_OK;
247 
248         private Integer contentLength;
249 
250         private boolean response;
251 
252         private boolean gzip;
253 
254         public void setResponseStatus(int responseStatus) {
255             this.responseStatus = responseStatus;
256         }
257 
258         public void setContentLength(int contentLength) {
259             this.contentLength = contentLength;
260         }
261 
262         public void setResponse(boolean response) {
263             this.response = response;
264         }
265 
266         public void setGzip(boolean gzip) {
267             this.gzip = gzip;
268         }
269 
270         @Override
271         protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
272                 throws ServletException, IOException {
273             try {
274                 assertEquals("Invalid header value received on server side", REQUEST_HEADER_VALUE,
275                         httpServletRequest.getHeader(REQUEST_HEADER_NAME));
276                 String receivedRequest =
277                         new String(FileCopyUtils.copyToByteArray(httpServletRequest.getInputStream()), "UTF-8");
278                 assertXMLEqual("Invalid request received", SOAP_REQUEST, receivedRequest);
279                 if (gzip) {
280                     assertEquals("Invalid Accept-Encoding header value received on server side", "gzip",
281                             httpServletRequest.getHeader("Accept-Encoding"));
282                 }
283 
284                 httpServletResponse.setStatus(responseStatus);
285                 if (response) {
286                     httpServletResponse.setContentType("text/xml");
287                     if (contentLength != null) {
288                         httpServletResponse.setContentLength(contentLength);
289                     }
290                     if (gzip) {
291                         httpServletResponse.addHeader("Content-Encoding", "gzip");
292                     }
293                     httpServletResponse.setHeader(RESPONSE_HEADER_NAME, RESPONSE_HEADER_VALUE);
294                     OutputStream os;
295                     if (gzip) {
296                         os = new GZIPOutputStream(httpServletResponse.getOutputStream());
297                     }
298                     else {
299                         os = httpServletResponse.getOutputStream();
300                     }
301                     FileCopyUtils.copy(SOAP_RESPONSE.getBytes("UTF-8"), os);
302                 }
303             }
304             catch (Exception ex) {
305                 throw new ServletException(ex);
306             }
307         }
308     }
309 
310 }