1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.ws.client.core;
18
19 import java.io.IOException;
20 import java.util.Enumeration;
21 import java.util.Iterator;
22 import java.util.StringTokenizer;
23 import javax.activation.CommandMap;
24 import javax.activation.DataHandler;
25 import javax.activation.MailcapCommandMap;
26 import javax.mail.util.ByteArrayDataSource;
27 import javax.servlet.ServletConfig;
28 import javax.servlet.ServletException;
29 import javax.servlet.http.HttpServlet;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import javax.xml.namespace.QName;
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.soap.MessageFactory;
36 import javax.xml.soap.MimeHeader;
37 import javax.xml.soap.MimeHeaders;
38 import javax.xml.soap.SOAPBody;
39 import javax.xml.soap.SOAPException;
40 import javax.xml.soap.SOAPMessage;
41 import javax.xml.transform.Result;
42 import javax.xml.transform.Source;
43 import javax.xml.transform.Transformer;
44 import javax.xml.transform.TransformerConfigurationException;
45 import javax.xml.transform.TransformerException;
46 import javax.xml.transform.TransformerFactory;
47 import javax.xml.transform.dom.DOMSource;
48 import javax.xml.transform.stream.StreamResult;
49
50 import org.springframework.oxm.Marshaller;
51 import org.springframework.oxm.Unmarshaller;
52 import org.springframework.oxm.XmlMappingException;
53 import org.springframework.util.StringUtils;
54 import org.springframework.ws.WebServiceMessage;
55 import org.springframework.ws.client.WebServiceTransportException;
56 import org.springframework.ws.pox.dom.DomPoxMessageFactory;
57 import org.springframework.ws.soap.SoapMessage;
58 import org.springframework.ws.soap.SoapMessageFactory;
59 import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
60 import org.springframework.ws.soap.client.SoapFaultClientException;
61 import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
62 import org.springframework.ws.transport.http.CommonsHttpMessageSender;
63 import org.springframework.ws.transport.support.FreePortScanner;
64 import org.springframework.xml.transform.StringResult;
65 import org.springframework.xml.transform.StringSource;
66
67 import org.junit.AfterClass;
68 import org.junit.Assert;
69 import org.junit.Before;
70 import org.junit.BeforeClass;
71 import org.junit.Test;
72 import org.mortbay.jetty.Server;
73 import org.mortbay.jetty.servlet.Context;
74 import org.mortbay.jetty.servlet.ServletHolder;
75 import org.w3c.dom.Document;
76 import org.xml.sax.SAXException;
77
78 import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
79 import static org.junit.Assert.assertEquals;
80
81 public class WebServiceTemplateIntegrationTest {
82
83 private static Server jettyServer;
84
85 private static String baseUrl;
86
87
88 private WebServiceTemplate template;
89
90 private String messagePayload = "<root xmlns='http://springframework.org/spring-ws'><child/></root>";
91
92 @BeforeClass
93 public static void startJetty() throws Exception {
94 int port = FreePortScanner.getFreePort();
95 baseUrl = "http://localhost:" + port;
96 jettyServer = new Server(port);
97 Context jettyContext = new Context(jettyServer, "/");
98 jettyContext.addServlet(new ServletHolder(new EchoSoapServlet()), "/soap/echo");
99 jettyContext.addServlet(new ServletHolder(new SoapFaultServlet()), "/soap/fault");
100 SoapFaultServlet badRequestFault = new SoapFaultServlet();
101 badRequestFault.setSc(400);
102 jettyContext.addServlet(new ServletHolder(badRequestFault), "/soap/badRequestFault");
103 jettyContext.addServlet(new ServletHolder(new NoResponseSoapServlet()), "/soap/noResponse");
104 jettyContext.addServlet(new ServletHolder(new AttachmentsServlet()), "/soap/attachment");
105 jettyContext.addServlet(new ServletHolder(new PoxServlet()), "/pox");
106 jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound");
107 jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server");
108 jettyServer.start();
109 }
110
111
112
113
114
115 @Before
116 public void removeXmlDataContentHandler() throws SOAPException {
117 MessageFactory messageFactory = MessageFactory.newInstance();
118 SOAPMessage message = messageFactory.createMessage();
119 message.createAttachmentPart();
120 CommandMap.setDefaultCommandMap(new MailcapCommandMap());
121 }
122
123 @AfterClass
124 public static void stopJetty() throws Exception {
125 if (jettyServer.isRunning()) {
126 jettyServer.stop();
127 }
128 }
129
130 @Test
131 public void testSaaj() throws Exception {
132 doSoap(new SaajSoapMessageFactory(MessageFactory.newInstance()));
133 }
134
135 @Test
136 public void testAxiom() throws Exception {
137 doSoap(new AxiomSoapMessageFactory());
138 }
139
140 @Test
141 public void testAxiomNonCaching() throws Exception {
142 AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory();
143 axiomFactory.setPayloadCaching(false);
144 doSoap(axiomFactory);
145 }
146
147 @Test
148 public void testPox() throws Exception {
149 template = new WebServiceTemplate(new DomPoxMessageFactory());
150 template.setMessageSender(new CommonsHttpMessageSender());
151 String content = "<root xmlns='http://springframework.org/spring-ws'><child/></root>";
152 StringResult result = new StringResult();
153 template.sendSourceAndReceiveToResult(baseUrl + "/pox", new StringSource(content), result);
154 assertXMLEqual(content, result.toString());
155 try {
156 template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound", new StringSource(content),
157 new StringResult());
158 Assert.fail("WebServiceTransportException expected");
159 }
160 catch (WebServiceTransportException ex) {
161
162 }
163 try {
164 template.sendSourceAndReceiveToResult(baseUrl + "/errors/server", new StringSource(content),
165 result);
166 Assert.fail("WebServiceTransportException expected");
167 }
168 catch (WebServiceTransportException ex) {
169
170 }
171 }
172
173 private void doSoap(SoapMessageFactory messageFactory) throws Exception {
174 template = new WebServiceTemplate(messageFactory);
175 template.setMessageSender(new CommonsHttpMessageSender());
176 sendSourceAndReceiveToResult();
177 sendSourceAndReceiveToResultNoResponse();
178 marshalSendAndReceiveResponse();
179 marshalSendAndReceiveNoResponse();
180 notFound();
181 fault();
182 faultNonCompliant();
183 attachment();
184 }
185
186 private void sendSourceAndReceiveToResult() throws SAXException, IOException {
187 StringResult result = new StringResult();
188 boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/echo",
189 new StringSource(messagePayload), result);
190 Assert.assertTrue("Invalid result", b);
191 assertXMLEqual(messagePayload, result.toString());
192 }
193
194 private void sendSourceAndReceiveToResultNoResponse() {
195 boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/noResponse",
196 new StringSource(messagePayload), new StringResult());
197 Assert.assertFalse("Invalid result", b);
198 }
199
200 private void marshalSendAndReceiveResponse() throws TransformerConfigurationException {
201 final Transformer transformer = TransformerFactory.newInstance().newTransformer();
202 final Object requestObject = new Object();
203 Marshaller marshaller = new Marshaller() {
204
205 public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
206 Assert.assertEquals("Invalid object", graph, requestObject);
207 try {
208 transformer.transform(new StringSource(messagePayload), result);
209 }
210 catch (TransformerException e) {
211 Assert.fail(e.getMessage());
212 }
213 }
214
215 public boolean supports(Class<?> clazz) {
216 Assert.assertEquals("Invalid class", Object.class, clazz);
217 return true;
218 }
219 };
220 final Object responseObject = new Object();
221 Unmarshaller unmarshaller = new Unmarshaller() {
222
223 public Object unmarshal(Source source) throws XmlMappingException, IOException {
224 return responseObject;
225 }
226
227 public boolean supports(Class<?> clazz) {
228 Assert.assertEquals("Invalid class", Object.class, clazz);
229 return true;
230 }
231 };
232 template.setMarshaller(marshaller);
233 template.setUnmarshaller(unmarshaller);
234 Object result = template.marshalSendAndReceive(baseUrl + "/soap/echo", requestObject);
235 Assert.assertEquals("Invalid response object", responseObject, result);
236 }
237
238 private void marshalSendAndReceiveNoResponse() throws TransformerConfigurationException {
239 final Transformer transformer = TransformerFactory.newInstance().newTransformer();
240 final Object requestObject = new Object();
241 Marshaller marshaller = new Marshaller() {
242
243 public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
244 Assert.assertEquals("Invalid object", graph, requestObject);
245 try {
246 transformer.transform(new StringSource(messagePayload), result);
247 }
248 catch (TransformerException e) {
249 Assert.fail(e.getMessage());
250 }
251 }
252
253 public boolean supports(Class<?> clazz) {
254 Assert.assertEquals("Invalid class", Object.class, clazz);
255 return true;
256 }
257 };
258 template.setMarshaller(marshaller);
259 Object result = template.marshalSendAndReceive(baseUrl + "/soap/noResponse", requestObject);
260 Assert.assertNull("Invalid response object", result);
261 }
262
263 private void notFound() {
264 try {
265 template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound",
266 new StringSource(messagePayload), new StringResult());
267 Assert.fail("WebServiceTransportException expected");
268 }
269 catch (WebServiceTransportException ex) {
270
271 }
272 }
273
274 private void fault() {
275 Result result = new StringResult();
276 try {
277 template.sendSourceAndReceiveToResult(baseUrl + "/soap/fault", new StringSource(messagePayload),
278 result);
279 Assert.fail("SoapFaultClientException expected");
280 }
281 catch (SoapFaultClientException ex) {
282
283 }
284 }
285
286 private void faultNonCompliant() {
287 Result result = new StringResult();
288 template.setCheckConnectionForFault(false);
289 template.setCheckConnectionForError(false);
290 try {
291 template.sendSourceAndReceiveToResult(baseUrl + "/soap/badRequestFault",
292 new StringSource(messagePayload), result);
293 Assert.fail("SoapFaultClientException expected");
294 }
295 catch (SoapFaultClientException ex) {
296
297 }
298 }
299
300 private void attachment() {
301 template.sendSourceAndReceiveToResult(baseUrl + "/soap/attachment", new StringSource(messagePayload),
302 new WebServiceMessageCallback() {
303
304 public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
305 SoapMessage soapMessage = (SoapMessage) message;
306 final String attachmentContent = "content";
307 soapMessage.addAttachment("attachment-1",
308 new DataHandler(new ByteArrayDataSource(attachmentContent, "text/plain")));
309 }
310 }, new StringResult());
311 }
312
313
314 private static class ErrorServlet extends HttpServlet {
315
316 private int sc;
317
318 private ErrorServlet(int sc) {
319 this.sc = sc;
320 }
321
322 @Override
323 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
324 resp.sendError(sc);
325 }
326 }
327
328
329 private static class PoxServlet extends HttpServlet {
330
331 private DocumentBuilderFactory documentBuilderFactory;
332
333 private TransformerFactory transformerFactory;
334
335 @Override
336 public void init(ServletConfig servletConfig) throws ServletException {
337 super.init(servletConfig);
338 documentBuilderFactory = DocumentBuilderFactory.newInstance();
339 documentBuilderFactory.setNamespaceAware(true);
340 transformerFactory = TransformerFactory.newInstance();
341 }
342
343 @Override
344 public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
345 try {
346 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
347 Document message = documentBuilder.parse(req.getInputStream());
348 Transformer transformer = transformerFactory.newTransformer();
349 transformer.transform(new DOMSource(message), new StreamResult(resp.getOutputStream()));
350 }
351 catch (Exception ex) {
352 throw new ServletException("POX POST failed" + ex.getMessage());
353 }
354 }
355 }
356
357
358 private abstract static class AbstractSoapServlet extends HttpServlet {
359
360 protected MessageFactory messageFactory = null;
361
362 private int sc = -1;
363
364 public void setSc(int sc) {
365 this.sc = sc;
366 }
367
368 @Override
369 public void init(ServletConfig servletConfig) throws ServletException {
370 super.init(servletConfig);
371 try {
372 messageFactory = MessageFactory.newInstance();
373 }
374 catch (SOAPException ex) {
375 throw new ServletException("Unable to create message factory" + ex.getMessage());
376 }
377 }
378
379 @Override
380 public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
381 try {
382 MimeHeaders headers = getHeaders(req);
383 SOAPMessage request = messageFactory.createMessage(headers, req.getInputStream());
384 SOAPMessage reply = onMessage(request);
385 if (sc != -1) {
386 resp.setStatus(sc);
387 }
388 if (reply != null) {
389 if (reply.saveRequired()) {
390 reply.saveChanges();
391 }
392 if (sc == -1) {
393 resp.setStatus(!reply.getSOAPBody().hasFault() ? HttpServletResponse.SC_OK :
394 HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
395 }
396 putHeaders(reply.getMimeHeaders(), resp);
397 reply.writeTo(resp.getOutputStream());
398 }
399 else if (sc == -1) {
400 resp.setStatus(HttpServletResponse.SC_ACCEPTED);
401 }
402 }
403 catch (Exception ex) {
404 throw new ServletException("SAAJ POST failed " + ex.getMessage(), ex);
405 }
406 }
407
408 private MimeHeaders getHeaders(HttpServletRequest httpServletRequest) {
409 Enumeration<?> enumeration = httpServletRequest.getHeaderNames();
410 MimeHeaders headers = new MimeHeaders();
411 while (enumeration.hasMoreElements()) {
412 String headerName = (String) enumeration.nextElement();
413 String headerValue = httpServletRequest.getHeader(headerName);
414 StringTokenizer values = new StringTokenizer(headerValue, ",");
415 while (values.hasMoreTokens()) {
416 headers.addHeader(headerName, values.nextToken().trim());
417 }
418 }
419 return headers;
420 }
421
422 private void putHeaders(MimeHeaders headers, HttpServletResponse res) {
423 Iterator<?> it = headers.getAllHeaders();
424 while (it.hasNext()) {
425 MimeHeader header = (MimeHeader) it.next();
426 String[] values = headers.getHeader(header.getName());
427 res.setHeader(header.getName(), StringUtils.arrayToCommaDelimitedString(values));
428 }
429 }
430
431 protected abstract SOAPMessage onMessage(SOAPMessage message) throws SOAPException;
432 }
433
434 private static class EchoSoapServlet extends AbstractSoapServlet {
435
436 @Override
437 protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException {
438 return message;
439 }
440 }
441
442 private static class NoResponseSoapServlet extends AbstractSoapServlet {
443
444 @Override
445 protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException {
446 return null;
447 }
448 }
449
450 private static class SoapFaultServlet extends AbstractSoapServlet {
451
452 @Override
453 protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException {
454 SOAPMessage response = messageFactory.createMessage();
455 SOAPBody body = response.getSOAPBody();
456 body.addFault(new QName("http://schemas.xmlsoap.org/soap/envelope/", "Server"), "Server fault");
457 return response;
458 }
459 }
460
461 private static class AttachmentsServlet extends AbstractSoapServlet {
462
463 @Override
464 protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException {
465 assertEquals("No attachments found", 1, message.countAttachments());
466 return null;
467 }
468 }
469
470 }