1   /*
2    * Copyright 2006 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.util.zip.GZIPOutputStream;
22  import javax.servlet.Servlet;
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServlet;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  import javax.xml.soap.MessageFactory;
28  import javax.xml.soap.MimeHeaders;
29  import javax.xml.soap.SOAPConstants;
30  import javax.xml.soap.SOAPException;
31  import javax.xml.soap.SOAPMessage;
32  import javax.xml.transform.Transformer;
33  import javax.xml.transform.TransformerException;
34  import javax.xml.transform.TransformerFactory;
35  import javax.xml.transform.dom.DOMResult;
36  
37  import org.custommonkey.xmlunit.XMLTestCase;
38  import org.custommonkey.xmlunit.XMLUnit;
39  import org.mortbay.jetty.Server;
40  import org.mortbay.jetty.servlet.Context;
41  import org.mortbay.jetty.servlet.ServletHolder;
42  import org.springframework.util.FileCopyUtils;
43  import org.springframework.ws.WebServiceMessage;
44  import org.springframework.ws.WebServiceMessageFactory;
45  import org.springframework.ws.soap.saaj.SaajSoapMessage;
46  import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
47  import org.springframework.ws.transport.FaultAwareWebServiceConnection;
48  import org.springframework.ws.transport.WebServiceConnection;
49  import org.springframework.xml.transform.StringResult;
50  import org.springframework.xml.transform.StringSource;
51  
52  public abstract class AbstractHttpWebServiceMessageSenderIntegrationTestCase extends XMLTestCase {
53  
54      private Server jettyServer;
55  
56      private static final String REQUEST_HEADER_NAME = "RequestHeader";
57  
58      private static final String REQUEST_HEADER_VALUE = "RequestHeaderValue";
59  
60      private static final String RESPONSE_HEADER_NAME = "ResponseHeader";
61  
62      private static final String RESPONSE_HEADER_VALUE = "ResponseHeaderValue";
63  
64      private static final String REQUEST = "<Request xmlns='http://springframework.org/spring-ws/' />";
65  
66      private static final String SOAP_REQUEST =
67              "<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body>" +
68                      REQUEST + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
69  
70      private static final String RESPONSE = "<Response  xmlns='http://springframework.org/spring-ws/' />";
71  
72      private static final String SOAP_RESPONSE =
73              "<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body>" +
74                      RESPONSE + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
75  
76      private AbstractHttpWebServiceMessageSender messageSender;
77  
78      private Context jettyContext;
79  
80      private static final String URI = "http://localhost:8888/";
81  
82      private MessageFactory saajMessageFactory;
83  
84      private TransformerFactory transformerFactory;
85  
86      private WebServiceMessageFactory messageFactory;
87  
88      protected final void setUp() throws Exception {
89          jettyServer = new Server(8888);
90          jettyContext = new Context(jettyServer, "/");
91          messageSender = createMessageSender();
92          XMLUnit.setIgnoreWhitespace(true);
93          saajMessageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
94          messageFactory = new SaajSoapMessageFactory(saajMessageFactory);
95          transformerFactory = TransformerFactory.newInstance();
96      }
97  
98      protected abstract AbstractHttpWebServiceMessageSender createMessageSender();
99  
100     protected final void tearDown() throws Exception {
101         if (jettyServer.isRunning()) {
102             jettyServer.stop();
103         }
104     }
105 
106     public void testSendAndReceiveResponse() throws Exception {
107         MyServlet servlet = new MyServlet();
108         servlet.setResponse(true);
109         validateResponse(servlet);
110     }
111 
112     public void testSendAndReceiveNoResponse() throws Exception {
113         validateNonResponse(new MyServlet());
114     }
115 
116     public void testSendAndReceiveNoResponseAccepted() throws Exception {
117         MyServlet servlet = new MyServlet();
118         servlet.setResponseStatus(HttpServletResponse.SC_ACCEPTED);
119         validateNonResponse(servlet);
120     }
121 
122     public void testSendAndReceiveCompressed() throws Exception {
123         MyServlet servlet = new MyServlet();
124         servlet.setResponse(true);
125         servlet.setGzip(true);
126         validateResponse(servlet);
127     }
128 
129     public void testSendAndReceiveInvalidContentSize() throws Exception {
130         MyServlet servlet = new MyServlet();
131         servlet.setResponse(true);
132         servlet.setContentLength(-1);
133         validateResponse(servlet);
134     }
135 
136     public void testSendAndReceiveCompressedInvalidContentSize() throws Exception {
137         MyServlet servlet = new MyServlet();
138         servlet.setResponse(true);
139         servlet.setGzip(true);
140         servlet.setContentLength(-1);
141         validateResponse(servlet);
142     }
143 
144     public void testSendAndReceiveFault() throws Exception {
145         MyServlet servlet = new MyServlet();
146         servlet.setResponseStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
147         jettyContext.addServlet(new ServletHolder(servlet), "/");
148         jettyServer.start();
149         FaultAwareWebServiceConnection connection =
150                 (FaultAwareWebServiceConnection) messageSender.createConnection(URI);
151         SOAPMessage request = createRequest();
152         try {
153             connection.send(new SaajSoapMessage(request));
154             connection.receive(messageFactory);
155             assertTrue("Response has no fault", connection.hasFault());
156         }
157         finally {
158             connection.close();
159         }
160     }
161 
162     private void validateResponse(Servlet servlet) throws Exception {
163         jettyContext.addServlet(new ServletHolder(servlet), "/");
164         jettyServer.start();
165         FaultAwareWebServiceConnection connection =
166                 (FaultAwareWebServiceConnection) messageSender.createConnection(URI);
167         SOAPMessage request = createRequest();
168         try {
169             connection.send(new SaajSoapMessage(request));
170             SaajSoapMessage response = (SaajSoapMessage) connection.receive(messageFactory);
171             assertNotNull("No response", response);
172             assertFalse("Response has fault", connection.hasFault());
173             SOAPMessage saajResponse = response.getSaajMessage();
174             String[] headerValues = saajResponse.getMimeHeaders().getHeader(RESPONSE_HEADER_NAME);
175             assertNotNull("Response has no header", headerValues);
176             assertEquals("Response has invalid header", 1, headerValues.length);
177             assertEquals("Response has invalid header values", RESPONSE_HEADER_VALUE, headerValues[0]);
178             StringResult result = new StringResult();
179             Transformer transformer = transformerFactory.newTransformer();
180             transformer.transform(response.getPayloadSource(), result);
181             assertXMLEqual("Invalid response", RESPONSE, result.toString());
182         }
183         finally {
184             connection.close();
185         }
186     }
187 
188     private void validateNonResponse(Servlet servlet) throws Exception {
189         jettyContext.addServlet(new ServletHolder(servlet), "/");
190         jettyServer.start();
191 
192         WebServiceConnection connection = messageSender.createConnection(URI);
193         SOAPMessage request = createRequest();
194         try {
195             connection.send(new SaajSoapMessage(request));
196             WebServiceMessage response = connection.receive(messageFactory);
197             assertNull("Response", response);
198         }
199         finally {
200             connection.close();
201         }
202     }
203 
204     private SOAPMessage createRequest() throws TransformerException, SOAPException {
205         SOAPMessage request = saajMessageFactory.createMessage();
206         MimeHeaders mimeHeaders = request.getMimeHeaders();
207         mimeHeaders.addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE);
208         Transformer transformer = transformerFactory.newTransformer();
209         transformer.transform(new StringSource(REQUEST), new DOMResult(request.getSOAPBody()));
210         return request;
211     }
212 
213     private class MyServlet extends HttpServlet {
214 
215         private int responseStatus = HttpServletResponse.SC_OK;
216 
217         private Integer contentLength;
218 
219         private boolean response;
220 
221         private boolean gzip;
222 
223         public void setResponseStatus(int responseStatus) {
224             this.responseStatus = responseStatus;
225         }
226 
227         public void setContentLength(int contentLength) {
228             this.contentLength = new Integer(contentLength);
229         }
230 
231         public void setResponse(boolean response) {
232             this.response = response;
233         }
234 
235         public void setGzip(boolean gzip) {
236             this.gzip = gzip;
237         }
238 
239         protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
240                 throws ServletException, IOException {
241             try {
242                 assertEquals("Invalid header value received on server side", REQUEST_HEADER_VALUE,
243                         httpServletRequest.getHeader(REQUEST_HEADER_NAME));
244                 String receivedRequest =
245                         new String(FileCopyUtils.copyToByteArray(httpServletRequest.getInputStream()), "UTF-8");
246                 assertXMLEqual("Invalid request received", SOAP_REQUEST, receivedRequest);
247                 if (gzip) {
248                     assertEquals("Invalid Accept-Encoding header value received on server side", "gzip",
249                             httpServletRequest.getHeader("Accept-Encoding"));
250                 }
251 
252                 httpServletResponse.setStatus(responseStatus);
253                 if (response) {
254                     httpServletResponse.setContentType("text/xml");
255                     if (contentLength != null) {
256                         httpServletResponse.setContentLength(contentLength.intValue());
257                     }
258                     if (gzip) {
259                         httpServletResponse.addHeader("Content-Encoding", "gzip");
260                     }
261                     httpServletResponse.setHeader(RESPONSE_HEADER_NAME, RESPONSE_HEADER_VALUE);
262                     OutputStream os;
263                     if (gzip) {
264                         os = new GZIPOutputStream(httpServletResponse.getOutputStream());
265                     }
266                     else {
267                         os = httpServletResponse.getOutputStream();
268                     }
269                     FileCopyUtils.copy(SOAP_RESPONSE.getBytes("UTF-8"), os);
270                 }
271             }
272             catch (Exception ex) {
273                 throw new ServletException(ex);
274             }
275         }
276     }
277 
278 }