From ded1efd456e90dc51646d4458f9177eda3dbbb9c Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 23 Apr 2014 11:37:41 +0200 Subject: [PATCH] Correctly set HTTP status code on SOAP 1.2 fault SOAP v1.2 specifies that for SOAP Fault's with Code of "env:Sender" the HTTP response status code should be 400 (rather than 500 for all other faults). This commit changes both client and server-side to reflect this. Issue: SWS-868 --- .../ws/FaultAwareWebServiceMessage.java | 10 +- .../ws/soap/AbstractSoapMessage.java | 13 +- .../FaultAwareWebServiceConnection.java | 15 +- .../http/AbstractHttpSenderConnection.java | 31 +- .../transport/http/HttpServletConnection.java | 20 + .../http/HttpTransportConstants.java | 5 +- ...ebServiceMessageReceiverObjectSupport.java | 2 +- .../ws/MockWebServiceMessage.java | 16 +- ...ebServiceTemplateIntegrationTestCase.java} | 191 +++----- ...WebServiceTemplateIntegrationTestCase.java | 408 ++++++++++++++++++ ...ap11WebServiceTemplateIntegrationTest.java | 32 ++ ...ap12WebServiceTemplateIntegrationTest.java | 35 ++ ...ap11WebServiceTemplateIntegrationTest.java | 34 ++ ...ap12WebServiceTemplateIntegrationTest.java | 36 ++ ...mPoxWebServiceTemplateIntegrationTest.java | 145 +++++++ ...ap11WebServiceTemplateIntegrationTest.java | 36 ++ ...ap12WebServiceTemplateIntegrationTest.java | 36 ++ .../client/core/WebServiceTemplateTest.java | 19 +- .../http/HttpServletConnectionTest.java | 44 +- ...viceMessageReceiverHandlerAdapterTest.java | 26 +- ...rviceMessageReceiverObjectSupportTest.java | 55 ++- .../http/HttpExchangeConnection.java | 16 + 22 files changed, 1033 insertions(+), 192 deletions(-) rename spring-ws-core/src/test/java/org/springframework/ws/client/core/{WebServiceTemplateIntegrationTest.java => AbstractSoap11WebServiceTemplateIntegrationTestCase.java} (71%) create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap12WebServiceTemplateIntegrationTestCase.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap11WebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap12WebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/DomPoxWebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap11WebServiceTemplateIntegrationTest.java create mode 100644 spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap12WebServiceTemplateIntegrationTest.java diff --git a/spring-ws-core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java b/spring-ws-core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java index 087abc15..77911b90 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.ws; +import javax.xml.namespace.QName; + /** * Sub-interface of {@link WebServiceMessage} that can contain special Fault messages. Fault messages (such as {@link * org.springframework.ws.soap.SoapFault} SOAP Faults) often require different processing rules. @@ -34,6 +36,12 @@ public interface FaultAwareWebServiceMessage extends WebServiceMessage { */ boolean hasFault(); + /** + * Returns the fault code, if any. + */ + QName getFaultCode(); + + /** * Returns the fault reason message. * diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/AbstractSoapMessage.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/AbstractSoapMessage.java index 0213098d..c070cce0 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/soap/AbstractSoapMessage.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/AbstractSoapMessage.java @@ -16,6 +16,7 @@ package org.springframework.ws.soap; +import javax.xml.namespace.QName; import javax.xml.transform.Result; import javax.xml.transform.Source; @@ -61,7 +62,17 @@ public abstract class AbstractSoapMessage extends AbstractMimeMessage implements return getSoapBody().hasFault(); } - /** Returns {@code getSoapBody().getFault().getFaultStringOrReason()}. */ + /** Returns {@code getSoapBody().getFault().getFaultCode()}. */ + @Override + public final QName getFaultCode() { + if (hasFault()) { + return getSoapBody().getFault().getFaultCode(); + } else { + return null; + } + } + + /** Returns {@code getSoapBody().getFault().getFaultStringOrReason()}. */ @Override public final String getFaultReason() { if (hasFault()) { diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java index fe7cd28b..b83546d1 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.ws.transport; import java.io.IOException; +import javax.xml.namespace.QName; import org.springframework.ws.soap.SoapFault; @@ -47,6 +48,18 @@ public interface FaultAwareWebServiceConnection extends WebServiceConnection { * * @param fault {@code true} if this will send a fault; {@code false} otherwise. * @throws IOException in case of I/O errors + * @deprecated In favor of {@link #setFaultCode(QName)} */ + @Deprecated void setFault(boolean fault) throws IOException; + + /** + * Sets a specific fault code. + * + *

