From 06cc6378f90f03ade7cc6cdb4bbe183a23d800cc Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 8 Jun 2007 23:57:54 +0000 Subject: [PATCH] Fixed SWS-137: Added checkConnectionForFault to WebServiceTemplate to with Web services which are not WS-I compliant --- core/pom.xml | 8 +- .../ws/FaultAwareWebServiceMessage.java | 46 ++++++++++ .../springframework/ws/WebServiceMessage.java | 33 ++------ .../ws/client/WebServiceFaultException.java | 18 ++-- .../core/SimpleFaultMessageResolver.java | 8 +- .../ws/client/core/WebServiceTemplate.java | 83 +++++++++++++++---- .../ws/server/MessageDispatcher.java | 8 +- .../springframework/ws/soap/SoapMessage.java | 3 +- .../FaultAwareWebServiceConnection.java | 22 ++++- .../http/AbstractHttpSenderConnection.java | 40 +++++---- .../transport/http/CommonsHttpConnection.java | 12 +-- .../transport/http/HttpServletConnection.java | 52 +++++++----- .../http/HttpTransportConstants.java | 45 ++++++++++ .../ws/transport/http/HttpUrlConnection.java | 2 +- .../http/MessageDispatcherServlet.java | 4 +- ...ebServiceMessageReceiverObjectSupport.java | 14 +++- .../ws/AbstractWebServiceMessageTestCase.java | 4 - .../ws/MockWebServiceMessage.java | 2 +- .../core/SimpleFaultMessageResolverTest.java | 6 +- .../WebServiceTemplateIntegrationTest.java | 11 +-- .../client/core/WebServiceTemplateTest.java | 10 ++- .../AbstractSaajImplementationTestCase.java | 3 +- ...viceMessageReceiverHandlerAdapterTest.java | 12 +-- src/changes/changes.xml | 3 + 24 files changed, 317 insertions(+), 132 deletions(-) create mode 100644 core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java create mode 100644 core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java diff --git a/core/pom.xml b/core/pom.xml index ae3ff6ce..885b6669 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,5 +1,7 @@ - + spring-ws org.springframework.ws @@ -135,10 +137,6 @@ javax.servlet servlet-api - - javax.mail - mail - javax.activation activation diff --git a/core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java b/core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java new file mode 100644 index 00000000..7944ffee --- /dev/null +++ b/core/src/main/java/org/springframework/ws/FaultAwareWebServiceMessage.java @@ -0,0 +1,46 @@ +/* + * Copyright 2007 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; + +import org.springframework.ws.soap.SoapFault; +import org.springframework.ws.soap.SoapMessage; + +/** + * Sub-interface of {@link WebServiceMessage} that can contain special Fault messages. Fault messages (such as {@link + * SoapFault} SOAP Faults) often require different processing rules. + * + * @author Arjen Poutsma + * @see SoapMessage + */ +public interface FaultAwareWebServiceMessage extends WebServiceMessage { + + /** + * Does this message have a fault? + * + * @return true if the message has a fault. + * @see #getFaultReason() + */ + boolean hasFault(); + + /** + * Returns the fault reason message. + * + * @return the fault reason message, if any; returns null when no fault is present. + * @see #hasFault() + */ + String getFaultReason(); +} diff --git a/core/src/main/java/org/springframework/ws/WebServiceMessage.java b/core/src/main/java/org/springframework/ws/WebServiceMessage.java index 975658da..5615a8b3 100644 --- a/core/src/main/java/org/springframework/ws/WebServiceMessage.java +++ b/core/src/main/java/org/springframework/ws/WebServiceMessage.java @@ -23,7 +23,7 @@ import javax.xml.transform.Source; /** * Represents a protocol-agnostic XML message. - * + *

*

Contains methods that provide access to the payload of the message. * * @author Arjen Poutsma @@ -33,17 +33,16 @@ import javax.xml.transform.Source; public interface WebServiceMessage { /** - * Returns the contents of the message as a {@link Source}. - *

Depending on the implementation, this can be retrieved multiple times, - * or just a single time. + * Returns the contents of the message as a {@link Source}.

Depending on the implementation, this can be + * retrieved multiple times, or just a single time. * * @return the message contents */ Source getPayloadSource(); /** - * Returns the contents of the message as a {@link Result}. - *

Implementations that are read-only will throw an {@link UnsupportedOperationException}. + * Returns the contents of the message as a {@link Result}.

Implementations that are read-only will throw an + * {@link UnsupportedOperationException}. * * @return the message contents * @throws UnsupportedOperationException if the message is read-only @@ -51,30 +50,12 @@ public interface WebServiceMessage { Result getPayloadResult(); /** - * Writes the entire message to the given output stream. - *

If the given stream is an instance of - * {@link org.springframework.ws.transport.TransportOutputStream}, the - * corresponding headers will be written as well. + * Writes the entire message to the given output stream.

If the given stream is an instance of {@link + * org.springframework.ws.transport.TransportOutputStream}, the corresponding headers will be written as well. * * @param outputStream the stream to write to * @throws IOException if an I/O exception occurs */ void writeTo(OutputStream outputStream) throws IOException; - /** - * Does this message have a fault? - * - * @return true if the message has a fault. - * @see #getFaultReason() - */ - boolean hasFault(); - - /** - * Returns the fault reason message. - * - * @return the fault reason message, if any; returns null when no fault is present. - * @see #hasFault() - */ - String getFaultReason(); - } diff --git a/core/src/main/java/org/springframework/ws/client/WebServiceFaultException.java b/core/src/main/java/org/springframework/ws/client/WebServiceFaultException.java index 0042b3cb..63f02ade 100644 --- a/core/src/main/java/org/springframework/ws/client/WebServiceFaultException.java +++ b/core/src/main/java/org/springframework/ws/client/WebServiceFaultException.java @@ -16,7 +16,7 @@ package org.springframework.ws.client; -import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.FaultAwareWebServiceMessage; /** * Thrown by SimpleFaultMessageResolver when the response message has a fault. @@ -25,20 +25,26 @@ import org.springframework.ws.WebServiceMessage; */ public class WebServiceFaultException extends WebServiceClientException { - private final WebServiceMessage webServiceMessage; + private final FaultAwareWebServiceMessage faultMessage; + + /** Create a new instance of the WebServiceFaultException class. */ + public WebServiceFaultException(String msg) { + super(msg); + faultMessage = null; + } /** * Create a new instance of the WebServiceFaultException class. * * @param faultMessage the fault message */ - public WebServiceFaultException(WebServiceMessage faultMessage) { + public WebServiceFaultException(FaultAwareWebServiceMessage faultMessage) { super(faultMessage.getFaultReason()); - webServiceMessage = faultMessage; + this.faultMessage = faultMessage; } /** Returns the fault message. */ - public WebServiceMessage getWebServiceMessage() { - return webServiceMessage; + public FaultAwareWebServiceMessage getWebServiceMessage() { + return faultMessage; } } diff --git a/core/src/main/java/org/springframework/ws/client/core/SimpleFaultMessageResolver.java b/core/src/main/java/org/springframework/ws/client/core/SimpleFaultMessageResolver.java index 7102d9e5..fdfb0d78 100644 --- a/core/src/main/java/org/springframework/ws/client/core/SimpleFaultMessageResolver.java +++ b/core/src/main/java/org/springframework/ws/client/core/SimpleFaultMessageResolver.java @@ -16,6 +16,7 @@ package org.springframework.ws.client.core; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.client.WebServiceFaultException; @@ -29,6 +30,11 @@ public class SimpleFaultMessageResolver implements FaultMessageResolver { /** Throws a new WebServiceFaultException. */ public void resolveFault(WebServiceMessage message) { - throw new WebServiceFaultException(message); + if (message instanceof FaultAwareWebServiceMessage) { + throw new WebServiceFaultException((FaultAwareWebServiceMessage) message); + } + else { + throw new WebServiceFaultException("Message has unknown fault: " + message); + } } } diff --git a/core/src/main/java/org/springframework/ws/client/core/WebServiceTemplate.java b/core/src/main/java/org/springframework/ws/client/core/WebServiceTemplate.java index dc497333..db0c73e1 100644 --- a/core/src/main/java/org/springframework/ws/client/core/WebServiceTemplate.java +++ b/core/src/main/java/org/springframework/ws/client/core/WebServiceTemplate.java @@ -33,6 +33,7 @@ import org.springframework.oxm.Unmarshaller; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.WebServiceIOException; @@ -73,6 +74,8 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService private String defaultUri; + private boolean checkConnectionForFault = true; + /** Creates a new WebServiceTemplate using default settings. */ public WebServiceTemplate() { initDefaultStrategies(); @@ -142,6 +145,25 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService this.faultMessageResolver = faultMessageResolver; } + /** + * Indicates whether the {@link FaultAwareWebServiceConnection#hasFault() connection} should be checked for fault + * indicators (true), or whether we should rely on the {@link FaultAwareWebServiceMessage#hasFault() + * message} only (false). The default is true. + *

+ * When using a HTTP transport, this property defines whether to check the HTTP response status code for fault + * indicators. Both the SOAP specification and the WS-I Basic Profile define that a Web service must return a "500 + * Internal Server Error" HTTP status code if the response envelope is a Fault. Setting this property to + * false allows this template to deal with non-conformant services. + * + * @see #hasFault(WebServiceConnection,WebServiceMessage) + * @see SOAP 1.1 specification + * @see WS-I Basic + * Profile + */ + public void setCheckConnectionForFault(boolean checkConnectionForFault) { + this.checkConnectionForFault = checkConnectionForFault; + } + /** * Initialize the default implementations for the template's strategies: {@link SoapFaultMessageResolver}, {@link * org.springframework.ws.soap.saaj.SaajSoapMessageFactory}, and {@link HttpUrlConnectionMessageSender}. @@ -358,7 +380,7 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService requestCallback.doWithMessage(request); } sendRequest(connection, request); - if (connection.hasError()) { + if (hasError(connection, request)) { return handleError(connection, request); } WebServiceMessage response = connection.receive(getMessageFactory()); @@ -396,23 +418,28 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService } /** - * Determines whether the given connection or message context has a fault. Default implementation checks whether the - * connection is a {@link FaultAwareWebServiceConnection}, and calls returns {@link - * FaultAwareWebServiceConnection#hasFault()} if so. Otherwise, {@link WebServiceMessage#hasFault()} is returned - * (which required a full message parse). + * Determines whether the given connection or message context has an error. + *

+ * This implementation checks the {@link WebServiceConnection#hasError() connection} first. If it indicates an + * error, it makes sure that it is not a {@link FaultAwareWebServiceConnection#hasFault() fault}. * * @param connection the connection (possibly a {@link FaultAwareWebServiceConnection} - * @param response the response message - * @return true if either the connection or the message has a fault; false otherwise + * @param request the response message (possibly a {@link FaultAwareWebServiceMessage} + * @return true if the connection has an error; false otherwise * @throws IOException in case of I/O errors */ - protected boolean hasFault(WebServiceConnection connection, WebServiceMessage response) throws IOException { - if (connection instanceof FaultAwareWebServiceConnection) { - return ((FaultAwareWebServiceConnection) connection).hasFault(); - } - else { - return response.hasFault(); + protected boolean hasError(WebServiceConnection connection, WebServiceMessage request) throws IOException { + if (connection.hasError()) { + // this could be a fault rather than an error + if (connection instanceof FaultAwareWebServiceConnection) { + FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection; + if (faultConnection.hasFault() && request instanceof FaultAwareWebServiceMessage) { + return false; + } + } + return true; } + return false; } /** @@ -425,10 +452,38 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService *WebServiceMessageExtractor)}, if any */ protected Object handleError(WebServiceConnection connection, WebServiceMessage request) throws IOException { - logger.debug("Received " + connection.getErrorMessage() + " error for request [" + request + "]"); + logger.warn("Received " + connection.getErrorMessage() + " error for request [" + request + "]"); throw new WebServiceTransportException(connection.getErrorMessage()); } + /** + * Determines whether the given connection or message has a fault. + *

+ * This implementation checks the {@link FaultAwareWebServiceConnection#hasFault() connection} if the {@link + * #setCheckConnectionForFault(boolean) checkConnectionForFault} property is true, and defaults to the {@link + * FaultAwareWebServiceMessage#hasFault() message} otherwise. + * + * @param connection the connection (possibly a {@link FaultAwareWebServiceConnection} + * @param response the response message (possibly a {@link FaultAwareWebServiceMessage} + * @return true if either the connection or the message has a fault; false otherwise + * @throws IOException in case of I/O errors + */ + protected boolean hasFault(WebServiceConnection connection, WebServiceMessage response) throws IOException { + if (checkConnectionForFault && connection instanceof FaultAwareWebServiceConnection) { + // check whether the connection has a fault (i.e. status code 500 in HTTP) + FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection; + if (!faultConnection.hasFault()) { + return false; + } + } + if (response instanceof FaultAwareWebServiceMessage) { + // either the connection has a fault, or checkConnectionForFault is false: let's verify the fault + FaultAwareWebServiceMessage faultMessage = (FaultAwareWebServiceMessage) response; + return faultMessage.hasFault(); + } + return false; + } + /** * Handles an fault in the given response message. The default implementation invokes the {@link * FaultMessageResolver fault resolver} if registered, or invokes {@link #handleError(WebServiceConnection, diff --git a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java index 8c2f8219..e6e51429 100644 --- a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java +++ b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java @@ -36,7 +36,9 @@ import org.springframework.core.io.Resource; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.NoEndpointFoundException; +import org.springframework.ws.WebServiceMessage; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.endpoint.MessageEndpoint; import org.springframework.ws.server.endpoint.PayloadEndpoint; @@ -322,8 +324,12 @@ public class MessageDispatcher implements WebServiceMessageReceiver, BeanNameAwa MessageContext messageContext) throws Exception { if (mappedEndpoint != null && messageContext.hasResponse() && !ObjectUtils.isEmpty(mappedEndpoint.getInterceptors())) { + boolean hasFault = false; + WebServiceMessage response = messageContext.getResponse(); + if (response instanceof FaultAwareWebServiceMessage) { + hasFault = ((FaultAwareWebServiceMessage) response).hasFault(); + } boolean resume = true; - boolean hasFault = messageContext.getResponse().hasFault(); for (int i = interceptorIndex; resume && i >= 0; i--) { EndpointInterceptor interceptor = mappedEndpoint.getInterceptors()[i]; if (!hasFault) { diff --git a/core/src/main/java/org/springframework/ws/soap/SoapMessage.java b/core/src/main/java/org/springframework/ws/soap/SoapMessage.java index ef126179..e0546236 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapMessage.java +++ b/core/src/main/java/org/springframework/ws/soap/SoapMessage.java @@ -16,6 +16,7 @@ package org.springframework.ws.soap; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.mime.MimeMessage; /** @@ -28,7 +29,7 @@ import org.springframework.ws.mime.MimeMessage; * @see #getPayloadResult() * @see #getEnvelope() */ -public interface SoapMessage extends MimeMessage { +public interface SoapMessage extends MimeMessage, FaultAwareWebServiceMessage { /** Returns the SoapEnvelope associated with this SoapMessage. */ SoapEnvelope getEnvelope() throws SoapEnvelopeException; diff --git a/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java b/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java index d4122a9d..2302938e 100644 --- a/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java @@ -18,20 +18,34 @@ package org.springframework.ws.transport; import java.io.IOException; +import org.springframework.ws.soap.SoapFault; + /** * Sub-interface of {@link WebServiceConnection} that is aware of any Fault messages received. Fault messages (such as - * SOAP Faults often require different processing rules. Typically, fault detection is done by inspecting connection - * error codes, etc. + * {@link SoapFault} SOAP Faults) often require different processing rules. Typically, fault detection is done by + * inspecting connection error codes, etc. * * @author Arjen Poutsma */ public interface FaultAwareWebServiceConnection extends WebServiceConnection { /** - * Indicates whether this connection has a Fault. + * Indicates whether this connection received a fault. + *

+ * Typically implemented by looking at an HTTP status code. * - * @return true if this connection has a fault; false otherwise. + * @return true if this connection received a fault; false otherwise. + * @throws IOException in case of I/O errors */ boolean hasFault() throws IOException; + /** + * Sets whether this connection will send a fault. + *

+ * Typically implemented by setting an HTTP status code. + * + * @param fault true if this will send a fault; false otherwise. + * @throws IOException in case of I/O errors + */ + void setFault(boolean fault) throws IOException; } diff --git a/core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java b/core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java index cd4c87ca..58e988b1 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/http/AbstractHttpSenderConnection.java @@ -35,16 +35,20 @@ import org.springframework.ws.transport.WebServiceConnection; public abstract class AbstractHttpSenderConnection extends AbstractSenderConnection implements FaultAwareWebServiceConnection { - protected static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding"; - - protected static final String ENCODING_GZIP = "gzip"; - - protected static final int HTTP_STATUS_INTERNAL_SERVER_ERROR = 500; - /** Buffer used for reading the response, when the content length is invalid. */ private byte[] responseBuffer; + public final boolean hasError() throws IOException { + return getResponseCode() / 100 != 2; + } + + /* + * Receiving response + */ protected final boolean hasResponse() throws IOException { + if (getResponseCode() == HttpTransportConstants.STATUS_ACCEPTED) { + return false; + } long contentLength = getResponseContentLength(); if (contentLength < 0) { if (responseBuffer == null) { @@ -55,15 +59,6 @@ public abstract class AbstractHttpSenderConnection extends AbstractSenderConnect return contentLength > 0; } - public final boolean hasError() throws IOException { - int code = getResponseCode(); - return code / 100 != 2 && code != HTTP_STATUS_INTERNAL_SERVER_ERROR; - } - - public final boolean hasFault() throws IOException { - return getResponseCode() == HTTP_STATUS_INTERNAL_SERVER_ERROR; - } - protected final InputStream getResponseInputStream() throws IOException { InputStream inputStream; if (responseBuffer != null) { @@ -77,9 +72,10 @@ public abstract class AbstractHttpSenderConnection extends AbstractSenderConnect /** Determine whether the given response is a GZIP response. */ private boolean isGzipResponse() throws IOException { - for (Iterator iterator = getResponseHeaders(HTTP_HEADER_CONTENT_ENCODING); iterator.hasNext();) { + for (Iterator iterator = getResponseHeaders(HttpTransportConstants.HEADER_CONTENT_ENCODING); + iterator.hasNext();) { String encodingHeader = (String) iterator.next(); - return encodingHeader.toLowerCase().indexOf(ENCODING_GZIP) != -1; + return encodingHeader.toLowerCase().indexOf(HttpTransportConstants.CONTENT_ENCODING_GZIP) != -1; } return false; } @@ -92,5 +88,15 @@ public abstract class AbstractHttpSenderConnection extends AbstractSenderConnect protected abstract InputStream getRawResponseInputStream() throws IOException; + /* + * Faults + */ + + public final boolean hasFault() throws IOException { + return getResponseCode() == HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR; + } + + public final void setFault(boolean fault) { + } } diff --git a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpConnection.java b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpConnection.java index fe91ca35..2b532045 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpConnection.java @@ -43,9 +43,9 @@ public class CommonsHttpConnection extends AbstractHttpSenderConnection { private final PostMethod postMethod; - private ByteArrayOutputStream bufferedOutput; + private ByteArrayOutputStream requestBuffer; - public CommonsHttpConnection(HttpClient httpClient, PostMethod postMethod) { + protected CommonsHttpConnection(HttpClient httpClient, PostMethod postMethod) { Assert.notNull(httpClient, "httpClient must not be null"); Assert.notNull(postMethod, "postMethod must not be null"); this.httpClient = httpClient; @@ -69,7 +69,7 @@ public class CommonsHttpConnection extends AbstractHttpSenderConnection { */ protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { - bufferedOutput = new ByteArrayOutputStream(); + requestBuffer = new ByteArrayOutputStream(); } protected void addRequestHeader(String name, String value) throws IOException { @@ -77,12 +77,12 @@ public class CommonsHttpConnection extends AbstractHttpSenderConnection { } protected OutputStream getRequestOutputStream() throws IOException { - return bufferedOutput; + return requestBuffer; } protected void onSendAfterWrite(WebServiceMessage message) throws IOException { - postMethod.setRequestEntity(new ByteArrayRequestEntity(bufferedOutput.toByteArray())); - bufferedOutput = null; + postMethod.setRequestEntity(new ByteArrayRequestEntity(requestBuffer.toByteArray())); + requestBuffer = null; httpClient.executeMethod(postMethod); } diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java b/core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java index 8ace983f..8dcbc039 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java @@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.transport.AbstractReceiverConnection; import org.springframework.ws.transport.EndpointAwareWebServiceConnection; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.support.EnumerationIterator; @@ -34,21 +35,20 @@ import org.springframework.ws.transport.support.EnumerationIterator; * * @author Arjen Poutsma */ -public class HttpServletConnection extends AbstractReceiverConnection implements EndpointAwareWebServiceConnection { +public class HttpServletConnection extends AbstractReceiverConnection + implements EndpointAwareWebServiceConnection, FaultAwareWebServiceConnection { private final HttpServletRequest httpServletRequest; private final HttpServletResponse httpServletResponse; - private boolean sentResponse = false; - - private boolean endpointFound = true; + private boolean statusCodeSet = false; /** * Constructs a new servlet connection with the given HttpServletRequest and * HttpServletResponse. */ - public HttpServletConnection(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + protected HttpServletConnection(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { this.httpServletRequest = httpServletRequest; this.httpServletResponse = httpServletResponse; } @@ -64,8 +64,8 @@ public class HttpServletConnection extends AbstractReceiverConnection implements } public void endpointNotFound() { - endpointFound = false; - getHttpServletResponse().setStatus(HttpServletResponse.SC_NOT_FOUND); + getHttpServletResponse().setStatus(HttpTransportConstants.STATUS_NOT_FOUND); + statusCodeSet = true; } public boolean hasError() throws IOException { @@ -76,12 +76,6 @@ public class HttpServletConnection extends AbstractReceiverConnection implements return null; } - public void close() throws IOException { - if (!sentResponse && endpointFound) { - getHttpServletResponse().setStatus(HttpServletResponse.SC_ACCEPTED); - } - } - /* * Receiving request */ @@ -110,13 +104,31 @@ public class HttpServletConnection extends AbstractReceiverConnection implements return getHttpServletResponse().getOutputStream(); } - protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { - sentResponse = true; - if (!message.hasFault()) { - getHttpServletResponse().setStatus(HttpServletResponse.SC_OK); - } - else { - getHttpServletResponse().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + protected void onSendAfterWrite(WebServiceMessage message) throws IOException { + statusCodeSet = true; + } + + public void close() throws IOException { + if (!statusCodeSet) { + getHttpServletResponse().setStatus(HttpTransportConstants.STATUS_ACCEPTED); } } + + /* + * Faults + */ + + public boolean hasFault() throws IOException { + return false; + } + + public void setFault(boolean fault) throws IOException { + if (fault) { + getHttpServletResponse().setStatus(HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR); + } + else { + getHttpServletResponse().setStatus(HttpTransportConstants.STATUS_OK); + } + statusCodeSet = true; + } } diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java b/core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java new file mode 100644 index 00000000..e0289eea --- /dev/null +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpTransportConstants.java @@ -0,0 +1,45 @@ +/* + * Copyright 2007 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.transport.http; + +import org.springframework.ws.transport.TransportConstants; + +/** + * Declares HTTP-specific transport constants. + * + * @author Arjen Poutsma + */ +public interface HttpTransportConstants extends TransportConstants { + + /** The "Content-Encoding" header. */ + String HEADER_CONTENT_ENCODING = "Content-Encoding"; + + /** Header value that indicates a compressed "Content-Encoding". */ + String CONTENT_ENCODING_GZIP = "gzip"; + + /** The "200 OK" status code. */ + int STATUS_OK = 200; + + /** The "202 Accepted" status code. */ + int STATUS_ACCEPTED = 202; + + /** The "404 Not Found" status code. */ + int STATUS_NOT_FOUND = 404; + + /** The "500 Server Error" status code. */ + int STATUS_INTERNAL_SERVER_ERROR = 500; +} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnection.java b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnection.java index 439d262a..83e2e238 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnection.java @@ -45,7 +45,7 @@ public class HttpUrlConnection extends AbstractHttpSenderConnection { * * @param connection the HttpURLConnection */ - public HttpUrlConnection(HttpURLConnection connection) { + protected HttpUrlConnection(HttpURLConnection connection) { Assert.notNull(connection, "connection must not be null"); this.connection = connection; } diff --git a/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java b/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java index 274ea478..1aaf0eee 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java +++ b/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java @@ -64,7 +64,7 @@ import org.springframework.ws.wsdl.WsdlDefinition; public class MessageDispatcherServlet extends FrameworkServlet { /** Well-known name for the {@link WebServiceMessageFactory} bean in the bean factory for this namespace. */ - public static final String WEB_SERVICE_MESSAGE_FACTORY_BEAN_NAME = "messageFactory"; + public static final String MESSAGE_FACTORY_BEAN_NAME = "messageFactory"; /** Well-known name for the {@link WebServiceMessageReceiver} object in the bean factory for this namespace. */ public static final String MESSAGE_RECEIVER_BEAN_NAME = "messageReceiver"; @@ -181,7 +181,7 @@ public class MessageDispatcherServlet extends FrameworkServlet { WebServiceMessageFactory messageFactory; try { messageFactory = (WebServiceMessageFactory) getWebApplicationContext() - .getBean(WEB_SERVICE_MESSAGE_FACTORY_BEAN_NAME, WebServiceMessageFactory.class); + .getBean(MESSAGE_FACTORY_BEAN_NAME, WebServiceMessageFactory.class); } catch (NoSuchBeanDefinitionException ignored) { messageFactory = (WebServiceMessageFactory) defaultStrategiesHelper diff --git a/core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java b/core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java index 63c3a7a7..2acce295 100644 --- a/core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java +++ b/core/src/main/java/org/springframework/ws/transport/support/WebServiceMessageReceiverObjectSupport.java @@ -22,12 +22,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.NoEndpointFoundException; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.transport.EndpointAwareWebServiceConnection; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageReceiver; import org.springframework.ws.transport.context.DefaultTransportContext; @@ -63,8 +65,9 @@ public abstract class WebServiceMessageReceiverObjectSupport implements Initiali } /** - * Handles an incoming connection by reading a message from the connection input stream, passing it to the receiver, - * and writing the response (if any) to the output stream. + * Handles an incoming connection by {@link WebServiceConnection#receive(WebServiceMessageFactory) receving} a + * message from it, passing it to the {@link WebServiceMessageReceiver#receive(MessageContext) receiver}, and {@link + * WebServiceConnection#send(WebServiceMessage) sending} the response (if any). *

* Stores the given connection in the transport context. * @@ -82,6 +85,13 @@ public abstract class WebServiceMessageReceiverObjectSupport implements Initiali MessageContext messageContext = new DefaultMessageContext(request, getMessageFactory()); receiver.receive(messageContext); if (messageContext.hasResponse()) { + WebServiceMessage response = messageContext.getResponse(); + if (response instanceof FaultAwareWebServiceMessage && + connection instanceof FaultAwareWebServiceConnection) { + FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage) response; + FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection; + faultConnection.setFault(faultResponse.hasFault()); + } connection.send(messageContext.getResponse()); } } diff --git a/core/src/test/java/org/springframework/ws/AbstractWebServiceMessageTestCase.java b/core/src/test/java/org/springframework/ws/AbstractWebServiceMessageTestCase.java index 2c4c8b43..9645a1f3 100644 --- a/core/src/test/java/org/springframework/ws/AbstractWebServiceMessageTestCase.java +++ b/core/src/test/java/org/springframework/ws/AbstractWebServiceMessageTestCase.java @@ -75,10 +75,6 @@ public abstract class AbstractWebServiceMessageTestCase extends XMLTestCase { XMLUnit.setIgnoreWhitespace(true); } - public void testHasFault() throws Exception { - assertFalse("Message has fault", webServiceMessage.hasFault()); - } - public void testDomPayload() throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); diff --git a/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java b/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java index a6d1b3af..b06b577d 100644 --- a/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java +++ b/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java @@ -42,7 +42,7 @@ import org.springframework.xml.transform.StringSource; * * @author Arjen Poutsma */ -public class MockWebServiceMessage implements WebServiceMessage { +public class MockWebServiceMessage implements FaultAwareWebServiceMessage { private final StringBuffer content; diff --git a/core/src/test/java/org/springframework/ws/client/core/SimpleFaultMessageResolverTest.java b/core/src/test/java/org/springframework/ws/client/core/SimpleFaultMessageResolverTest.java index 822a0fd3..70cbf224 100644 --- a/core/src/test/java/org/springframework/ws/client/core/SimpleFaultMessageResolverTest.java +++ b/core/src/test/java/org/springframework/ws/client/core/SimpleFaultMessageResolverTest.java @@ -18,7 +18,7 @@ package org.springframework.ws.client.core; import junit.framework.TestCase; import org.easymock.MockControl; -import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.client.WebServiceFaultException; public class SimpleFaultMessageResolverTest extends TestCase { @@ -30,8 +30,8 @@ public class SimpleFaultMessageResolverTest extends TestCase { } public void testResolveFault() throws Exception { - MockControl messageControl = MockControl.createControl(WebServiceMessage.class); - WebServiceMessage messageMock = (WebServiceMessage) messageControl.getMock(); + MockControl messageControl = MockControl.createControl(FaultAwareWebServiceMessage.class); + FaultAwareWebServiceMessage messageMock = (FaultAwareWebServiceMessage) messageControl.getMock(); String message = "message"; messageControl.expectAndReturn(messageMock.getFaultReason(), message); messageControl.replay(); diff --git a/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java b/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java index b6ad6519..f65f7d73 100644 --- a/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java +++ b/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java @@ -50,7 +50,6 @@ import org.springframework.ws.pox.dom.DomPoxMessageFactory; 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.client.core.SoapFaultMessageResolver; import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; import org.springframework.ws.transport.http.CommonsHttpMessageSender; import org.springframework.xml.transform.StringResult; @@ -74,9 +73,6 @@ public class WebServiceTemplateIntegrationTest extends XMLTestCase { jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound"); jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server"); jettyServer.start(); - template = new WebServiceTemplate(); - template.setMessageSender(new CommonsHttpMessageSender()); - template.setFaultMessageResolver(new SoapFaultMessageResolver()); } protected void tearDown() throws Exception { @@ -88,8 +84,8 @@ public class WebServiceTemplateIntegrationTest extends XMLTestCase { } public void testPox() throws Exception { - template.setMessageFactory(new DomPoxMessageFactory()); - template.setFaultMessageResolver(null); + template = new WebServiceTemplate(new DomPoxMessageFactory()); + template.setMessageSender(new CommonsHttpMessageSender()); String content = ""; StringResult result = new StringResult(); template.sendAndReceive("http://localhost:8888/pox", new StringSource(content), result); @@ -117,7 +113,8 @@ public class WebServiceTemplateIntegrationTest extends XMLTestCase { private void testSoap(SoapMessageFactory messageFactory) throws SAXException, IOException, ParserConfigurationException { - template.setMessageFactory(messageFactory); + template = new WebServiceTemplate(messageFactory); + template.setMessageSender(new CommonsHttpMessageSender()); String content = ""; StringResult result = new StringResult(); template.sendAndReceive("http://localhost:8888/soap/echo", new StringSource(content), result); diff --git a/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java b/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java index 8a937420..cc7e9fc7 100644 --- a/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java +++ b/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateTest.java @@ -117,7 +117,6 @@ public class WebServiceTemplateTest extends XMLTestCase { callbackControl.verify(); extractorControl.verify(); connectionControl.verify(); - } public void testSendAndReceiveMessageNoResponse() throws Exception { @@ -150,11 +149,15 @@ public class WebServiceTemplateTest extends XMLTestCase { faultResolverControl.setMatcher(MockControl.ALWAYS_MATCHER); faultResolverControl.replay(); + MockWebServiceMessage response = new MockWebServiceMessage(""); + response.setFault(true); + connectionMock.send(null); connectionControl.setMatcher(MockControl.ALWAYS_MATCHER); - connectionControl.expectAndReturn(connectionMock.hasError(), false); + connectionControl.expectAndReturn(connectionMock.hasError(), true); + connectionControl.expectAndReturn(connectionMock.hasFault(), true); connectionControl - .expectAndReturn(connectionMock.receive(messageFactory), new MockWebServiceMessage("")); + .expectAndReturn(connectionMock.receive(messageFactory), response); connectionControl.expectAndReturn(connectionMock.hasFault(), true); connectionMock.close(); connectionControl.replay(); @@ -177,6 +180,7 @@ public class WebServiceTemplateTest extends XMLTestCase { connectionMock.send(null); connectionControl.setMatcher(MockControl.ALWAYS_MATCHER); connectionControl.expectAndReturn(connectionMock.hasError(), true); + connectionControl.expectAndReturn(connectionMock.hasFault(), false); String errorMessage = "errorMessage"; connectionControl.expectAndReturn(connectionMock.getErrorMessage(), errorMessage, 2); connectionMock.close(); diff --git a/core/src/test/java/org/springframework/ws/soap/saaj/AbstractSaajImplementationTestCase.java b/core/src/test/java/org/springframework/ws/soap/saaj/AbstractSaajImplementationTestCase.java index 6f9dd591..330e928b 100644 --- a/core/src/test/java/org/springframework/ws/soap/saaj/AbstractSaajImplementationTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/saaj/AbstractSaajImplementationTestCase.java @@ -20,7 +20,6 @@ import java.io.ByteArrayOutputStream; import java.util.Iterator; import java.util.Locale; import javax.activation.DataHandler; -import javax.mail.util.ByteArrayDataSource; import javax.xml.namespace.QName; import javax.xml.soap.AttachmentPart; import javax.xml.soap.Detail; @@ -273,7 +272,7 @@ public abstract class AbstractSaajImplementationTestCase extends XMLTestCase { } public void testAddAttachmentPart() throws Exception { - DataHandler dataHandler = new DataHandler(new ByteArrayDataSource("data", "text")); + DataHandler dataHandler = new DataHandler("data", "text/plain"); AttachmentPart attachmentPart = implementation.addAttachmentPart(message, dataHandler); assertNotNull("No attachment part", attachmentPart); } diff --git a/core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java b/core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java index ed3a0356..185c2c38 100644 --- a/core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java +++ b/core/src/test/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapterTest.java @@ -22,8 +22,8 @@ import junit.framework.TestCase; import org.easymock.MockControl; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.ws.FaultAwareWebServiceMessage; import org.springframework.ws.NoEndpointFoundException; -import org.springframework.ws.WebServiceMessage; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.context.MessageContext; import org.springframework.ws.transport.WebServiceMessageReceiver; @@ -48,9 +48,9 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { private MockControl messageControl; - private WebServiceMessage responseMock; + private FaultAwareWebServiceMessage responseMock; - private WebServiceMessage requestMock; + private FaultAwareWebServiceMessage requestMock; protected void setUp() throws Exception { adapter = new WebServiceMessageReceiverHandlerAdapter(); @@ -59,9 +59,9 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { factoryControl = MockControl.createControl(WebServiceMessageFactory.class); factoryMock = (WebServiceMessageFactory) factoryControl.getMock(); adapter.setMessageFactory(factoryMock); - messageControl = MockControl.createControl(WebServiceMessage.class); - requestMock = (WebServiceMessage) messageControl.getMock(); - responseMock = (WebServiceMessage) messageControl.getMock(); + messageControl = MockControl.createControl(FaultAwareWebServiceMessage.class); + requestMock = (FaultAwareWebServiceMessage) messageControl.getMock(); + responseMock = (FaultAwareWebServiceMessage) messageControl.getMock(); } public void testHandleNonPost() throws Exception { diff --git a/src/changes/changes.xml b/src/changes/changes.xml index be0453ee..8785c3c4 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,6 +6,9 @@ + Added checkConnectionForFault to WebServiceTemplate to + deal with Web services which are not WS-I compliant + WebServiceTemplate returns boolean values rather than void Changed WebServiceMessageCallback's doInMessage() to doWithMessage()