Typically implemented by setting an HTTP status code. + * + * @param faultCode the fault code to be set on the connection, or {@code null} for no fault. + * @throws IOException in case of I/O errors + */ + void setFaultCode(QName faultCode) throws IOException; } diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java index e783aa32..83ebb19c 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.zip.GZIPInputStream; +import javax.xml.namespace.QName; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; @@ -119,15 +120,34 @@ public abstract class AbstractHttpSenderConnection extends AbstractSenderConnect @Override public final boolean hasFault() throws IOException { - return HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR == getResponseCode() && isXmlResponse(); + // SOAP 1.1 specifies a 500 status code for faults + // SOAP 1.2 specifies a 400 status code for sender faults, and 500 for all other faults + switch (getResponseCode()) { + case HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR: + return isSoap11Response() || isSoap12Response(); + case HttpTransportConstants.STATUS_BAD_REQUEST: + return isSoap12Response(); + default: + return false; + } } - /** Determine whether the response is a XML message. */ - private boolean isXmlResponse() throws IOException { + /** Determine whether the response is a SOAP 1.1 message. */ + private boolean isSoap11Response() throws IOException { Iterator iterator = getResponseHeaders(HttpTransportConstants.HEADER_CONTENT_TYPE); if (iterator.hasNext()) { String contentType = iterator.next().toLowerCase(); - return contentType.contains("xml"); + return contentType.contains("text/xml"); + } + return false; + } + + /** Determine whether the response is a SOAP 1.1 message. */ + private boolean isSoap12Response() throws IOException { + Iterator iterator = getResponseHeaders(HttpTransportConstants.HEADER_CONTENT_TYPE); + if (iterator.hasNext()) { + String contentType = iterator.next().toLowerCase(); + return contentType.contains("application/soap+xml"); } return false; } @@ -136,4 +156,7 @@ public abstract class AbstractHttpSenderConnection extends AbstractSenderConnect public final void setFault(boolean fault) { } + @Override + public final void setFaultCode(QName faultCode) throws IOException { + } } diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java index 00d2a66c..d735133a 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java @@ -24,6 +24,8 @@ import java.net.URISyntaxException; import java.util.Iterator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPConstants; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.transport.AbstractReceiverConnection; @@ -163,4 +165,22 @@ public class HttpServletConnection extends AbstractReceiverConnection } statusCodeSet = true; } + + @Override + public void setFaultCode(QName faultCode) throws IOException { + if (faultCode != null) { + if (SOAPConstants.SOAP_SENDER_FAULT.equals(faultCode)) { + getHttpServletResponse() + .setStatus(HttpTransportConstants.STATUS_BAD_REQUEST); + } + else { + getHttpServletResponse() + .setStatus(HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR); + } + } + else { + getHttpServletResponse().setStatus(HttpTransportConstants.STATUS_OK); + } + statusCodeSet = true; + } } diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java index ba3558ac..22edf8d5 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,9 @@ public interface HttpTransportConstants extends TransportConstants { /** The "204 No Content" status code. */ int STATUS_NO_CONTENT = 204; + /** The "400 Bad Request" status code. */ + int STATUS_BAD_REQUEST = 400; + /** The "404 Not Found" status code. */ int STATUS_NOT_FOUND = 404; diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java index f93ea9a0..31471cd5 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java @@ -93,7 +93,7 @@ public abstract class WebServiceMessageReceiverObjectSupport implements Initiali connection instanceof FaultAwareWebServiceConnection) { FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage) response; FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection; - faultConnection.setFault(faultResponse.hasFault()); + faultConnection.setFaultCode(faultResponse.getFaultCode()); } connection.send(messageContext.getResponse()); } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/MockWebServiceMessage.java b/spring-ws-core/src/test/java/org/springframework/ws/MockWebServiceMessage.java index db0de58c..fc37d1a4 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/MockWebServiceMessage.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/MockWebServiceMessage.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; +import javax.xml.namespace.QName; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; @@ -49,9 +50,11 @@ public class MockWebServiceMessage implements FaultAwareWebServiceMessage { private boolean fault = false; + private QName faultCode; + private String faultReason; - public MockWebServiceMessage() { + public MockWebServiceMessage() { } public MockWebServiceMessage(Source source) throws TransformerException { @@ -126,7 +129,16 @@ public class MockWebServiceMessage implements FaultAwareWebServiceMessage { this.fault = fault; } - @Override + @Override + public QName getFaultCode() { + return faultCode; + } + + public void setFaultCode(QName faultCode) { + this.faultCode = faultCode; + } + + @Override public String getFaultReason() { return faultReason; } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap11WebServiceTemplateIntegrationTestCase.java similarity index 71% rename from spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java rename to spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap11WebServiceTemplateIntegrationTestCase.java index 873f3a20..0e97531b 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap11WebServiceTemplateIntegrationTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,7 @@ import java.io.IOException; import java.util.Enumeration; import java.util.Iterator; import java.util.StringTokenizer; -import javax.activation.CommandMap; import javax.activation.DataHandler; -import javax.activation.MailcapCommandMap; import javax.mail.util.ByteArrayDataSource; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -30,8 +28,6 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeader; import javax.xml.soap.MimeHeaders; @@ -44,8 +40,18 @@ import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.AfterClass; +import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.xml.sax.SAXException; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; @@ -53,32 +59,15 @@ import org.springframework.oxm.XmlMappingException; import org.springframework.util.StringUtils; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.client.WebServiceTransportException; -import org.springframework.ws.pox.dom.DomPoxMessageFactory; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.SoapMessageFactory; -import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; import org.springframework.ws.soap.client.SoapFaultClientException; -import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; -import org.springframework.ws.transport.http.CommonsHttpMessageSender; +import org.springframework.ws.transport.http.HttpComponentsMessageSender; import org.springframework.ws.transport.support.FreePortScanner; import org.springframework.xml.transform.StringResult; import org.springframework.xml.transform.StringSource; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.servlet.Context; -import org.mortbay.jetty.servlet.ServletHolder; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; -import static org.junit.Assert.assertEquals; - -public class WebServiceTemplateIntegrationTest { +public abstract class AbstractSoap11WebServiceTemplateIntegrationTestCase { private static Server jettyServer; @@ -102,88 +91,29 @@ public class WebServiceTemplateIntegrationTest { jettyContext.addServlet(new ServletHolder(badRequestFault), "/soap/badRequestFault"); jettyContext.addServlet(new ServletHolder(new NoResponseSoapServlet()), "/soap/noResponse"); jettyContext.addServlet(new ServletHolder(new AttachmentsServlet()), "/soap/attachment"); - jettyContext.addServlet(new ServletHolder(new PoxServlet()), "/pox"); jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound"); jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server"); jettyServer.start(); } - - /** - * A workaround for the faulty XmlDataContentHandler in the SAAJ RI, which cannot handle mime types such as - * "text/xml; charset=UTF-8", causing issues with Axiom. We basically reset the command map - */ - @Before - public void removeXmlDataContentHandler() throws SOAPException { - MessageFactory messageFactory = MessageFactory.newInstance(); - SOAPMessage message = messageFactory.createMessage(); - message.createAttachmentPart(); - CommandMap.setDefaultCommandMap(new MailcapCommandMap()); - } - - @AfterClass - public static void stopJetty() throws Exception { - if (jettyServer.isRunning()) { - jettyServer.stop(); - } - } - - @Test - public void testSaaj() throws Exception { - doSoap(new SaajSoapMessageFactory(MessageFactory.newInstance())); - } - @Test - public void testAxiom() throws Exception { - doSoap(new AxiomSoapMessageFactory()); - } + @AfterClass + public static void stopJetty() throws Exception { + if (jettyServer.isRunning()) { + jettyServer.stop(); + } + } - @Test - public void testAxiomNonCaching() throws Exception { - AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory(); - axiomFactory.setPayloadCaching(false); - doSoap(axiomFactory); - } + @Before + public void createWebServiceTemplate() throws Exception { + template = new WebServiceTemplate(createMessageFactory()); + template.setMessageSender(new HttpComponentsMessageSender()); + } - @Test - public void testPox() throws Exception { - template = new WebServiceTemplate(new DomPoxMessageFactory()); - template.setMessageSender(new CommonsHttpMessageSender()); - String content = ""; - StringResult result = new StringResult(); - template.sendSourceAndReceiveToResult(baseUrl + "/pox", new StringSource(content), result); - assertXMLEqual(content, result.toString()); - try { - template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound", new StringSource(content), - new StringResult()); - Assert.fail("WebServiceTransportException expected"); - } - catch (WebServiceTransportException ex) { - //expected - } - try { - template.sendSourceAndReceiveToResult(baseUrl + "/errors/server", new StringSource(content), - result); - Assert.fail("WebServiceTransportException expected"); - } - catch (WebServiceTransportException ex) { - //expected - } - } - private void doSoap(SoapMessageFactory messageFactory) throws Exception { - template = new WebServiceTemplate(messageFactory); - template.setMessageSender(new CommonsHttpMessageSender()); - sendSourceAndReceiveToResult(); - sendSourceAndReceiveToResultNoResponse(); - marshalSendAndReceiveResponse(); - marshalSendAndReceiveNoResponse(); - notFound(); - fault(); - faultNonCompliant(); - attachment(); - } + public abstract SoapMessageFactory createMessageFactory() throws Exception; - private void sendSourceAndReceiveToResult() throws SAXException, IOException { + @Test + public void sendSourceAndReceiveToResult() throws SAXException, IOException { StringResult result = new StringResult(); boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/echo", new StringSource(messagePayload), result); @@ -191,17 +121,20 @@ public class WebServiceTemplateIntegrationTest { assertXMLEqual(messagePayload, result.toString()); } - private void sendSourceAndReceiveToResultNoResponse() { + @Test + public void sendSourceAndReceiveToResultNoResponse() { boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/noResponse", new StringSource(messagePayload), new StringResult()); Assert.assertFalse("Invalid result", b); } - private void marshalSendAndReceiveResponse() throws TransformerConfigurationException { + @Test + public void marshalSendAndReceiveResponse() throws TransformerConfigurationException { final Transformer transformer = TransformerFactory.newInstance().newTransformer(); final Object requestObject = new Object(); Marshaller marshaller = new Marshaller() { + @Override public void marshal(Object graph, Result result) throws XmlMappingException, IOException { Assert.assertEquals("Invalid object", graph, requestObject); try { @@ -212,6 +145,7 @@ public class WebServiceTemplateIntegrationTest { } } + @Override public boolean supports(Class clazz) { Assert.assertEquals("Invalid class", Object.class, clazz); return true; @@ -220,10 +154,12 @@ public class WebServiceTemplateIntegrationTest { final Object responseObject = new Object(); Unmarshaller unmarshaller = new Unmarshaller() { + @Override public Object unmarshal(Source source) throws XmlMappingException, IOException { return responseObject; } + @Override public boolean supports(Class clazz) { Assert.assertEquals("Invalid class", Object.class, clazz); return true; @@ -235,11 +171,13 @@ public class WebServiceTemplateIntegrationTest { Assert.assertEquals("Invalid response object", responseObject, result); } - private void marshalSendAndReceiveNoResponse() throws TransformerConfigurationException { + @Test + public void marshalSendAndReceiveNoResponse() throws TransformerConfigurationException { final Transformer transformer = TransformerFactory.newInstance().newTransformer(); final Object requestObject = new Object(); Marshaller marshaller = new Marshaller() { + @Override public void marshal(Object graph, Result result) throws XmlMappingException, IOException { Assert.assertEquals("Invalid object", graph, requestObject); try { @@ -250,6 +188,7 @@ public class WebServiceTemplateIntegrationTest { } } + @Override public boolean supports(Class clazz) { Assert.assertEquals("Invalid class", Object.class, clazz); return true; @@ -260,7 +199,8 @@ public class WebServiceTemplateIntegrationTest { Assert.assertNull("Invalid response object", result); } - private void notFound() { + @Test + public void notFound() { try { template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound", new StringSource(messagePayload), new StringResult()); @@ -271,7 +211,8 @@ public class WebServiceTemplateIntegrationTest { } } - private void fault() { + @Test + public void fault() { Result result = new StringResult(); try { template.sendSourceAndReceiveToResult(baseUrl + "/soap/fault", new StringSource(messagePayload), @@ -283,7 +224,8 @@ public class WebServiceTemplateIntegrationTest { } } - private void faultNonCompliant() { + @Test + public void faultNonCompliant() { Result result = new StringResult(); template.setCheckConnectionForFault(false); template.setCheckConnectionForError(false); @@ -297,10 +239,12 @@ public class WebServiceTemplateIntegrationTest { } } - private void attachment() { + @Test + public void attachment() { template.sendSourceAndReceiveToResult(baseUrl + "/soap/attachment", new StringSource(messagePayload), new WebServiceMessageCallback() { + @Override public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException { SoapMessage soapMessage = (SoapMessage) message; final String attachmentContent = "content"; @@ -325,36 +269,7 @@ public class WebServiceTemplateIntegrationTest { } } - /** Simple POX Servlet. */ - private static class PoxServlet extends HttpServlet { - - private DocumentBuilderFactory documentBuilderFactory; - - private TransformerFactory transformerFactory; - - @Override - public void init(ServletConfig servletConfig) throws ServletException { - super.init(servletConfig); - documentBuilderFactory = DocumentBuilderFactory.newInstance(); - documentBuilderFactory.setNamespaceAware(true); - transformerFactory = TransformerFactory.newInstance(); - } - - @Override - public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - try { - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - Document message = documentBuilder.parse(req.getInputStream()); - Transformer transformer = transformerFactory.newTransformer(); - transformer.transform(new DOMSource(message), new StreamResult(resp.getOutputStream())); - } - catch (Exception ex) { - throw new ServletException("POX POST failed" + ex.getMessage()); - } - } - } - - /** Abstract SOAP Servlet */ + /** Abstract SOAP Servlet */ private abstract static class AbstractSoapServlet extends HttpServlet { protected MessageFactory messageFactory = null; @@ -386,9 +301,7 @@ public class WebServiceTemplateIntegrationTest { resp.setStatus(sc); } if (reply != null) { - if (reply.saveRequired()) { - reply.saveChanges(); - } + reply.saveChanges(); if (sc == -1) { resp.setStatus(!reply.getSOAPBody().hasFault() ? HttpServletResponse.SC_OK : HttpServletResponse.SC_INTERNAL_SERVER_ERROR); diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap12WebServiceTemplateIntegrationTestCase.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap12WebServiceTemplateIntegrationTestCase.java new file mode 100644 index 00000000..ba4ae011 --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AbstractSoap12WebServiceTemplateIntegrationTestCase.java @@ -0,0 +1,408 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.StringTokenizer; +import javax.activation.CommandMap; +import javax.activation.DataHandler; +import javax.activation.MailcapCommandMap; +import javax.mail.util.ByteArrayDataSource; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.MimeHeader; +import javax.xml.soap.MimeHeaders; +import javax.xml.soap.SOAPBody; +import javax.xml.soap.SOAPConstants; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPMessage; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.AfterClass; +import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.xml.sax.SAXException; + +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.oxm.XmlMappingException; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.client.WebServiceTransportException; +import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.client.SoapFaultClientException; +import org.springframework.ws.transport.http.HttpComponentsMessageSender; +import org.springframework.ws.transport.support.FreePortScanner; +import org.springframework.xml.transform.StringResult; +import org.springframework.xml.transform.StringSource; + +public abstract class AbstractSoap12WebServiceTemplateIntegrationTestCase { + + private static Server jettyServer; + + private static String baseUrl; + + + private WebServiceTemplate template; + + private String messagePayload = ""; + + @BeforeClass + public static void startJetty() throws Exception { + int port = FreePortScanner.getFreePort(); + baseUrl = "http://localhost:" + port; + jettyServer = new Server(port); + Context jettyContext = new Context(jettyServer, "/"); + jettyContext.addServlet(new ServletHolder(new EchoSoapServlet()), "/soap/echo"); + jettyContext.addServlet(new ServletHolder(new SoapReceiverFaultServlet()), "/soap/receiverFault"); + jettyContext.addServlet(new ServletHolder(new SoapSenderFaultServlet()), "/soap/senderFault"); + jettyContext.addServlet(new ServletHolder(new NoResponseSoapServlet()), "/soap/noResponse"); + jettyContext.addServlet(new ServletHolder(new AttachmentsServlet()), "/soap/attachment"); + jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound"); + jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server"); + jettyServer.start(); + } + + @AfterClass + public static void stopJetty() throws Exception { + if (jettyServer.isRunning()) { + jettyServer.stop(); + } + } + + /** + * A workaround for the faulty XmlDataContentHandler in the SAAJ RI, which cannot handle mime types such as + * "text/xml; charset=UTF-8", causing issues with Axiom. We basically reset the command map + */ + @Before + public void removeXmlDataContentHandler() throws SOAPException { + MessageFactory messageFactory = MessageFactory.newInstance(); + SOAPMessage message = messageFactory.createMessage(); + message.createAttachmentPart(); + CommandMap.setDefaultCommandMap(new MailcapCommandMap()); + } + + @Before + public void createWebServiceTemplate() throws Exception { + template = new WebServiceTemplate(createMessageFactory()); + template.setMessageSender(new HttpComponentsMessageSender()); + } + + + public abstract SoapMessageFactory createMessageFactory() throws Exception; + + @Test + public void sendSourceAndReceiveToResult() throws SAXException, IOException { + StringResult result = new StringResult(); + boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/echo", + new StringSource(messagePayload), result); + Assert.assertTrue("Invalid result", b); + assertXMLEqual(messagePayload, result.toString()); + } + + @Test + public void sendSourceAndReceiveToResultNoResponse() { + boolean b = template.sendSourceAndReceiveToResult(baseUrl + "/soap/noResponse", + new StringSource(messagePayload), new StringResult()); + Assert.assertFalse("Invalid result", b); + } + + @Test + public void marshalSendAndReceiveResponse() throws TransformerConfigurationException { + final Transformer transformer = TransformerFactory.newInstance().newTransformer(); + final Object requestObject = new Object(); + Marshaller marshaller = new Marshaller() { + + @Override + public void marshal(Object graph, Result result) throws XmlMappingException, IOException { + Assert.assertEquals("Invalid object", graph, requestObject); + try { + transformer.transform(new StringSource(messagePayload), result); + } + catch (TransformerException e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public boolean supports(Class clazz) { + Assert.assertEquals("Invalid class", Object.class, clazz); + return true; + } + }; + final Object responseObject = new Object(); + Unmarshaller unmarshaller = new Unmarshaller() { + + @Override + public Object unmarshal(Source source) throws XmlMappingException, IOException { + return responseObject; + } + + @Override + public boolean supports(Class clazz) { + Assert.assertEquals("Invalid class", Object.class, clazz); + return true; + } + }; + template.setMarshaller(marshaller); + template.setUnmarshaller(unmarshaller); + Object result = template.marshalSendAndReceive(baseUrl + "/soap/echo", requestObject); + Assert.assertEquals("Invalid response object", responseObject, result); + } + + @Test + public void marshalSendAndReceiveNoResponse() throws TransformerConfigurationException { + final Transformer transformer = TransformerFactory.newInstance().newTransformer(); + final Object requestObject = new Object(); + Marshaller marshaller = new Marshaller() { + + @Override + public void marshal(Object graph, Result result) throws XmlMappingException, IOException { + Assert.assertEquals("Invalid object", graph, requestObject); + try { + transformer.transform(new StringSource(messagePayload), result); + } + catch (TransformerException e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public boolean supports(Class clazz) { + Assert.assertEquals("Invalid class", Object.class, clazz); + return true; + } + }; + template.setMarshaller(marshaller); + Object result = template.marshalSendAndReceive(baseUrl + "/soap/noResponse", requestObject); + Assert.assertNull("Invalid response object", result); + } + + @Test + public void notFound() { + try { + template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound", + new StringSource(messagePayload), new StringResult()); + Assert.fail("WebServiceTransportException expected"); + } + catch (WebServiceTransportException ex) { + //expected + } + } + + @Test + public void receiverFault() { + Result result = new StringResult(); + try { + template.sendSourceAndReceiveToResult(baseUrl + "/soap/receiverFault", new StringSource(messagePayload), + result); + Assert.fail("SoapFaultClientException expected"); + } + catch (SoapFaultClientException ex) { + //expected + } + } + + @Test + public void senderFault() { + Result result = new StringResult(); + try { + template.sendSourceAndReceiveToResult(baseUrl + "/soap/senderFault", new StringSource(messagePayload), + result); + Assert.fail("SoapFaultClientException expected"); + } + catch (SoapFaultClientException ex) { + //expected + } + } + + @Test + public void attachment() { + template.sendSourceAndReceiveToResult(baseUrl + "/soap/attachment", new StringSource(messagePayload), + new WebServiceMessageCallback() { + + @Override + public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException { + SoapMessage soapMessage = (SoapMessage) message; + final String attachmentContent = "content"; + soapMessage.addAttachment("attachment-1", + new DataHandler(new ByteArrayDataSource(attachmentContent, "text/plain"))); + } + }, new StringResult()); + } + + /** Servlet that returns and error message for a given status code. */ + private static class ErrorServlet extends HttpServlet { + + private int sc; + + private ErrorServlet(int sc) { + this.sc = sc; + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.sendError(sc); + } + } + + /** Abstract SOAP Servlet */ + private abstract static class AbstractSoapServlet extends HttpServlet { + + protected MessageFactory messageFactory = null; + + private final int sc = -1; + + @Override + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + try { + messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL); + } + catch (SOAPException ex) { + throw new ServletException("Unable to create message factory" + ex.getMessage()); + } + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + MimeHeaders headers = getHeaders(req); + SOAPMessage request = messageFactory.createMessage(headers, req.getInputStream()); + SOAPMessage reply = onMessage(request); + if (reply != null) { + reply.saveChanges(); + SOAPBody replyBody = reply.getSOAPBody(); + if (!replyBody.hasFault()) { + resp.setStatus(HttpServletResponse.SC_OK); + } + else { + if (replyBody.getFault().getFaultCodeAsQName() + .equals(SOAPConstants.SOAP_SENDER_FAULT)) { + resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); + + } + else { + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + putHeaders(reply.getMimeHeaders(), resp); + reply.writeTo(resp.getOutputStream()); + } + else { + resp.setStatus(HttpServletResponse.SC_ACCEPTED); + } + } + catch (Exception ex) { + throw new ServletException("SAAJ POST failed " + ex.getMessage(), ex); + } + } + + private MimeHeaders getHeaders(HttpServletRequest httpServletRequest) { + Enumeration enumeration = httpServletRequest.getHeaderNames(); + MimeHeaders headers = new MimeHeaders(); + while (enumeration.hasMoreElements()) { + String headerName = (String) enumeration.nextElement(); + String headerValue = httpServletRequest.getHeader(headerName); + StringTokenizer values = new StringTokenizer(headerValue, ","); + while (values.hasMoreTokens()) { + headers.addHeader(headerName, values.nextToken().trim()); + } + } + return headers; + } + + private void putHeaders(MimeHeaders headers, HttpServletResponse res) { + Iterator it = headers.getAllHeaders(); + while (it.hasNext()) { + MimeHeader header = (MimeHeader) it.next(); + String[] values = headers.getHeader(header.getName()); + for (String value : values) { + res.addHeader(header.getName(), value); + } + } + } + + protected abstract SOAPMessage onMessage(SOAPMessage message) throws SOAPException; + } + + private static class EchoSoapServlet extends AbstractSoapServlet { + + @Override + protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException { + return message; + } + } + + private static class NoResponseSoapServlet extends AbstractSoapServlet { + + @Override + protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException { + return null; + } + } + + private static class SoapReceiverFaultServlet extends AbstractSoapServlet { + + @Override + protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException { + SOAPMessage response = messageFactory.createMessage(); + SOAPBody body = response.getSOAPBody(); + body.addFault(SOAPConstants.SOAP_RECEIVER_FAULT, "Receiver Fault"); + return response; + } + } + + private static class SoapSenderFaultServlet extends AbstractSoapServlet { + + @Override + protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException { + SOAPMessage response = messageFactory.createMessage(); + SOAPBody body = response.getSOAPBody(); + body.addFault(SOAPConstants.SOAP_SENDER_FAULT, "Sender Fault"); + return response; + } + } + + private static class AttachmentsServlet extends AbstractSoapServlet { + + @Override + protected SOAPMessage onMessage(SOAPMessage message) throws SOAPException { + assertEquals("No attachments found", 1, message.countAttachments()); + return null; + } + } + +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..08071109 --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest + extends AbstractSoap11WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + return new AxiomSoapMessageFactory(); + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..503d5d3d --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.SoapVersion; +import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest + extends AbstractSoap12WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory(); + messageFactory.setSoapVersion(SoapVersion.SOAP_12); + return messageFactory; + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap11WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap11WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..22b44509 --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap11WebServiceTemplateIntegrationTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class AxiomStreamingSoap11WebServiceTemplateIntegrationTest + extends AbstractSoap11WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory(); + axiomFactory.setPayloadCaching(false); + return axiomFactory; + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap12WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap12WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..34cd448e --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/AxiomStreamingSoap12WebServiceTemplateIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.SoapVersion; +import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class AxiomStreamingSoap12WebServiceTemplateIntegrationTest + extends AbstractSoap12WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory(); + axiomFactory.setSoapVersion(SoapVersion.SOAP_12); + axiomFactory.setPayloadCaching(false); + return axiomFactory; + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/DomPoxWebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/DomPoxWebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..f256066c --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/DomPoxWebServiceTemplateIntegrationTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import java.io.IOException; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.w3c.dom.Document; + +import org.springframework.ws.client.WebServiceTransportException; +import org.springframework.ws.pox.dom.DomPoxMessageFactory; +import org.springframework.ws.transport.http.HttpComponentsMessageSender; +import org.springframework.ws.transport.support.FreePortScanner; +import org.springframework.xml.transform.StringResult; +import org.springframework.xml.transform.StringSource; + +public class DomPoxWebServiceTemplateIntegrationTest { + + private static Server jettyServer; + + private static String baseUrl; + + @BeforeClass + public static void startJetty() throws Exception { + int port = FreePortScanner.getFreePort(); + baseUrl = "http://localhost:" + port; + jettyServer = new Server(port); + Context jettyContext = new Context(jettyServer, "/"); + jettyContext.addServlet(new ServletHolder(new PoxServlet()), "/pox"); + jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound"); + jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server"); + jettyServer.start(); + } + + @AfterClass + public static void stopJetty() throws Exception { + if (jettyServer.isRunning()) { + jettyServer.stop(); + } + } + + @Test + public void domPox() throws Exception { + WebServiceTemplate template = new WebServiceTemplate(new DomPoxMessageFactory()); + template.setMessageSender(new HttpComponentsMessageSender()); + String content = ""; + StringResult result = new StringResult(); + template.sendSourceAndReceiveToResult(baseUrl + "/pox", new StringSource(content), + result); + assertXMLEqual(content, result.toString()); + try { + template.sendSourceAndReceiveToResult(baseUrl + "/errors/notfound", + new StringSource(content), new StringResult()); + Assert.fail("WebServiceTransportException expected"); + } + catch (WebServiceTransportException ex) { + //expected + } + try { + template.sendSourceAndReceiveToResult(baseUrl + "/errors/server", + new StringSource(content), result); + Assert.fail("WebServiceTransportException expected"); + } + catch (WebServiceTransportException ex) { + //expected + } + } + + /** Servlet that returns and error message for a given status code. */ + private static class ErrorServlet extends HttpServlet { + + private int sc; + + private ErrorServlet(int sc) { + this.sc = sc; + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.sendError(sc); + } + } + + /** Simple POX Servlet. */ + private static class PoxServlet extends HttpServlet { + + private DocumentBuilderFactory documentBuilderFactory; + + private TransformerFactory transformerFactory; + + @Override + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + transformerFactory = TransformerFactory.newInstance(); + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document message = documentBuilder.parse(req.getInputStream()); + Transformer transformer = transformerFactory.newTransformer(); + transformer.transform(new DOMSource(message), new StreamResult(resp.getOutputStream())); + } + catch (Exception ex) { + throw new ServletException("POX POST failed" + ex.getMessage()); + } + } + } + +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap11WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap11WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..5fa55161 --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap11WebServiceTemplateIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import javax.xml.soap.MessageFactory; +import javax.xml.soap.SOAPConstants; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class SaajSoap11WebServiceTemplateIntegrationTest + extends AbstractSoap11WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); + return new SaajSoapMessageFactory(messageFactory); + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap12WebServiceTemplateIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap12WebServiceTemplateIntegrationTest.java new file mode 100644 index 00000000..496adf3a --- /dev/null +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/SaajSoap12WebServiceTemplateIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2014 the original author or authors. + * + * Licensed 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.springframework.ws.client.core; + +import javax.xml.soap.MessageFactory; +import javax.xml.soap.SOAPConstants; + +import org.springframework.ws.soap.SoapMessageFactory; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; + +/** + * @author Arjen Poutsma + */ +public class SaajSoap12WebServiceTemplateIntegrationTest + extends AbstractSoap12WebServiceTemplateIntegrationTestCase { + + @Override + public SoapMessageFactory createMessageFactory() throws Exception { + MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL); + return new SaajSoapMessageFactory(messageFactory); + } +} diff --git a/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java b/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java index da78f171..b4d22a45 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,11 @@ import java.net.URI; import javax.xml.transform.Result; import javax.xml.transform.Source; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; import org.springframework.ws.MockWebServiceMessage; @@ -37,12 +42,6 @@ import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.xml.transform.StringResult; import org.springframework.xml.transform.StringSource; -import org.junit.Before; -import org.junit.Test; - -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; - public class WebServiceTemplateTest { private WebServiceTemplate template; @@ -60,10 +59,12 @@ public class WebServiceTemplateTest { expect(connectionMock.getUri()).andReturn(expectedUri).anyTimes(); template.setMessageSender(new WebServiceMessageSender() { + @Override public WebServiceConnection createConnection(URI uri) throws IOException { return connectionMock; } + @Override public boolean supports(URI uri) { assertEquals("Invalid uri", expectedUri, uri); return true; @@ -339,10 +340,12 @@ public class WebServiceTemplateTest { final URI customUri = new URI("http://www.springframework.org/spring-ws/custom"); template.setMessageSender(new WebServiceMessageSender() { + @Override public WebServiceConnection createConnection(URI uri) throws IOException { return connectionMock; } + @Override public boolean supports(URI uri) { assertEquals("Invalid uri", customUri, uri); return true; @@ -472,10 +475,12 @@ public class WebServiceTemplateTest { template.setMessageSender(new WebServiceMessageSender() { + @Override public WebServiceConnection createConnection(URI uri) throws IOException { return connectionMock; } + @Override public boolean supports(URI uri) { assertEquals("Invalid uri", providerUri, uri); return true; diff --git a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java index d1f87200..5958af71 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.ws.transport.http; +import java.io.IOException; import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeaders; import javax.xml.soap.SOAPConstants; @@ -23,19 +24,20 @@ import javax.xml.soap.SOAPMessage; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; + import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.ws.soap.SoapVersion; import org.springframework.ws.soap.saaj.SaajSoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; import org.springframework.xml.transform.StringResult; import org.springframework.xml.transform.StringSource; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; - public class HttpServletConnectionTest { private HttpServletConnection connection; @@ -69,7 +71,7 @@ public class HttpServletConnectionTest { } @Test - public void testReceive() throws Exception { + public void receive() throws Exception { byte[] bytes = SOAP_CONTENT.getBytes("UTF-8"); httpServletRequest.addHeader("Content-Type", "text/xml"); httpServletRequest.addHeader("Content-Length", Integer.toString(bytes.length)); @@ -84,13 +86,13 @@ public class HttpServletConnectionTest { SOAPMessage saajMessage = message.getSaajMessage(); String[] headerValues = saajMessage.getMimeHeaders().getHeader(HEADER_NAME); Assert.assertNotNull("Response has no header", headerValues); - Assert.assertEquals("Response has invalid header", 1, headerValues.length); - Assert.assertEquals("Response has invalid header values", HEADER_VALUE, headerValues[0]); + assertEquals("Response has invalid header", 1, headerValues.length); + assertEquals("Response has invalid header values", HEADER_VALUE, headerValues[0]); } @Test - public void testSend() throws Exception { - SaajSoapMessage message = (SaajSoapMessage) messageFactory.createWebServiceMessage(); + public void send() throws Exception { + SaajSoapMessage message = messageFactory.createWebServiceMessage(); SOAPMessage saajMessage = message.getSaajMessage(); MimeHeaders mimeHeaders = saajMessage.getMimeHeaders(); mimeHeaders.addHeader(HEADER_NAME, HEADER_VALUE); @@ -99,8 +101,24 @@ public class HttpServletConnectionTest { connection.send(message); - Assert.assertEquals("Invalid header", HEADER_VALUE, httpServletResponse.getHeader(HEADER_NAME)); + assertEquals("Invalid header", HEADER_VALUE, + httpServletResponse.getHeader(HEADER_NAME)); assertXMLEqual("Invalid content", SOAP_CONTENT, httpServletResponse.getContentAsString()); } + @Test + public void faultCodes() throws IOException { + connection.setFaultCode(SoapVersion.SOAP_11.getClientOrSenderFaultName()); + assertEquals(500, httpServletResponse.getStatus()); + + connection.setFaultCode(SoapVersion.SOAP_11.getServerOrReceiverFaultName()); + assertEquals(500, httpServletResponse.getStatus()); + + connection.setFaultCode(SoapVersion.SOAP_12.getClientOrSenderFaultName()); + assertEquals(400, httpServletResponse.getStatus()); + + connection.setFaultCode(SoapVersion.SOAP_12.getServerOrReceiverFaultName()); + assertEquals(500, httpServletResponse.getStatus()); + } + } \ No newline at end of file diff --git a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java index 9f34ccd2..2a4c95d3 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java @@ -1,11 +1,11 @@ /* - * Copyright 2005-2012 the original author or authors. + * Copyright 2005-2014 the original author or authors. * * Licensed 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 + * 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, @@ -20,6 +20,11 @@ import java.io.InputStream; import java.io.OutputStream; import javax.servlet.http.HttpServletResponse; +import static org.easymock.EasyMock.*; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.ws.FaultAwareWebServiceMessage; @@ -27,14 +32,9 @@ import org.springframework.ws.InvalidXmlException; import org.springframework.ws.NoEndpointFoundException; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.context.MessageContext; +import org.springframework.ws.soap.SoapVersion; import org.springframework.ws.transport.WebServiceMessageReceiver; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import static org.easymock.EasyMock.*; - public class WebServiceMessageReceiverHandlerAdapterTest { private static final String REQUEST = "