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 36fcee0e..b4613b58 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 @@ -31,6 +31,9 @@ import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.WebServiceClientException; import org.springframework.ws.client.support.WebServiceAccessor; import org.springframework.ws.context.MessageContext; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.TransportInputStream; +import org.springframework.ws.transport.TransportOutputStream; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; @@ -76,44 +79,32 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService setMessageSender(messageSender); } - /** - * Returns the marshaller for this template. - */ + /** Returns the marshaller for this template. */ public Marshaller getMarshaller() { return marshaller; } - /** - * Sets the marshaller for this template. - */ + /** Sets the marshaller for this template. */ public void setMarshaller(Marshaller marshaller) { this.marshaller = marshaller; } - /** - * Returns the unmarshaller for this template. - */ + /** Returns the unmarshaller for this template. */ public Unmarshaller getUnmarshaller() { return unmarshaller; } - /** - * Sets the unmarshaller for this template. - */ + /** Sets the unmarshaller for this template. */ public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } - /** - * Returns the fault resolver for this template. - */ + /** Returns the fault resolver for this template. */ public FaultResolver getFaultResolver() { return faultResolver; } - /** - * Sets the fault resolver for this template. - */ + /** Sets the fault resolver for this template. */ public void setFaultResolver(FaultResolver faultResolver) { Assert.notNull(faultResolver, "faultResolver must not be null"); this.faultResolver = faultResolver; @@ -185,36 +176,6 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService /* * Source-handling methods */ -/* - public Source sendAndReceive(final Source requestPayload) throws IOException { - return sendAndReceive(requestPayload, (WebServiceMessageCallback) null); - } -*/ - - /* - public Source sendAndReceive(final Source requestPayload, final WebServiceMessageCallback requestCallback) - throws IOException { - return (Source) sendAndReceive(new WebServiceMessageCallback() { - public void doInMessage(WebServiceMessage message) throws IOException { - try { - Transformer transformer = createTransformer(); - transformer.transform(requestPayload, message.getPayloadResult()); - if (requestCallback != null) { - requestCallback.doInMessage(message); - } - } - catch (TransformerException ex) { - throw new WebServiceClientException("Could not transform payload to request message", ex); - } - } - }, new WebServiceMessageExtractor() { - - public Object extractData(WebServiceMessage message) throws IOException { - return message.getPayloadSource(); - } - }); - } - */ public Object sendAndReceive(final Source requestPayload, final SourceExtractor responseExtractor) throws IOException { @@ -254,8 +215,8 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService } /* - * WebServiceMessage-handling methods - */ + * WebServiceMessage-handling methods + */ public void sendAndReceive(WebServiceMessageCallback requestCallback, WebServiceMessageCallback responseCallback) throws IOException { @@ -266,33 +227,64 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService public Object sendAndReceive(WebServiceMessageCallback requestCallback, WebServiceMessageExtractor responseExtractor) throws IOException { Assert.notNull(responseExtractor, "response extractor must not be null"); - WebServiceConnection connection = getMessageSender().createConnection(); MessageContext messageContext = createMessageContext(); + WebServiceConnection connection = getMessageSender().createConnection(); try { + WebServiceMessage request = messageContext.getRequest(); if (requestCallback != null) { - requestCallback.doInMessage(messageContext.getRequest()); + requestCallback.doInMessage(request); } - connection.sendAndReceive(messageContext); - if (!messageContext.hasResponse()) { - return null; - } - WebServiceMessage response = messageContext.getResponse(); - if (response.hasFault()) { - getFaultResolver().resolveFault(response); - return null; - } - else { - return responseExtractor.extractData(response); + sendRequest(connection, messageContext); + TransportInputStream tis = connection.getTransportInputStream(); + if (tis != null) { + try { + messageContext.readResponse(tis); + if (messageContext.hasResponse()) { + WebServiceMessage response = messageContext.getResponse(); + if (!hasFault(connection, messageContext)) { + // normal response + return responseExtractor.extractData(response); + } + else { + // fault response + getFaultResolver().resolveFault(response); + } + } + } + finally { + tis.close(); + } } + return null; } finally { connection.close(); } } - /** - * Adapter to enable use of a WebServiceMessageCallback inside a WebServiceMessageExtractor. - */ + /** Sends the request in the given message context over the connection. */ + private void sendRequest(WebServiceConnection connection, MessageContext messageContext) throws IOException { + TransportOutputStream tos = connection.getTransportOutputStream(); + try { + messageContext.getRequest().writeTo(tos); + tos.flush(); + } + finally { + tos.close(); + } + } + + /** Determines whether the given connection or message context has a fault. */ + private boolean hasFault(WebServiceConnection connection, MessageContext messageContext) throws IOException { + if (connection instanceof FaultAwareWebServiceConnection) { + return ((FaultAwareWebServiceConnection) connection).hasFault(); + } + else { + return messageContext.getResponse().hasFault(); + } + } + + /** Adapter to enable use of a WebServiceMessageCallback inside a WebServiceMessageExtractor. */ private static class WebServiceMessageCallbackMessageExtractor implements WebServiceMessageExtractor { private final WebServiceMessageCallback callback; @@ -307,9 +299,7 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService } } - /** - * Adapter to enable use of a SourceExtractor inside a WebServiceMessageExtractor. - */ + /** Adapter to enable use of a SourceExtractor inside a WebServiceMessageExtractor. */ private static class SourceExtractorMessageExtractor implements WebServiceMessageExtractor { private final SourceExtractor sourceExtractor; diff --git a/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java b/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java new file mode 100644 index 00000000..620f2d7c --- /dev/null +++ b/core/src/main/java/org/springframework/ws/transport/FaultAwareWebServiceConnection.java @@ -0,0 +1,36 @@ +/* + * 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; + +import java.io.IOException; + +/** + * Sub-interface of {@link WebServiceConnection} that is aware of any Faults received. 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. + * + * @return true if this connection has a fault; false otherwise. + */ + boolean hasFault() throws IOException; + +} diff --git a/core/src/main/java/org/springframework/ws/transport/WebServiceConnection.java b/core/src/main/java/org/springframework/ws/transport/WebServiceConnection.java index acc42a0a..12b8baca 100644 --- a/core/src/main/java/org/springframework/ws/transport/WebServiceConnection.java +++ b/core/src/main/java/org/springframework/ws/transport/WebServiceConnection.java @@ -19,33 +19,59 @@ package org.springframework.ws.transport; import java.io.IOException; import org.springframework.ws.WebServiceMessage; -import org.springframework.ws.context.MessageContext; +import org.springframework.ws.transport.http.HttpServletConnection; /** * Represents a point-to-point connection that a client can use for sending {@link WebServiceMessage} objects directly * to a remote party. *

* A WebServiceConnection can be obtained using a {@link WebServiceMessageSender}. + *

+ * On the receiving side, the typical usage scenario for this connection is:

  1. Create concrete connection + * implementation (eg. {@link HttpServletConnection})
  2. Read request from {@link #getTransportInputStream() the input + * stream}.
  3. {@link TransportInputStream#close() Close} the input stream
  4. Write response to {@link + * #getTransportOutputStream() the output stream}.
  5. {@link TransportOutputStream#flush() Flush} the output stream + *
  6. {@link TransportOutputStream#close() Close} the output stream
  7. {@link #close() Close the connection}
+ *

+ * On the sending side, the typical usage scenario for this connection is:

  1. Create connection with {@link + * WebServiceMessageSender#createConnection()}
  2. Write request to {@link #getTransportOutputStream() the output + * stream}.
  3. {@link TransportOutputStream#flush() Flush} the output stream
  4. {@link TransportOutputStream#close() + * Close} the output stream
  5. Read request from {@link #getTransportInputStream() the input stream}.
  6. {@link + * TransportInputStream#close() Close} the input stream
  7. {@link #close() Close the connection}
* * @author Arjen Poutsma * @see WebServiceMessageSender#createConnection() + * @see #getTransportInputStream() + * @see #getTransportOutputStream() */ public interface WebServiceConnection { /** - * Sends the given message and blocks until it has returned the response. The response message, if any, is stored in - * the context. + * Returns a transport input stream for this connection. + *

+ * Returns null if no transport input stream is available (eg. a request might not have a response). * - * @param messageContext the context which contains the request message to be sent, and which will contain the - * response afterwards - * @throws IOException in case of I/O errors + * @return a transport input stream for this connection, or null + * @throws IOException if an I/O error occurs when creating the input stream, the connection is closed */ - void sendAndReceive(MessageContext messageContext) throws IOException; + TransportInputStream getTransportInputStream() throws IOException; /** - * Closes the WebServiceConnection. + * Returns a transport output stream for this connection. + *

+ * Returns null if no transport output stream is available (eg. a response might not have a response). * - * @throws IOException in case of I/O errors + * @return a transport output stream for this connection + * @throws IOException if an I/O error occurs when creating the output stream, the connection is closed + */ + TransportOutputStream getTransportOutputStream() throws IOException; + + /** + * Closes this connection. + *

+ * Once a connection has been closed, it is not available for further use. A new connection needs to be created. + * + * @throws IOException if an I/O error occurs when closing this socket */ void close() throws IOException; diff --git a/core/src/main/java/org/springframework/ws/transport/context/DefaultTransportContext.java b/core/src/main/java/org/springframework/ws/transport/context/DefaultTransportContext.java index e84387ca..48d70ca4 100644 --- a/core/src/main/java/org/springframework/ws/transport/context/DefaultTransportContext.java +++ b/core/src/main/java/org/springframework/ws/transport/context/DefaultTransportContext.java @@ -16,8 +16,7 @@ package org.springframework.ws.transport.context; -import org.springframework.ws.transport.TransportInputStream; -import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; /** * Default implementation of the TransportContext interface. @@ -26,24 +25,15 @@ import org.springframework.ws.transport.TransportOutputStream; */ public class DefaultTransportContext implements TransportContext { - private final TransportInputStream transportInputStream; + private final WebServiceConnection connection; - private final TransportOutputStream transportOutputStream; - - /** - * Creates a new DefaultTransportContext that exposes the given streams. - */ - public DefaultTransportContext(TransportInputStream transportInputStream, - TransportOutputStream transportOutputStream) { - this.transportInputStream = transportInputStream; - this.transportOutputStream = transportOutputStream; + /** Creates a new DefaultTransportContext that exposes the given connection. */ + public DefaultTransportContext(WebServiceConnection connection) { + this.connection = connection; } - public TransportInputStream getTransportInputStream() { - return transportInputStream; + public WebServiceConnection getConnection() { + return connection; } - public TransportOutputStream getTransportOutputStream() { - return transportOutputStream; - } } diff --git a/core/src/main/java/org/springframework/ws/transport/context/TransportContext.java b/core/src/main/java/org/springframework/ws/transport/context/TransportContext.java index a7a06997..3094455f 100644 --- a/core/src/main/java/org/springframework/ws/transport/context/TransportContext.java +++ b/core/src/main/java/org/springframework/ws/transport/context/TransportContext.java @@ -1,11 +1,9 @@ package org.springframework.ws.transport.context; -import org.springframework.ws.transport.TransportInputStream; -import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; /** - * Strategy interface for determining the current {@link org.springframework.ws.transport.TransportInputStream} and - * {@link org.springframework.ws.transport.TransportOutputStream}. + * Strategy interface for determining the current {@link org.springframework.ws.transport.WebServiceConnection}. *

* An instance of this class can be associated with a thread via the {@link TransportContextHolder} class. * @@ -13,13 +11,6 @@ import org.springframework.ws.transport.TransportOutputStream; */ public interface TransportContext { - /** - * Returns the current TransportInputStream. - */ - TransportInputStream getTransportInputStream(); - - /** - * Returns the current TransportOutputStream. - */ - TransportOutputStream getTransportOutputStream(); + /** Returns the current WebServiceConnection. */ + WebServiceConnection getConnection(); } 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 new file mode 100644 index 00000000..2e7d7e31 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpConnection.java @@ -0,0 +1,165 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Iterator; + +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.TransportInputStream; +import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; + +/** + * Implementation of {@link WebServiceConnection} that is based on Jakarta Commons HttpClient. Exposes a {@link + * PostMethod}. + * + * @author Arjen Poutsma + */ +public class CommonsHttpConnection implements FaultAwareWebServiceConnection { + + private final HttpClient httpClient; + + private final PostMethod postMethod; + + private byte[] bufferedInput; + + public CommonsHttpConnection(HttpClient httpClient, PostMethod postMethod) { + Assert.notNull(httpClient, "httpClient must not be null"); + Assert.notNull(postMethod, "postMethod must not be null"); + this.httpClient = httpClient; + this.postMethod = postMethod; + } + + /** Returns the wrapped PostMethod. */ + public HttpMethod getPostMethod() { + return postMethod; + } + + public void close() throws IOException { + postMethod.releaseConnection(); + } + + public TransportOutputStream getTransportOutputStream() { + return new CommonsHttpTransportOutputStream(); + } + + public TransportInputStream getTransportInputStream() throws IOException { + return getContentLength() > 0 ? new CommonsHttpTransportInputStream() : null; + } + + public boolean hasFault() throws IOException { + return postMethod.getStatusCode() == HttpStatus.SC_INTERNAL_SERVER_ERROR; + } + + private long getContentLength() throws IOException { + if (postMethod.getResponseContentLength() != -1) { + return postMethod.getResponseContentLength(); + } + else if (bufferedInput != null) { + return bufferedInput.length; + } + else { + bufferedInput = FileCopyUtils.copyToByteArray(getInputStream()); + return bufferedInput.length; + } + } + + private InputStream getInputStream() throws IOException { + if (postMethod.getStatusCode() != HttpStatus.SC_INTERNAL_SERVER_ERROR && + postMethod.getStatusCode() / 100 != 2) { + throw new HttpTransportException("Did not receive successful HTTP response: status code = " + + postMethod.getStatusCode() + ", status message = [" + postMethod.getStatusText() + "]"); + } + return postMethod.getResponseBodyAsStream(); + } + + /** + * Implementation of {@link TransportInputStream} based on the {@link PostMethod} field. + * + * @see CommonsHttpConnection#postMethod + */ + class CommonsHttpTransportInputStream extends TransportInputStream { + + protected InputStream createInputStream() throws IOException { + if (bufferedInput != null) { + return new ByteArrayInputStream(bufferedInput); + } + else { + return getInputStream(); + } + } + + public Iterator getHeaderNames() throws IOException { + Header[] headers = postMethod.getResponseHeaders(); + String[] names = new String[headers.length]; + for (int i = 0; i < headers.length; i++) { + names[i] = headers[i].getName(); + } + return Arrays.asList(names).iterator(); + } + + public Iterator getHeaders(String name) throws IOException { + Header[] headers = postMethod.getResponseHeaders(name); + String[] values = new String[headers.length]; + for (int i = 0; i < headers.length; i++) { + values[i] = headers[i].getValue(); + } + return Arrays.asList(values).iterator(); + } + } + + /** + * Implementation of {@link TransportOutputStream} based on the {@link PostMethod} field. + * + * @see CommonsHttpConnection#postMethod + */ + class CommonsHttpTransportOutputStream extends TransportOutputStream { + + private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + public void addHeader(String name, String value) throws IOException { + postMethod.addRequestHeader(name, value); + } + + protected OutputStream createOutputStream() throws IOException { + return bos; + } + + public void close() throws IOException { + super.close(); + postMethod.setRequestEntity(new ByteArrayRequestEntity(bos.toByteArray())); + httpClient.executeMethod(postMethod); + } + + } + + +} diff --git a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpMessageSender.java b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpMessageSender.java index a54e8e68..1869f259 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpMessageSender.java +++ b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpMessageSender.java @@ -19,8 +19,10 @@ package org.springframework.ws.transport.http; import java.io.IOException; import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.methods.PostMethod; +import org.springframework.beans.factory.DisposableBean; import org.springframework.util.Assert; import org.springframework.ws.transport.WebServiceConnection; @@ -34,7 +36,7 @@ import org.springframework.ws.transport.WebServiceConnection; * @author Arjen Poutsma * @see org.springframework.ws.transport.http.HttpUrlConnectionMessageSender */ -public class CommonsHttpMessageSender extends AbstractHttpWebServiceMessageSender { +public class CommonsHttpMessageSender extends AbstractHttpWebServiceMessageSender implements DisposableBean { private HttpClient httpClient; @@ -60,23 +62,26 @@ public class CommonsHttpMessageSender extends AbstractHttpWebServiceMessageSende this.httpClient = httpClient; } - /** - * Returns the HttpClient used by this message sender. - */ + public void destroy() throws Exception { + HttpConnectionManager connectionManager = httpClient.getHttpConnectionManager(); + if (connectionManager instanceof MultiThreadedHttpConnectionManager) { + ((MultiThreadedHttpConnectionManager) connectionManager).shutdown(); + } + } + + /** Returns the HttpClient used by this message sender. */ public HttpClient getHttpClient() { return httpClient; } - /** - * Set the HttpClient used by this message sender. - */ + /** Set the HttpClient used by this message sender. */ public void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } public WebServiceConnection createConnection() throws IOException { PostMethod method = new PostMethod(getUrl().toString()); - return new CommonsHttpWebServiceConnection(getHttpClient(), method); + return new CommonsHttpConnection(getHttpClient(), method); } } diff --git a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportInputStream.java b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportInputStream.java deleted file mode 100644 index 42df4601..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportInputStream.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2006 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 java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Iterator; - -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.springframework.util.Assert; -import org.springframework.ws.transport.TransportInputStream; - -/** - * Implementation of the TransportInputStream interface based on {@link - * org.apache.commons.httpclient.methods.PostMethod}. Exposes the PostMethod. - * - * @author Arjen Poutsma - */ -class CommonsHttpTransportInputStream extends TransportInputStream { - - private final PostMethod postMethod; - - /** - * Constructs a new instance of the CommonsHttpTransportInputStream with a given - * PostMethod. - */ - public CommonsHttpTransportInputStream(PostMethod postMethod) { - Assert.notNull(postMethod, "postMethod must not be null"); - this.postMethod = postMethod; - } - - protected InputStream createInputStream() throws IOException { - return postMethod.getResponseBodyAsStream(); - } - - public Iterator getHeaderNames() throws IOException { - Header[] headers = postMethod.getResponseHeaders(); - String[] names = new String[headers.length]; - for (int i = 0; i < headers.length; i++) { - names[i] = headers[i].getName(); - } - return Arrays.asList(names).iterator(); - } - - public Iterator getHeaders(String name) throws IOException { - Header[] headers = postMethod.getResponseHeaders(name); - String[] names = new String[headers.length]; - for (int i = 0; i < headers.length; i++) { - names[i] = headers[i].getValue(); - } - return Arrays.asList(names).iterator(); - } - - /** - * Returns the wrapped PostMethod. - */ - public HttpMethod getPostMethod() { - return postMethod; - } -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportOutputStream.java b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportOutputStream.java deleted file mode 100644 index 71834bc1..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpTransportOutputStream.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2006 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 java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.PostMethod; -import org.springframework.ws.transport.TransportOutputStream; - -/** - * Implementation of the TransportOutputStream interface based on {@link - * org.apache.commons.httpclient.methods.PostMethod}. Exposes the PostMethod. - * - * @author Arjen Poutsma - */ -class CommonsHttpTransportOutputStream extends TransportOutputStream { - - private final PostMethod postMethod; - - private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - /** - * Constructs a new instance of the CommonsHttpTransportOutputStream with a given - * PostMethod. - */ - public CommonsHttpTransportOutputStream(PostMethod postMethod) { - this.postMethod = postMethod; - } - - public void addHeader(String name, String value) throws IOException { - postMethod.addRequestHeader(name, value); - } - - public void close() throws IOException { - postMethod.setRequestEntity(new ByteArrayRequestEntity(bos.toByteArray())); - } - - protected OutputStream createOutputStream() throws IOException { - return bos; - } - - /** - * Returns the wrapped PostMethod. - */ - public HttpMethod getPostMethod() { - return postMethod; - } - -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpWebServiceConnection.java b/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpWebServiceConnection.java deleted file mode 100644 index 3f105b36..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/CommonsHttpWebServiceConnection.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 java.io.IOException; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.PostMethod; -import org.springframework.util.Assert; -import org.springframework.ws.WebServiceMessage; -import org.springframework.ws.context.MessageContext; -import org.springframework.ws.transport.WebServiceConnection; - -/** - * Implementation of the {@link WebServiceConnection} that uses Jakarta Commons HttpClient. - * - * @author Arjen Poutsma - */ -public class CommonsHttpWebServiceConnection implements WebServiceConnection { - - private final HttpClient httpClient; - - private final PostMethod postMethod; - - public CommonsHttpWebServiceConnection(HttpClient httpClient, PostMethod postMethod) { - Assert.notNull(httpClient, "httpClient must not be null"); - Assert.notNull(postMethod, "postMethod must not be null"); - this.httpClient = httpClient; - this.postMethod = postMethod; - } - - public void close() throws IOException { - postMethod.releaseConnection(); - } - - public void sendAndReceive(MessageContext messageContext) throws IOException { - writeRequestMessage(messageContext.getRequest()); - executePostMethod(); - validateResponse(postMethod); - readResponse(postMethod, messageContext); - } - - private void writeRequestMessage(WebServiceMessage message) throws IOException { - CommonsHttpTransportOutputStream tos = new CommonsHttpTransportOutputStream(postMethod); - message.writeTo(tos); - tos.flush(); - // calling close() causes the RequestEntity to be set on the PostMethod - tos.close(); - } - - private void executePostMethod() throws IOException { - httpClient.executeMethod(postMethod); - } - - private void validateResponse(PostMethod postMethod) throws IOException { - int statusCode = postMethod.getStatusCode(); - if (statusCode != HttpStatus.SC_INTERNAL_SERVER_ERROR && statusCode / 100 != 2) { - throw new HttpTransportException("Did not receive successful HTTP response: status code = " + statusCode + - ", status message = [" + postMethod.getStatusText() + "]"); - } - - } - - private void readResponse(PostMethod postMethod, MessageContext messageContext) throws IOException { - if (postMethod.getStatusCode() == HttpStatus.SC_NO_CONTENT || postMethod.getResponseContentLength() == 0) { - return; - } - messageContext.readResponse(new CommonsHttpTransportInputStream(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 new file mode 100644 index 00000000..59df649f --- /dev/null +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpServletConnection.java @@ -0,0 +1,114 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.ws.transport.TransportInputStream; +import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; +import org.springframework.ws.transport.support.EnumerationIterator; + +/** + * Implementation of {@link WebServiceConnection} that is based on the Servlet API. Exposes a {@link HttpServletRequest} + * and {@link HttpServletResponse}. + * + * @author Arjen Poutsma + * @author Arjen Poutsma + * @see #getHttpServletRequest() + * @see #getHttpServletResponse() + */ +public class HttpServletConnection implements WebServiceConnection { + + private final HttpServletRequest httpServletRequest; + + private final HttpServletResponse httpServletResponse; + + /** + * Constructs a new servlet connection with the given HttpServletRequest and + * HttpServletResponse. + */ + public HttpServletConnection(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + this.httpServletRequest = httpServletRequest; + this.httpServletResponse = httpServletResponse; + } + + /** Returns the HttpServletRequest for this connection. */ + public HttpServletRequest getHttpServletRequest() { + return httpServletRequest; + } + + /** Returns the HttpServletResponse for this connection. */ + public HttpServletResponse getHttpServletResponse() { + return httpServletResponse; + } + + public TransportInputStream getTransportInputStream() { + return new HttpServletTransportInputStream(); + } + + public TransportOutputStream getTransportOutputStream() { + return new HttpServletTransportOutputStream(); + } + + public void close() throws IOException { + // no op + } + + /** + * Implementation of {@link TransportInputStream} based on the {@link HttpServletRequest} field. + * + * @see HttpServletConnection#httpServletRequest + */ + private class HttpServletTransportInputStream extends TransportInputStream { + + protected InputStream createInputStream() throws IOException { + return httpServletRequest.getInputStream(); + } + + public Iterator getHeaderNames() { + return new EnumerationIterator(httpServletRequest.getHeaderNames()); + } + + public Iterator getHeaders(String name) { + return new EnumerationIterator(httpServletRequest.getHeaders(name)); + } + } + + /** + * Implementation of {@link TransportOutputStream} based on the {@link HttpServletResponse} field. + * + * @see HttpServletConnection#httpServletResponse + */ + private class HttpServletTransportOutputStream extends TransportOutputStream { + + protected OutputStream createOutputStream() throws IOException { + return httpServletResponse.getOutputStream(); + } + + public void addHeader(String name, String value) { + httpServletResponse.addHeader(name, value); + } + } + + +} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportInputStream.java b/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportInputStream.java deleted file mode 100644 index c17d9886..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportInputStream.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2006 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 java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import javax.servlet.http.HttpServletRequest; - -import org.springframework.ws.transport.TransportInputStream; -import org.springframework.ws.transport.support.EnumerationIterator; - -/** - * HTTP Servlet specific implementation of the TransportInputStream interface. Exposes the - * HttpServletRequest. - * - * @author Arjen Poutsma - * @see #getHttpServletRequest() - */ -class HttpServletTransportInputStream extends TransportInputStream { - - private final HttpServletRequest httpServletRequest; - - /** - * Constructs a new instance of the HttpTransportRequest with the given - * HttpServletRequest. - */ - public HttpServletTransportInputStream(HttpServletRequest httpServletRequest) throws IOException { - this.httpServletRequest = httpServletRequest; - } - - protected InputStream createInputStream() throws IOException { - return httpServletRequest.getInputStream(); - } - - /** - * Returns the wrapped HttpServletRequest. - */ - public HttpServletRequest getHttpServletRequest() { - return httpServletRequest; - } - - public Iterator getHeaderNames() { - return new EnumerationIterator(httpServletRequest.getHeaderNames()); - } - - public Iterator getHeaders(String name) { - return new EnumerationIterator(httpServletRequest.getHeaders(name)); - } -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportOutputStream.java b/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportOutputStream.java deleted file mode 100644 index 7f4996e8..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpServletTransportOutputStream.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2006 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 java.io.IOException; -import java.io.OutputStream; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.ws.transport.TransportOutputStream; - -/** - * HTTP Servlet specific implementation of the TransportOutputStream interface. Exposes the - * HttpServletResponse. - * - * @author Arjen Poutsma - * @see #getHttpServletResponse() - */ -class HttpServletTransportOutputStream extends TransportOutputStream { - - private final HttpServletResponse httpServletResponse; - - /** - * Constructs a new instance of the HttpTransportResponse with the given - * HttpServletResponse. - */ - public HttpServletTransportOutputStream(HttpServletResponse httpServletResponse) throws IOException { - this.httpServletResponse = httpServletResponse; - } - - protected OutputStream createOutputStream() throws IOException { - return httpServletResponse.getOutputStream(); - } - - /** - * Returns the wrapped HttpServletResponse. - */ - public HttpServletResponse getHttpServletResponse() { - return httpServletResponse; - } - - public void addHeader(String name, String value) { - httpServletResponse.addHeader(name, value); - } -} 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 new file mode 100644 index 00000000..4213d168 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnection.java @@ -0,0 +1,169 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.TransportInputStream; +import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; + +/** + * Implementation of the {@link WebServiceConnection} interface that uses a {@link HttpURLConnection}. + * + * @author Arjen Poutsma + */ +public class HttpUrlConnection implements FaultAwareWebServiceConnection { + + private final HttpURLConnection connection; + + private byte[] bufferedInput; + + /** Creates a new instance of the HttpUrlConnection with the given HttpURLConnection. */ + public HttpUrlConnection(HttpURLConnection connection) throws ProtocolException { + Assert.notNull(connection, "connection must not be null"); + this.connection = connection; + } + + /** Returns the wrapped HttpURLConnection. */ + public HttpURLConnection getConnection() { + return connection; + } + + public void close() { + connection.disconnect(); + } + + public TransportOutputStream getTransportOutputStream() { + return new HttpUrlConnectionTransportOutputStream(); + } + + public TransportInputStream getTransportInputStream() throws IOException { + return getContentLength() > 0 ? new HttpUrlConnectionTransportInputStream() : null; + } + + public boolean hasFault() throws IOException { + return connection.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR; + } + + private int getContentLength() throws IOException { + if (connection.getContentLength() != -1) { + return connection.getContentLength(); + } + else if (bufferedInput != null) { + return bufferedInput.length; + } + else { + bufferedInput = FileCopyUtils.copyToByteArray(getInputStream()); + return bufferedInput.length; + } + } + + private InputStream getInputStream() throws IOException { + if (connection.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) { + return connection.getErrorStream(); + } + else if (connection.getResponseCode() / 100 == 2) { + return connection.getInputStream(); + } + else { + throw new HttpTransportException("Did not receive successful HTTP response: status code = " + + connection.getResponseCode() + ", status message = [" + connection.getResponseMessage() + "]"); + } + } + + /** + * Implementation of {@link TransportInputStream} based on the {@link HttpURLConnection} field. + * + * @see HttpUrlConnection#connection + */ + + class HttpUrlConnectionTransportInputStream extends TransportInputStream { + + protected InputStream createInputStream() throws IOException { + if (bufferedInput != null) { + return new ByteArrayInputStream(bufferedInput); + } + else { + return getInputStream(); + } + } + + public Iterator getHeaderNames() throws IOException { + List headerNames = new ArrayList(); + // Header field 0 is the status line, so we start at 1 + int i = 1; + while (true) { + String headerName = connection.getHeaderFieldKey(i); + if (!StringUtils.hasLength(headerName)) { + break; + } + headerNames.add(headerName); + i++; + } + return headerNames.iterator(); + } + + public Iterator getHeaders(String name) throws IOException { + String headerField = connection.getHeaderField(name); + if (headerField == null) { + return Collections.EMPTY_LIST.iterator(); + } + else { + Set tokens = StringUtils.commaDelimitedListToSet(headerField); + return tokens.iterator(); + } + } + + } + + /** + * Implementation of {@link TransportOutputStream} based on the {@link HttpURLConnection} field. + * + * @see HttpUrlConnection#connection + */ + class HttpUrlConnectionTransportOutputStream extends TransportOutputStream { + + public void addHeader(String name, String value) throws IOException { + connection.addRequestProperty(name, value); + } + + protected OutputStream createOutputStream() throws IOException { + return connection.getOutputStream(); + } + + public void close() throws IOException { + super.close(); + connection.connect(); + } + } + +} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionMessageSender.java b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionMessageSender.java index ef0da86b..e51f4e60 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionMessageSender.java +++ b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionMessageSender.java @@ -35,12 +35,21 @@ import org.springframework.ws.transport.WebServiceConnection; */ public class HttpUrlConnectionMessageSender extends AbstractHttpWebServiceMessageSender { + private static final String HTTP_METHOD_POST = "POST"; + public WebServiceConnection createConnection() throws IOException { - URLConnection con = getUrl().openConnection(); - if (!(con instanceof HttpURLConnection)) { + URLConnection connection = getUrl().openConnection(); + if (!(connection instanceof HttpURLConnection)) { throw new HttpTransportException("URL [" + getUrl() + "] is not an HTTP URL"); } - return new HttpUrlConnectionWebServiceConnection((HttpURLConnection) con); + else { + HttpURLConnection httpURLConnection = (HttpURLConnection) connection; + httpURLConnection.setRequestMethod(HTTP_METHOD_POST); + httpURLConnection.setUseCaches(false); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + return new HttpUrlConnection(httpURLConnection); + } } } diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportInputStream.java b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportInputStream.java deleted file mode 100644 index bc4780d1..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportInputStream.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2006 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 java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.ws.transport.TransportInputStream; - -/** - * Implementation of the TransportInputStream interface based on {@link java.net.HttpURLConnection}. - * Exposes the HttpURLConnection. - * - * @author Arjen Poutsma - */ -class HttpUrlConnectionTransportInputStream extends TransportInputStream { - - private final HttpURLConnection connection; - - /** - * Constructs a new instance of the HttpUrlConnectionTransportInputStream based on the given - * HttpURLConnection. - */ - public HttpUrlConnectionTransportInputStream(HttpURLConnection connection) throws IOException { - Assert.notNull(connection, "connection must not be null"); - this.connection = connection; - } - - protected InputStream createInputStream() throws IOException { - if (connection.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) { - return connection.getErrorStream(); - } - else { - return connection.getInputStream(); - } - } - - public Iterator getHeaderNames() throws IOException { - List headerNames = new ArrayList(); - // Header field 0 is the status line, so we start at 1 - int i = 1; - while (true) { - String headerName = connection.getHeaderFieldKey(i); - if (!StringUtils.hasLength(headerName)) { - break; - } - headerNames.add(headerName); - i++; - } - return headerNames.iterator(); - } - - public Iterator getHeaders(String name) throws IOException { - String headerField = connection.getHeaderField(name); - if (headerField == null) { - return Collections.EMPTY_LIST.iterator(); - } - else { - Set tokens = StringUtils.commaDelimitedListToSet(headerField); - return tokens.iterator(); - } - } -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportOutputStream.java b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportOutputStream.java deleted file mode 100644 index 8956c60a..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionTransportOutputStream.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2006 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 java.io.IOException; -import java.io.OutputStream; -import java.net.HttpURLConnection; - -import org.springframework.util.Assert; -import org.springframework.ws.transport.TransportOutputStream; - -/** - * Implementation of the TransportOutputStream interface based on {@link java.net.HttpURLConnection}. - * Exposes the HttpURLConnection. - * - * @author Arjen Poutsma - */ -class HttpUrlConnectionTransportOutputStream extends TransportOutputStream { - - private final HttpURLConnection connection; - - /** - * Constructs a new instance of the HttpUrlConnectionTransportOutputStream based on the given - * HttpURLConnection. - */ - public HttpUrlConnectionTransportOutputStream(HttpURLConnection connection) throws IOException { - Assert.notNull(connection, "connection must not be null"); - this.connection = connection; - } - - public void addHeader(String name, String value) throws IOException { - connection.setRequestProperty(name, value); - } - - protected OutputStream createOutputStream() throws IOException { - return connection.getOutputStream(); - } -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionWebServiceConnection.java b/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionWebServiceConnection.java deleted file mode 100644 index 120186f3..00000000 --- a/core/src/main/java/org/springframework/ws/transport/http/HttpUrlConnectionWebServiceConnection.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 java.io.IOException; -import java.net.HttpURLConnection; -import java.net.ProtocolException; - -import org.springframework.util.Assert; -import org.springframework.ws.WebServiceMessage; -import org.springframework.ws.context.MessageContext; -import org.springframework.ws.transport.TransportOutputStream; -import org.springframework.ws.transport.WebServiceConnection; - -/** - * Implementation of the {@link WebServiceConnection} interface that uses a {@link HttpURLConnection}. - * - * @author Arjen Poutsma - */ -public class HttpUrlConnectionWebServiceConnection implements WebServiceConnection { - - private static final String HTTP_METHOD_POST = "POST"; - - private final HttpURLConnection connection; - - /** - * Creates a new instance of the HttpUrlConnectionWebServiceConnection with the given - * HttpURLConnection. - */ - public HttpUrlConnectionWebServiceConnection(HttpURLConnection connection) { - Assert.notNull(connection, "connection must not be null"); - this.connection = connection; - } - - public void close() { - connection.disconnect(); - } - - public void sendAndReceive(MessageContext messageContext) throws IOException { - prepareConnection(); - writeRequestMessage(messageContext.getRequest()); - validateResponse(); - readResponse(messageContext); - } - - private void prepareConnection() throws ProtocolException { - connection.setRequestMethod(HTTP_METHOD_POST); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - } - - private void writeRequestMessage(WebServiceMessage message) throws IOException { - TransportOutputStream tos = new HttpUrlConnectionTransportOutputStream(connection); - message.writeTo(tos); - tos.flush(); - } - - private void validateResponse() throws IOException { - int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_INTERNAL_ERROR && responseCode / 100 != 2) { - throw new HttpTransportException("Did not receive successful HTTP response: status code = " + responseCode + - ", status message = [" + connection.getResponseMessage() + "]"); - } - } - - private void readResponse(MessageContext messageContext) throws IOException { - if (connection.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT || connection.getContentLength() == 0) { - return; - } - messageContext.readResponse(new HttpUrlConnectionTransportInputStream(connection)); - } - -} diff --git a/core/src/main/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapter.java b/core/src/main/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapter.java index 3e975326..e0ae2366 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapter.java +++ b/core/src/main/java/org/springframework/ws/transport/http/WebServiceMessageReceiverHandlerAdapter.java @@ -16,15 +16,13 @@ package org.springframework.ws.transport.http; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.ModelAndView; import org.springframework.ws.WebServiceMessage; -import org.springframework.ws.transport.TransportInputStream; -import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageReceiver; import org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport; @@ -55,13 +53,13 @@ public class WebServiceMessageReceiverHandlerAdapter extends WebServiceMessageRe HttpServletResponse httpServletResponse, Object handler) throws Exception { if ("POST".equals(httpServletRequest.getMethod())) { - TransportInputStream tis = new HttpServletTransportInputStream(httpServletRequest); - TransportOutputStream tos = new HttpServletTransportOutputStream(httpServletResponse); - handle(tis, tos, (WebServiceMessageReceiver) handler); + WebServiceConnection connection = new HttpServletConnection(httpServletRequest, httpServletResponse); + handleConnection(connection, (WebServiceMessageReceiver) handler); return null; } else { - throw new ServletException("Request method '" + httpServletRequest.getMethod() + "' not supported"); + httpServletResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + return null; } } @@ -69,35 +67,30 @@ public class WebServiceMessageReceiverHandlerAdapter extends WebServiceMessageRe return handler instanceof WebServiceMessageReceiver; } - /** - * Sets the response code to 204, No Content. - */ - protected void handleNoResponse(TransportInputStream tis, TransportOutputStream tos) { - HttpServletResponse httpServletResponse = ((HttpServletTransportOutputStream) tos).getHttpServletResponse(); - httpServletResponse.setStatus(HttpServletResponse.SC_NO_CONTENT); + /** Sets the response code to 204, Accepted. */ + protected void handleNoResponse(WebServiceConnection connection) { + HttpServletResponse httpServletResponse = ((HttpServletConnection) connection).getHttpServletResponse(); + httpServletResponse.setStatus(HttpServletResponse.SC_ACCEPTED); } /** * Sets the response code to 200, OK, for normal responses. Set the code to 500, Internal Server Error, in case of a * SOAP Fault, */ - protected void handleResponse(TransportInputStream tis, TransportOutputStream tos, WebServiceMessage response) - throws Exception { - HttpServletResponse httpServletResponse = ((HttpServletTransportOutputStream) tos).getHttpServletResponse(); + protected void handleResponse(WebServiceConnection connection, WebServiceMessage response) throws Exception { + HttpServletResponse httpServletResponse = ((HttpServletConnection) connection).getHttpServletResponse(); if (response.hasFault()) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { httpServletResponse.setStatus(HttpServletResponse.SC_OK); } - response.writeTo(tos); + super.handleResponse(connection, response); } - /** - * Sets the response code to 404, Not Found. - */ - protected void handleNoEndpointFound(TransportInputStream tis, TransportOutputStream tos) { - HttpServletResponse httpServletResponse = ((HttpServletTransportOutputStream) tos).getHttpServletResponse(); + /** Sets the response code to 404, Not Found. */ + protected void handleNoEndpointFound(WebServiceConnection connection) { + HttpServletResponse httpServletResponse = ((HttpServletConnection) connection).getHttpServletResponse(); httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND); } } 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 88b77573..f2c4cb7e 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 @@ -16,6 +16,8 @@ package org.springframework.ws.transport.support; +import java.io.IOException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; @@ -27,6 +29,7 @@ import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.transport.TransportInputStream; import org.springframework.ws.transport.TransportOutputStream; +import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageReceiver; import org.springframework.ws.transport.context.DefaultTransportContext; import org.springframework.ws.transport.context.TransportContext; @@ -34,30 +37,24 @@ import org.springframework.ws.transport.context.TransportContextHolder; /** * Convenience base class for server-side transport objects. Contains a {@link WebServiceMessageFactory}, and has - * methods for handling incoming WebServiceMessage requests. + * methods for handling incoming {@link WebServiceConnection}s. * * @author Arjen Poutsma - * @see #handle(org.springframework.ws.transport.TransportInputStream,org.springframework.ws.transport.TransportOutputStream,org.springframework.ws.transport.WebServiceMessageReceiver) + * @see #handleConnection */ public abstract class WebServiceMessageReceiverObjectSupport implements InitializingBean { - /** - * Logger available to subclasses. - */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); private WebServiceMessageFactory messageFactory; - /** - * Returns the WebServiceMessageFactory. - */ + /** Returns the WebServiceMessageFactory. */ public WebServiceMessageFactory getMessageFactory() { return messageFactory; } - /** - * Sets the WebServiceMessageFactory. - */ + /** Sets the WebServiceMessageFactory. */ public void setMessageFactory(WebServiceMessageFactory messageFactory) { this.messageFactory = messageFactory; } @@ -67,66 +64,92 @@ public abstract class WebServiceMessageReceiverObjectSupport implements Initiali logger.info("Using message factory [" + messageFactory + "]"); } - protected final void handle(TransportInputStream tis, TransportOutputStream tos, WebServiceMessageReceiver receiver) + /** + * 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. + *

+ * Stores the given connection in the transport context. + * + * @param connection the incoming connection + * @param receiver the handler of the message, typically a {@link org.springframework.ws.server.MessageDispatcher} + * @see org.springframework.ws.transport.context.TransportContext + */ + protected final void handleConnection(WebServiceConnection connection, WebServiceMessageReceiver receiver) throws Exception { TransportContext previousTransportContext = TransportContextHolder.getTransportContext(); - TransportContextHolder.setTransportContext(new DefaultTransportContext(tis, tos)); + TransportContextHolder.setTransportContext(new DefaultTransportContext(connection)); try { - WebServiceMessage messageRequest = getMessageFactory().createWebServiceMessage(tis); - MessageContext messageContext = new DefaultMessageContext(messageRequest, getMessageFactory()); + MessageContext messageContext = handleRequest(connection); receiver.receive(messageContext); if (!messageContext.hasResponse()) { - handleNoResponse(tis, tos); + handleNoResponse(connection); } else { - handleResponse(tis, tos, messageContext.getResponse()); + handleResponse(connection, messageContext.getResponse()); } } catch (NoEndpointFoundException ex) { - handleNoEndpointFound(tis, tos); + handleNoEndpointFound(connection); } finally { + try { + connection.close(); + } + catch (IOException ex) { + logger.debug("Could not close connection", ex); + } TransportContextHolder.setTransportContext(previousTransportContext); } } - /** - * Invoked from {@link #handle(org.springframework.ws.transport.TransportInputStream,org.springframework.ws.transport.TransportOutputStream,org.springframework.ws.transport.WebServiceMessageReceiver)} - * when no response is given. Default implementation does nothing. Can be overriden to set certain - * transport-specific response headers. - * - * @param tis the transport input stream - * @param tos the transport output stream - */ - protected void handleNoResponse(TransportInputStream tis, TransportOutputStream tos) { + private MessageContext handleRequest(WebServiceConnection connection) throws IOException { + TransportInputStream tis = connection.getTransportInputStream(); + try { + WebServiceMessage messageRequest = getMessageFactory().createWebServiceMessage(tis); + return new DefaultMessageContext(messageRequest, getMessageFactory()); + } + finally { + tis.close(); + } } /** - * Handles the sending of the response. Invoked from {@link #handle(org.springframework.ws.transport.TransportInputStream,org.springframework.ws.transport.TransportOutputStream,org.springframework.ws.transport.WebServiceMessageReceiver)}. - * Default implementation writes the given response to the given TransportOutputStream. Can be - * overriden to set certain transport-specific headers. + * Invoked from {@link #handleConnection} when no response is given. Default implementation does nothing. Can be + * overriden to set certain transport-specific response headers. * - * @param tis the transport input stream - * @param tos the transport output stream - * @param response the response message + * @param connection the incoming connection + */ + protected void handleNoResponse(WebServiceConnection connection) { + } + + /** + * Handles the sending of the response. Invoked from {@link #handleConnection}. Default implementation writes the + * given response to the given TransportOutputStream. Can be overriden to set certain + * transport-specific headers. + * + * @param connection the incoming connection + * @param response the response message * @see WebServiceMessage#writeTo(java.io.OutputStream) */ - protected void handleResponse(TransportInputStream tis, TransportOutputStream tos, WebServiceMessage response) - throws Exception { - response.writeTo(tos); - tos.flush(); + protected void handleResponse(WebServiceConnection connection, WebServiceMessage response) throws Exception { + TransportOutputStream tos = connection.getTransportOutputStream(); + try { + response.writeTo(tos); + tos.flush(); + } + finally { + tos.close(); + } } /** - * Invoked from {@link #handle(org.springframework.ws.transport.TransportInputStream,org.springframework.ws.transport.TransportOutputStream,org.springframework.ws.transport.WebServiceMessageReceiver)} - * when no suitable endpoint is found. Default implementation does nothing. Can be overriden to set certain - * transport-specific response headers. + * Invoked from {@link #handleConnection} when no suitable endpoint is found. Default implementation does nothing. + * Can be overriden to set certain transport-specific response headers. * - * @param tis the transport input stream - * @param tos the transport output stream + * @param connection the incoming connection */ - protected void handleNoEndpointFound(TransportInputStream tis, TransportOutputStream tos) { + protected void handleNoEndpointFound(WebServiceConnection connection) { } } diff --git a/core/src/test/java/org/springframework/ws/client/core/SimpleSaajServlet.java b/core/src/test/java/org/springframework/ws/client/core/SimpleSaajServlet.java new file mode 100644 index 00000000..c4057aa1 --- /dev/null +++ b/core/src/test/java/org/springframework/ws/client/core/SimpleSaajServlet.java @@ -0,0 +1,122 @@ +/* + * 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.client.core; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.StringTokenizer; +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.SOAPException; +import javax.xml.soap.SOAPMessage; + +/** + * A simple Servlet that uses SAAJ to echo request. + * + * @author Arjen Poutsma + */ +public class SimpleSaajServlet extends HttpServlet { + + private MessageFactory msgFactory = null; + + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + try { + msgFactory = MessageFactory.newInstance(); + } + catch (SOAPException ex) { + throw new ServletException("Unable to create message factory" + ex.getMessage()); + } + } + + 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()); + + if (values.length == 1) { + res.setHeader(header.getName(), header.getValue()); + } + else { + StringBuffer concat = new StringBuffer(); + int i = 0; + + while (i < values.length) { + if (i != 0) { + concat.append(','); + } + concat.append(values[i++]); + } + res.setHeader(header.getName(), concat.toString()); + } + } + } + + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + MimeHeaders headers = getHeaders(req); + SOAPMessage msg = msgFactory.createMessage(headers, req.getInputStream()); + SOAPMessage reply = onMessage(msg); + resp.setStatus(HttpServletResponse.SC_OK); + if (reply != null) { + if (reply.saveRequired()) { + reply.saveChanges(); + } + resp.setStatus(HttpServletResponse.SC_OK); + putHeaders(reply.getMimeHeaders(), resp); + reply.writeTo(resp.getOutputStream()); + } + } + catch (Exception ex) { + throw new ServletException("SAAJ POST failed " + ex.getMessage()); + } + } + + public SOAPMessage onMessage(SOAPMessage message) { + return message; + } + + +} 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 new file mode 100644 index 00000000..0adc740d --- /dev/null +++ b/core/src/test/java/org/springframework/ws/client/core/WebServiceTemplateIntegrationTest.java @@ -0,0 +1,68 @@ +/* + * 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.client.core; + +import java.net.URL; +import javax.xml.soap.MessageFactory; + +import org.custommonkey.xmlunit.XMLTestCase; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; +import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender; +import org.springframework.xml.transform.StringResult; +import org.springframework.xml.transform.StringSource; + +public class WebServiceTemplateIntegrationTest extends XMLTestCase { + + private WebServiceTemplate template; + + private Server jettyServer; + + protected void setUp() throws Exception { + jettyServer = new Server(8085); + Context jettyContext = new Context(jettyServer, "/"); + jettyContext.addServlet(SimpleSaajServlet.class, "/"); + jettyServer.start(); + template = new WebServiceTemplate(); + HttpUrlConnectionMessageSender messageSender = new HttpUrlConnectionMessageSender(); + messageSender.setUrl(new URL("http://localhost:8085/")); + template.setMessageSender(messageSender); + } + + protected void tearDown() throws Exception { + jettyServer.stop(); + } + + public void testSendAndReceiveSaaj() throws Exception { + template.setMessageFactory(new SaajSoapMessageFactory(MessageFactory.newInstance())); + String content = ""; + StringResult result = new StringResult(); + template.sendAndReceive(new StringSource(content), result); + assertXMLEqual(content, result.toString()); + } + + public void testSendAndReceiveAxiom() throws Exception { + template.setMessageFactory(new AxiomSoapMessageFactory()); + String content = ""; + StringResult result = new StringResult(); + template.sendAndReceive(new StringSource(content), result); + assertXMLEqual(content, result.toString()); + } + +} 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 3f675d9e..552575d7 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 @@ -16,17 +16,20 @@ package org.springframework.ws.client.core; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import javax.xml.transform.Result; -import javax.xml.transform.Source; +import java.util.Collections; import org.custommonkey.xmlunit.XMLTestCase; import org.easymock.MockControl; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; +import org.springframework.ws.MockWebServiceMessageFactory; import org.springframework.ws.WebServiceMessage; -import org.springframework.ws.WebServiceMessageFactory; -import org.springframework.ws.context.MessageContext; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.MockTransportInputStream; +import org.springframework.ws.transport.MockTransportOutputStream; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.xml.transform.StringResult; @@ -34,38 +37,29 @@ import org.springframework.xml.transform.StringSource; public class WebServiceTemplateTest extends XMLTestCase { - private static final WebServiceMessageExtractor SIMPLE_EXTRACTOR = new WebServiceMessageExtractor() { - - public Object extractData(WebServiceMessage message) throws IOException { - return message; - } - }; - private WebServiceTemplate template; - private MockControl factoryControl; + private MockControl connectionControl; - private WebServiceMessageFactory factoryMock; - - private MockControl messageControl; - - private WebServiceMessage requestMock; - - private WebServiceMessage responseMock; + private FaultAwareWebServiceConnection connectionMock; protected void setUp() throws Exception { template = new WebServiceTemplate(); - factoryControl = MockControl.createControl(WebServiceMessageFactory.class); - factoryMock = (WebServiceMessageFactory) factoryControl.getMock(); - template.setMessageFactory(factoryMock); - messageControl = MockControl.createControl(WebServiceMessage.class); - requestMock = (WebServiceMessage) messageControl.getMock(); - responseMock = (WebServiceMessage) messageControl.getMock(); + MockWebServiceMessageFactory messageFactory = new MockWebServiceMessageFactory(); + template.setMessageFactory(messageFactory); + connectionControl = MockControl.createStrictControl(FaultAwareWebServiceConnection.class); + connectionMock = (FaultAwareWebServiceConnection) connectionControl.getMock(); + template.setMessageSender(new WebServiceMessageSender() { + + public WebServiceConnection createConnection() throws IOException { + return connectionMock; + } + }); + } public void testMarshalAndSendNoMarshallerSet() throws Exception { template.setMarshaller(null); - replayMockControls(); try { template.marshalSendAndReceive(new Object()); fail("IllegalStateException expected"); @@ -73,12 +67,10 @@ public class WebServiceTemplateTest extends XMLTestCase { catch (IllegalStateException ex) { // expected behavior } - verifyMockControls(); } public void testMarshalAndSendNoUnmarshallerSet() throws Exception { template.setUnmarshaller(null); - replayMockControls(); try { template.marshalSendAndReceive(new Object()); fail("IllegalStateException expected"); @@ -86,209 +78,213 @@ public class WebServiceTemplateTest extends XMLTestCase { catch (IllegalStateException ex) { // expected behavior } - verifyMockControls(); } public void testSendAndReceiveMessageResponse() throws Exception { - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); - messageControl.expectAndReturn(responseMock.hasFault(), false); - template.setMessageSender(new ResponseMessageSender()); - replayMockControls(); - WebServiceMessage response = (WebServiceMessage) template.sendAndReceive(new WebServiceMessageCallback() { - public void doInMessage(WebServiceMessage message) throws IOException { - assertEquals("Invalid request message", requestMock, message); - } - }, SIMPLE_EXTRACTOR); - assertEquals("Invalid response", responseMock, response); - verifyMockControls(); + MockControl callbackControl = MockControl.createControl(WebServiceMessageCallback.class); + WebServiceMessageCallback requestCallback = (WebServiceMessageCallback) callbackControl.getMock(); + requestCallback.doInMessage(null); + callbackControl.setMatcher(MockControl.ALWAYS_MATCHER); + callbackControl.replay(); + + MockControl extractorControl = MockControl.createControl(WebServiceMessageExtractor.class); + WebServiceMessageExtractor extractorMock = (WebServiceMessageExtractor) extractorControl.getMock(); + extractorMock.extractData(null); + extractorControl.setMatcher(MockControl.ALWAYS_MATCHER); + Object extracted = new Object(); + extractorControl.setReturnValue(extracted); + extractorControl.replay(); + + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), new MockTransportInputStream( + new ByteArrayInputStream("".getBytes("UTF-8")), Collections.EMPTY_MAP)); + connectionControl.expectAndReturn(connectionMock.hasFault(), false); + connectionMock.close(); + connectionControl.replay(); + + Object result = template.sendAndReceive(requestCallback, extractorMock); + assertEquals("Invalid response", extracted, result); + + callbackControl.verify(); + extractorControl.verify(); + connectionControl.verify(); + } public void testSendAndReceiveMessageNoResponse() throws Exception { - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - template.setMessageSender(new NoResponseMessageSender()); - replayMockControls(); - WebServiceMessage response = (WebServiceMessage) template.sendAndReceive(new WebServiceMessageCallback() { - public void doInMessage(WebServiceMessage message) throws IOException { - assertEquals("Invalid request message", requestMock, message); - } - }, SIMPLE_EXTRACTOR); - assertNull("No response", response); - verifyMockControls(); + MockControl callbackControl = MockControl.createControl(WebServiceMessageCallback.class); + WebServiceMessageCallback requestCallback = (WebServiceMessageCallback) callbackControl.getMock(); + requestCallback.doInMessage(null); + callbackControl.setMatcher(MockControl.ALWAYS_MATCHER); + callbackControl.replay(); + + MockControl extractorControl = MockControl.createControl(WebServiceMessageExtractor.class); + WebServiceMessageExtractor extractorMock = (WebServiceMessageExtractor) extractorControl.getMock(); + extractorControl.replay(); + + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), null); + connectionMock.close(); + connectionControl.replay(); + + Object result = (WebServiceMessage) template.sendAndReceive(requestCallback, extractorMock); + assertNull("Invalid response", result); + callbackControl.verify(); + extractorControl.verify(); + connectionControl.verify(); } - public void testSendAndReceiveMessageFaultResponse() throws Exception { - MockControl resolverControl = MockControl.createControl(FaultResolver.class); - FaultResolver resolverMock = (FaultResolver) resolverControl.getMock(); - template.setFaultResolver(resolverMock); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); - messageControl.expectAndReturn(responseMock.hasFault(), true); - resolverMock.resolveFault(responseMock); - template.setMessageSender(new ResponseMessageSender()); - replayMockControls(); - resolverControl.replay(); - WebServiceMessage response = (WebServiceMessage) template.sendAndReceive(new WebServiceMessageCallback() { - public void doInMessage(WebServiceMessage message) throws IOException { - assertEquals("Invalid request message", requestMock, message); - } - }, SIMPLE_EXTRACTOR); - assertNull("Invalid response", response); - verifyMockControls(); - resolverControl.verify(); + public void testSendAndReceiveMessageFault() throws Exception { + MockControl callbackControl = MockControl.createControl(WebServiceMessageCallback.class); + WebServiceMessageCallback requestCallback = (WebServiceMessageCallback) callbackControl.getMock(); + requestCallback.doInMessage(null); + callbackControl.setMatcher(MockControl.ALWAYS_MATCHER); + callbackControl.replay(); + + MockControl extractorControl = MockControl.createControl(WebServiceMessageExtractor.class); + WebServiceMessageExtractor extractorMock = (WebServiceMessageExtractor) extractorControl.getMock(); + extractorControl.replay(); + + MockControl faultResolverControl = MockControl.createControl(FaultResolver.class); + FaultResolver faultResolverMock = (FaultResolver) faultResolverControl.getMock(); + template.setFaultResolver(faultResolverMock); + faultResolverMock.resolveFault(null); + faultResolverControl.setMatcher(MockControl.ALWAYS_MATCHER); + faultResolverControl.replay(); + + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), new MockTransportInputStream( + new ByteArrayInputStream("".getBytes("UTF-8")), Collections.EMPTY_MAP)); + connectionControl.expectAndReturn(connectionMock.hasFault(), true); + connectionMock.close(); + connectionControl.replay(); + + Object result = template.sendAndReceive(requestCallback, extractorMock); + assertNull("Invalid response", result); + + callbackControl.verify(); + extractorControl.verify(); + connectionControl.verify(); } - public void testSendAndReceiveSourceExtractor() throws Exception { - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); - messageControl.expectAndReturn(responseMock.hasFault(), false); - messageControl.expectAndReturn(requestMock.getPayloadResult(), new StringResult()); - final Source expected = new StringSource(""); - messageControl.expectAndReturn(responseMock.getPayloadSource(), expected); - template.setMessageSender(new ResponseMessageSender()); - replayMockControls(); - Object result = template.sendAndReceive(new StringSource(""), new SourceExtractor() { + public void testSendAndReceiveSourceResponse() throws Exception { + MockControl extractorControl = MockControl.createControl(SourceExtractor.class); + SourceExtractor extractorMock = (SourceExtractor) extractorControl.getMock(); + extractorMock.extractData(null); + extractorControl.setMatcher(MockControl.ALWAYS_MATCHER); + Object extracted = new Object(); + extractorControl.setReturnValue(extracted); + extractorControl.replay(); - public Object extractData(Source source) throws IOException { - assertEquals("Invalid response", expected, source); - return null; - } - }); - assertNull("Invalid result", result); + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), new MockTransportInputStream( + new ByteArrayInputStream("".getBytes("UTF-8")), Collections.EMPTY_MAP)); + connectionControl.expectAndReturn(connectionMock.hasFault(), false); + connectionMock.close(); + connectionControl.replay(); + + Object result = template.sendAndReceive(new StringSource(""), extractorMock); + assertEquals("Invalid response", extracted, result); + + extractorControl.verify(); + connectionControl.verify(); } public void testSendAndReceiveSourceNoResponse() throws Exception { - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - messageControl.expectAndReturn(requestMock.getPayloadResult(), new StringResult()); - template.setMessageSender(new NoResponseMessageSender()); - replayMockControls(); - Object result = template.sendAndReceive(new StringSource(""), new SourceExtractor() { + MockControl extractorControl = MockControl.createControl(SourceExtractor.class); + SourceExtractor extractorMock = (SourceExtractor) extractorControl.getMock(); + extractorControl.replay(); - public Object extractData(Source source) throws IOException { - assertNull("Invalid response", source); - return null; - } - }); - assertNull("Invalid result", result); + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), null); + connectionMock.close(); + connectionControl.replay(); + + Object result = template.sendAndReceive(new StringSource(""), extractorMock); + assertNull("Invalid response", result); + + extractorControl.verify(); + connectionControl.verify(); } public void testSendAndReceiveResultResponse() throws Exception { - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); - messageControl.expectAndReturn(responseMock.hasFault(), false); - messageControl.expectAndReturn(requestMock.getPayloadResult(), new StringResult()); - Source expected = new StringSource(""); - messageControl.expectAndReturn(responseMock.getPayloadSource(), expected); - template.setMessageSender(new ResponseMessageSender()); - replayMockControls(); - Result result = new StringResult(); + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), new MockTransportInputStream( + new ByteArrayInputStream("".getBytes("UTF-8")), Collections.EMPTY_MAP)); + connectionControl.expectAndReturn(connectionMock.hasFault(), false); + connectionMock.close(); + connectionControl.replay(); + + StringResult result = new StringResult(); template.sendAndReceive(new StringSource(""), result); - assertXMLEqual("Invalid response", "", result.toString()); + + connectionControl.verify(); } - public void testSendAndReceiveMarshallResponse() throws Exception { - template.setMessageSender(new ResponseMessageSender()); + public void testSendAndReceiveMarshalResponse() throws Exception { MockControl marshallerControl = MockControl.createControl(Marshaller.class); Marshaller marshallerMock = (Marshaller) marshallerControl.getMock(); template.setMarshaller(marshallerMock); + marshallerMock.marshal(null, null); + marshallerControl.setMatcher(MockControl.ALWAYS_MATCHER); + marshallerControl.replay(); + MockControl unmarshallerControl = MockControl.createControl(Unmarshaller.class); Unmarshaller unmarshallerMock = (Unmarshaller) unmarshallerControl.getMock(); template.setUnmarshaller(unmarshallerMock); - Object request = new Object(); - Object expected = new Object(); - Result requestResult = new StringResult(); - Source responseSource = new StringSource(""); - - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - messageControl.expectAndReturn(requestMock.getPayloadResult(), requestResult); - marshallerMock.marshal(request, requestResult); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); - messageControl.expectAndReturn(responseMock.hasFault(), false); - messageControl.expectAndReturn(responseMock.getPayloadSource(), responseSource); - unmarshallerControl.expectAndReturn(unmarshallerMock.unmarshal(responseSource), expected); - - replayMockControls(); - marshallerControl.replay(); + unmarshallerMock.unmarshal(null); + unmarshallerControl.setMatcher(MockControl.ALWAYS_MATCHER); + Object unmarshalled = new Object(); + unmarshallerControl.setReturnValue(unmarshalled); unmarshallerControl.replay(); - Object response = template.marshalSendAndReceive(request); + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), new MockTransportInputStream( + new ByteArrayInputStream("".getBytes("UTF-8")), Collections.EMPTY_MAP)); + connectionControl.expectAndReturn(connectionMock.hasFault(), false); + connectionMock.close(); + connectionControl.replay(); - assertEquals("Invalid response", expected, response); + Object result = template.marshalSendAndReceive(new Object()); + assertEquals("Invalid result", unmarshalled, result); - verifyMockControls(); + connectionControl.verify(); marshallerControl.verify(); unmarshallerControl.verify(); } - public void testSendAndReceiveMarshallNoResponse() throws Exception { - template.setMessageSender(new NoResponseMessageSender()); + public void testSendAndReceiveMarshalNoResponse() throws Exception { MockControl marshallerControl = MockControl.createControl(Marshaller.class); Marshaller marshallerMock = (Marshaller) marshallerControl.getMock(); template.setMarshaller(marshallerMock); + marshallerMock.marshal(null, null); + marshallerControl.setMatcher(MockControl.ALWAYS_MATCHER); + marshallerControl.replay(); + MockControl unmarshallerControl = MockControl.createControl(Unmarshaller.class); Unmarshaller unmarshallerMock = (Unmarshaller) unmarshallerControl.getMock(); template.setUnmarshaller(unmarshallerMock); - Object request = new Object(); - Result requestResult = new StringResult(); + unmarshallerControl.replay(); - factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), requestMock); - messageControl.expectAndReturn(requestMock.getPayloadResult(), requestResult); - marshallerMock.marshal(request, requestResult); + connectionControl.expectAndReturn(connectionMock.getTransportOutputStream(), + new MockTransportOutputStream(new ByteArrayOutputStream())); + connectionControl.expectAndReturn(connectionMock.getTransportInputStream(), null); + connectionMock.close(); + connectionControl.replay(); - replayMockControls(); - marshallerControl.replay(); + Object result = template.marshalSendAndReceive(new Object()); + assertNull("Invalid result", result); - Object response = template.marshalSendAndReceive(request); - - assertNull("Invalid response", response); - - verifyMockControls(); + connectionControl.verify(); marshallerControl.verify(); - } - - private void replayMockControls() { - factoryControl.replay(); - messageControl.replay(); - } - - private void verifyMockControls() { - factoryControl.verify(); - messageControl.verify(); - } - - private class NoResponseMessageSender implements WebServiceMessageSender { - - public WebServiceConnection createConnection() throws IOException { - return new NoResponseConnection(); - } - } - - private class NoResponseConnection implements WebServiceConnection { - - public void sendAndReceive(MessageContext messageContext) throws IOException { - assertEquals("Invalid request message", requestMock, messageContext.getRequest()); - } - - public void close() throws IOException { - } - } - - private class ResponseMessageSender implements WebServiceMessageSender { - - public WebServiceConnection createConnection() throws IOException { - return new ResponseConnection(); - } - } - - private class ResponseConnection implements WebServiceConnection { - - public void sendAndReceive(MessageContext messageContext) throws IOException { - assertEquals("Invalid request message", requestMock, messageContext.getRequest()); - messageContext.getResponse(); - } - - public void close() throws IOException { - } + unmarshallerControl.verify(); } } \ No newline at end of file diff --git a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java index ed274aaa..64324912 100644 --- a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java @@ -24,7 +24,7 @@ import org.springframework.ws.soap.AbstractSoapMessageFactoryTestCase; import org.springframework.ws.soap.Attachment; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.SoapVersion; -import org.springframework.ws.transport.StubTransportInputStream; +import org.springframework.ws.transport.MockTransportInputStream; import org.springframework.ws.transport.TransportInputStream; public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapMessageFactoryTestCase { @@ -42,7 +42,7 @@ public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapM headers.setProperty("Content-Type", "text/xml"); String soapAction = "http://springframework.org/spring-ws/Action"; headers.setProperty("SOAPAction", soapAction); - TransportInputStream tis = new StubTransportInputStream(is, headers); + TransportInputStream tis = new MockTransportInputStream(is, headers); WebServiceMessage message = messageFactory.createWebServiceMessage(tis); assertTrue("Not a SoapMessage", message instanceof SoapMessage); @@ -56,7 +56,7 @@ public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapM Properties headers = new Properties(); headers.setProperty("Content-Type", "multipart/related; type=\"text/xml\"; boundary=\"----=_Part_0_11416420.1149699787554\""); - TransportInputStream tis = new StubTransportInputStream(is, headers); + TransportInputStream tis = new MockTransportInputStream(is, headers); WebServiceMessage message = messageFactory.createWebServiceMessage(tis); assertTrue("Not a SoapMessage", message instanceof SoapMessage); diff --git a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageTestCase.java index ae1ce476..87a70b05 100644 --- a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageTestCase.java @@ -25,7 +25,7 @@ import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.Resource; import org.springframework.ws.soap.AbstractSoapMessageTestCase; import org.springframework.ws.soap.SoapVersion; -import org.springframework.ws.transport.StubTransportOutputStream; +import org.springframework.ws.transport.MockTransportOutputStream; public abstract class AbstractSoap11MessageTestCase extends AbstractSoapMessageTestCase { @@ -46,7 +46,7 @@ public abstract class AbstractSoap11MessageTestCase extends AbstractSoapMessageT public void testWriteToTransportOutputStream() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StubTransportOutputStream tos = new StubTransportOutputStream(bos); + MockTransportOutputStream tos = new MockTransportOutputStream(bos); String soapAction = "http://springframework.org/spring-ws/Action"; soapMessage.setSoapAction(soapAction); soapMessage.writeTo(tos); @@ -63,7 +63,7 @@ public abstract class AbstractSoap11MessageTestCase extends AbstractSoapMessageT InputStreamSource inputStreamSource = new ByteArrayResource("contents".getBytes("UTF-8")); soapMessage.addAttachment(inputStreamSource, "text/plain"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StubTransportOutputStream tos = new StubTransportOutputStream(bos); + MockTransportOutputStream tos = new MockTransportOutputStream(bos); soapMessage.writeTo(tos); String contentType = (String) tos.getHeaders().get("Content-Type"); assertTrue("Invalid Content-Type set", contentType.indexOf(SoapVersion.SOAP_11.getContentType()) != -1); diff --git a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java index 149269ff..1935e3b1 100644 --- a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java @@ -24,7 +24,7 @@ import org.springframework.ws.soap.AbstractSoapMessageFactoryTestCase; import org.springframework.ws.soap.Attachment; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.SoapVersion; -import org.springframework.ws.transport.StubTransportInputStream; +import org.springframework.ws.transport.MockTransportInputStream; import org.springframework.ws.transport.TransportInputStream; public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapMessageFactoryTestCase { @@ -40,7 +40,7 @@ public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapM InputStream is = AbstractSoap12MessageFactoryTestCase.class.getResourceAsStream("soap12.xml"); final Properties headers = new Properties(); headers.setProperty("Content-Type", "application/soap+xml"); - TransportInputStream tis = new StubTransportInputStream(is, headers); + TransportInputStream tis = new MockTransportInputStream(is, headers); WebServiceMessage message = messageFactory.createWebServiceMessage(tis); assertTrue("Not a SoapMessage", message instanceof SoapMessage); @@ -53,7 +53,7 @@ public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapM Properties headers = new Properties(); headers.setProperty("Content-Type", "multipart/related; type=\"application/soap+xml\"; boundary=\"----=_Part_0_11416420.1149699787554\""); - TransportInputStream tis = new StubTransportInputStream(is, headers); + TransportInputStream tis = new MockTransportInputStream(is, headers); WebServiceMessage message = messageFactory.createWebServiceMessage(tis); assertTrue("Not a SoapMessage", message instanceof SoapMessage); diff --git a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageTestCase.java index 9509a53b..80d8890d 100644 --- a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageTestCase.java @@ -25,7 +25,7 @@ import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.Resource; import org.springframework.ws.soap.AbstractSoapMessageTestCase; import org.springframework.ws.soap.SoapVersion; -import org.springframework.ws.transport.StubTransportOutputStream; +import org.springframework.ws.transport.MockTransportOutputStream; public abstract class AbstractSoap12MessageTestCase extends AbstractSoapMessageTestCase { @@ -47,7 +47,7 @@ public abstract class AbstractSoap12MessageTestCase extends AbstractSoapMessageT public void testWriteToTransportResponse() throws Exception { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StubTransportOutputStream tos = new StubTransportOutputStream(bos); + MockTransportOutputStream tos = new MockTransportOutputStream(bos); soapMessage.writeTo(tos); String result = bos.toString("UTF-8"); @@ -60,7 +60,7 @@ public abstract class AbstractSoap12MessageTestCase extends AbstractSoapMessageT InputStreamSource inputStreamSource = new ByteArrayResource("contents".getBytes("UTF-8")); soapMessage.addAttachment(inputStreamSource, "text/plain"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StubTransportOutputStream tos = new StubTransportOutputStream(bos); + MockTransportOutputStream tos = new MockTransportOutputStream(bos); soapMessage.writeTo(tos); String contentType = (String) tos.getHeaders().get("Content-Type"); assertTrue("Invalid Content-Type set", contentType.indexOf(SoapVersion.SOAP_12.getContentType()) != -1); diff --git a/core/src/test/java/org/springframework/ws/transport/StubTransportInputStream.java b/core/src/test/java/org/springframework/ws/transport/MockTransportInputStream.java similarity index 92% rename from core/src/test/java/org/springframework/ws/transport/StubTransportInputStream.java rename to core/src/test/java/org/springframework/ws/transport/MockTransportInputStream.java index 5db01410..30f056f8 100644 --- a/core/src/test/java/org/springframework/ws/transport/StubTransportInputStream.java +++ b/core/src/test/java/org/springframework/ws/transport/MockTransportInputStream.java @@ -24,13 +24,13 @@ import java.util.Map; import org.springframework.util.Assert; -public class StubTransportInputStream extends TransportInputStream { +public class MockTransportInputStream extends TransportInputStream { private Map headers; private InputStream inputStream; - public StubTransportInputStream(InputStream inputStream, Map headers) { + public MockTransportInputStream(InputStream inputStream, Map headers) { Assert.notNull(inputStream, "inputStream must not be null"); Assert.notNull(headers, "headers must not be null"); this.inputStream = inputStream; diff --git a/core/src/test/java/org/springframework/ws/transport/StubTransportOutputStream.java b/core/src/test/java/org/springframework/ws/transport/MockTransportOutputStream.java similarity index 91% rename from core/src/test/java/org/springframework/ws/transport/StubTransportOutputStream.java rename to core/src/test/java/org/springframework/ws/transport/MockTransportOutputStream.java index 95e2ebcb..d82b0277 100644 --- a/core/src/test/java/org/springframework/ws/transport/StubTransportOutputStream.java +++ b/core/src/test/java/org/springframework/ws/transport/MockTransportOutputStream.java @@ -23,13 +23,13 @@ import java.util.Map; import org.springframework.util.Assert; -public class StubTransportOutputStream extends TransportOutputStream { +public class MockTransportOutputStream extends TransportOutputStream { private Map headers = new HashMap(); private OutputStream outputStream; - public StubTransportOutputStream(OutputStream outputStream) { + public MockTransportOutputStream(OutputStream outputStream) { Assert.notNull(outputStream, "outputStream must not be null"); this.outputStream = outputStream; } diff --git a/core/src/test/java/org/springframework/ws/transport/http/AbstractHttpWebServiceMessageSenderTestCase.java b/core/src/test/java/org/springframework/ws/transport/http/AbstractHttpWebServiceMessageSenderTestCase.java index 968e4e28..675ca0d5 100644 --- a/core/src/test/java/org/springframework/ws/transport/http/AbstractHttpWebServiceMessageSenderTestCase.java +++ b/core/src/test/java/org/springframework/ws/transport/http/AbstractHttpWebServiceMessageSenderTestCase.java @@ -16,22 +16,16 @@ package org.springframework.ws.transport.http; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; +import java.util.Iterator; import javax.servlet.GenericServlet; +import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.xml.soap.MessageFactory; -import javax.xml.soap.SOAPMessage; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.stream.StreamSource; import org.custommonkey.xmlunit.XMLTestCase; import org.custommonkey.xmlunit.XMLUnit; @@ -39,13 +33,10 @@ import org.mortbay.jetty.Server; import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.ServletHolder; import org.springframework.util.FileCopyUtils; -import org.springframework.ws.context.DefaultMessageContext; -import org.springframework.ws.context.MessageContext; -import org.springframework.ws.soap.saaj.SaajSoapMessage; -import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; +import org.springframework.ws.transport.FaultAwareWebServiceConnection; +import org.springframework.ws.transport.TransportInputStream; +import org.springframework.ws.transport.TransportOutputStream; import org.springframework.ws.transport.WebServiceConnection; -import org.springframework.xml.transform.StringResult; -import org.springframework.xml.transform.StringSource; public abstract class AbstractHttpWebServiceMessageSenderTestCase extends XMLTestCase { @@ -59,34 +50,17 @@ public abstract class AbstractHttpWebServiceMessageSenderTestCase extends XMLTes private static final String RESPONSE_HEADER_VALUE = "ResponseHeaderValue"; - protected static final String URL = "http://localhost:8888"; + protected static final String REQUEST = "Request"; - protected static final String REQUEST = ""; - - protected static final String EXPECTED_SOAP_REQUEST = - "" + "" + - "" + REQUEST + "" + ""; - - protected static final String RESPONSE = ""; - - protected Transformer transformer; + protected static final String RESPONSE = "Response"; protected AbstractHttpWebServiceMessageSender messageSender; - private MessageFactory messageFactory; - - private String receivedRequest; - - private String receivedHeader; + private Context jettyContext; protected final void setUp() throws Exception { - messageFactory = MessageFactory.newInstance(); - transformer = TransformerFactory.newInstance().newTransformer(); - jettyServer = new Server(8888); - Context root = new Context(jettyServer, "/"); - root.addServlet(new ServletHolder(new ResponseServlet()), "/response"); - root.addServlet(new ServletHolder(new NoResponseServlet()), "/noresponse"); - jettyServer.start(); + jettyServer = new Server(8085); + jettyContext = new Context(jettyServer, "/"); messageSender = createMessageSender(); XMLUnit.setIgnoreWhitespace(true); } @@ -94,31 +68,64 @@ public abstract class AbstractHttpWebServiceMessageSenderTestCase extends XMLTes protected abstract AbstractHttpWebServiceMessageSender createMessageSender(); protected final void tearDown() throws Exception { - jettyServer.stop(); + if (jettyServer.isRunning()) { + jettyServer.stop(); + } } public void testSendAndReceiveResponse() throws Exception { - messageSender.setUrl(new URL("http://localhost:8888/response")); - SOAPMessage saajRequest = messageFactory.createMessage(); - saajRequest.getMimeHeaders().addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE); - transformer.transform(new StringSource(REQUEST), new DOMResult(saajRequest.getSOAPBody())); - SaajSoapMessage request = new SaajSoapMessage(saajRequest); - MessageContext context = new DefaultMessageContext(request, new SaajSoapMessageFactory(messageFactory)); - WebServiceConnection connection = messageSender.createConnection(); + validateResponse(new MyServlet(true)); + } + + public void testSendAndReceiveResponseInvalidContentLength() throws Exception { + validateResponse(new MyServlet(true, HttpServletResponse.SC_OK, false)); + } + + public void testSendAndReceiveFault() throws Exception { + jettyContext + .addServlet(new ServletHolder(new MyServlet(true, HttpServletResponse.SC_INTERNAL_SERVER_ERROR)), "/"); + jettyServer.start(); + messageSender.setUrl(new URL("http://localhost:8085/")); + FaultAwareWebServiceConnection connection = (FaultAwareWebServiceConnection) messageSender.createConnection(); try { - connection.sendAndReceive(context); - assertXMLEqual(EXPECTED_SOAP_REQUEST, receivedRequest.toString()); - assertEquals("Invalid header value received on server side", REQUEST_HEADER_VALUE, receivedHeader); - assertTrue("No response", context.hasResponse()); - SaajSoapMessage response = (SaajSoapMessage) context.getResponse(); - SOAPMessage saajResponse = response.getSaajMessage(); - assertNotNull("No header value received on client side", - saajResponse.getMimeHeaders().getHeader(RESPONSE_HEADER_NAME)); - assertEquals("Invalid header value received on client side", RESPONSE_HEADER_VALUE, - saajResponse.getMimeHeaders().getHeader(RESPONSE_HEADER_NAME)[0]); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - response.writeTo(os); - assertXMLEqual(RESPONSE, os.toString("UTF-8")); + TransportOutputStream tos = connection.getTransportOutputStream(); + tos.addHeader("Content-Type", "text/xml"); + tos.addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE); + FileCopyUtils.copy(REQUEST.getBytes("UTF-8"), tos); + assertNotNull("No response", connection.getTransportInputStream()); + assertTrue("Response has no fault", connection.hasFault()); + } + finally { + connection.close(); + } + } + + private void validateResponse(Servlet servlet) throws Exception { + jettyContext.addServlet(new ServletHolder(servlet), "/"); + jettyServer.start(); + messageSender.setUrl(new URL("http://localhost:8085/")); + FaultAwareWebServiceConnection connection = (FaultAwareWebServiceConnection) messageSender.createConnection(); + try { + TransportOutputStream tos = connection.getTransportOutputStream(); + tos.addHeader("Content-Type", "text/xml"); + tos.addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE); + FileCopyUtils.copy(REQUEST.getBytes("UTF-8"), tos); + assertNotNull("No response", connection.getTransportInputStream()); + assertFalse("Response has fault", connection.hasFault()); + TransportInputStream tis = connection.getTransportInputStream(); + boolean headerFound = false; + for (Iterator iterator = tis.getHeaderNames(); iterator.hasNext();) { + String headerName = (String) iterator.next(); + if (RESPONSE_HEADER_NAME.equals(headerName)) { + headerFound = true; + } + } + assertTrue("Response has invalid header", headerFound); + Iterator headerValues = tis.getHeaders(RESPONSE_HEADER_NAME); + assertTrue("Response has no header values", headerValues.hasNext()); + assertEquals("Response has invalid header values", RESPONSE_HEADER_VALUE, headerValues.next()); + String result = new String(FileCopyUtils.copyToByteArray(tis), "UTF-8"); + assertEquals("Invalid response", RESPONSE, result); } finally { connection.close(); @@ -126,56 +133,72 @@ public abstract class AbstractHttpWebServiceMessageSenderTestCase extends XMLTes } public void testSendAndReceiveNoResponse() throws Exception { - messageSender.setUrl(new URL("http://localhost:8888/noresponse")); - SOAPMessage saajRequest = messageFactory.createMessage(); - transformer.transform(new StringSource(REQUEST), new DOMResult(saajRequest.getSOAPBody())); - SaajSoapMessage request = new SaajSoapMessage(saajRequest); - MessageContext context = new DefaultMessageContext(request, new SaajSoapMessageFactory(messageFactory)); + validateNonResponse(new MyServlet(false)); + } + + public void testSendAndReceiveNoResponseAccepted() throws Exception { + validateNonResponse(new MyServlet(false, HttpServletResponse.SC_ACCEPTED)); + } + + public void testSendAndReceiveNoResponseInvalidContentLength() throws Exception { + validateNonResponse(new MyServlet(false, HttpServletResponse.SC_OK, false)); + } + + private void validateNonResponse(Servlet servlet) throws Exception { + jettyContext.addServlet(new ServletHolder(servlet), "/"); + jettyServer.start(); + + messageSender.setUrl(new URL("http://localhost:8085/")); WebServiceConnection connection = messageSender.createConnection(); try { - connection.sendAndReceive(context); - assertFalse("Response", context.hasResponse()); + TransportOutputStream tos = connection.getTransportOutputStream(); + tos.addHeader(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE); + FileCopyUtils.copy(REQUEST.getBytes("UTF-8"), tos); + assertNull("Response", connection.getTransportInputStream()); } finally { connection.close(); } } - private class ResponseServlet extends GenericServlet { + private static class MyServlet extends GenericServlet { - public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { - try { - StringResult requestResult = new StringResult(); - transformer.transform(new StreamSource(req.getInputStream()), requestResult); - receivedRequest = requestResult.toString(); - receivedHeader = ((HttpServletRequest) req).getHeader(REQUEST_HEADER_NAME); + private boolean response; - HttpServletResponse httpServletResponse = (HttpServletResponse) res; - httpServletResponse.setStatus(HttpServletResponse.SC_OK); - httpServletResponse.addHeader("Content-Type", "text/xml"); - httpServletResponse.addHeader(RESPONSE_HEADER_NAME, RESPONSE_HEADER_VALUE); - FileCopyUtils.copy(RESPONSE.getBytes("UTF-8"), res.getOutputStream()); - } - catch (TransformerException ex) { - throw new ServletException(ex); - } + private int responseStatus; + + private boolean validContentLength; + + public MyServlet(boolean response) { + this(response, HttpServletResponse.SC_OK, true); } - } - private class NoResponseServlet extends GenericServlet { + public MyServlet(boolean response, int responseStatus) { + this(response, responseStatus, true); + } + + public MyServlet(boolean response, int responseStatus, boolean validContentLength) { + this.response = response; + this.responseStatus = responseStatus; + this.validContentLength = validContentLength; + } public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { - try { - StringResult requestResult = new StringResult(); - transformer.transform(new StreamSource(req.getInputStream()), requestResult); - receivedRequest = requestResult.toString(); - receivedHeader = ((HttpServletRequest) req).getHeader(REQUEST_HEADER_NAME); + HttpServletRequest httpServletRequest = (HttpServletRequest) req; + HttpServletResponse httpServletResponse = (HttpServletResponse) res; + assertEquals("Invalid header value received on server side", REQUEST_HEADER_VALUE, + httpServletRequest.getHeader(REQUEST_HEADER_NAME)); + String receivedRequest = new String(FileCopyUtils.copyToByteArray(req.getInputStream()), "UTF-8"); + assertEquals("Invalid request received", REQUEST, receivedRequest); - HttpServletResponse httpServletResponse = (HttpServletResponse) res; - httpServletResponse.setStatus(HttpServletResponse.SC_NO_CONTENT); + httpServletResponse.setStatus(responseStatus); + if (!validContentLength) { + httpServletResponse.setContentLength(-1); } - catch (TransformerException ex) { - throw new ServletException(ex); + if (response) { + httpServletResponse.addHeader(RESPONSE_HEADER_NAME, RESPONSE_HEADER_VALUE); + httpServletResponse.setContentType("text/xml"); + FileCopyUtils.copy(RESPONSE.getBytes("UTF-8"), res.getOutputStream()); } } } diff --git a/core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportInputStreamTest.java b/core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java similarity index 54% rename from core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportInputStreamTest.java rename to core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java index bc6736ac..2d90c325 100644 --- a/core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportInputStreamTest.java +++ b/core/src/test/java/org/springframework/ws/transport/http/HttpServletConnectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2006 the original author or authors. + * 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. @@ -17,34 +17,41 @@ package org.springframework.ws.transport.http; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import junit.framework.TestCase; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.util.FileCopyUtils; +import org.springframework.ws.transport.TransportInputStream; -public class HttpServletTransportInputStreamTest extends TestCase { +public class HttpServletConnectionTest extends TestCase { - private HttpServletTransportInputStream tis; + private HttpServletConnection connection; private MockHttpServletRequest request; + private MockHttpServletResponse response; + protected void setUp() throws Exception { request = new MockHttpServletRequest(); - tis = new HttpServletTransportInputStream(request); + response = new MockHttpServletResponse(); + connection = new HttpServletConnection(request, response); } public void testReadInputStream() throws Exception { byte[] content = "content".getBytes("UTF-8"); request.setContent(content); - byte[] result = FileCopyUtils.copyToByteArray(tis); + byte[] result = FileCopyUtils.copyToByteArray(connection.getTransportInputStream()); assertTrue("Invalid contents", Arrays.equals(content, result)); } - public void testHeaders() throws Exception { + public void testGetHeaders() throws Exception { String headerName = "Header"; String headerValue = "Value"; request.addHeader(headerName, headerValue); + TransportInputStream tis = connection.getTransportInputStream(); Iterator iterator = tis.getHeaderNames(); assertTrue("No headers found", iterator.hasNext()); assertEquals("Invalid header", headerName, iterator.next()); @@ -53,4 +60,18 @@ public class HttpServletTransportInputStreamTest extends TestCase { assertEquals("Invalid header value", headerValue, iterator.next()); } + public void testWriteOutputStream() throws Exception { + byte[] content = "content".getBytes("UTF-8"); + FileCopyUtils.copy(content, connection.getTransportOutputStream()); + assertTrue("Invalid contents", Arrays.equals(content, response.getContentAsByteArray())); + } + + public void testAddHeaders() throws Exception { + String headerName = "Header"; + String headerValue = "Value"; + connection.getTransportOutputStream().addHeader(headerName, headerValue); + assertTrue("No header set", response.getHeaderNames().contains(headerName)); + assertEquals("Invalid header value set", Collections.singletonList(headerValue), + response.getHeaders(headerName)); + } } \ No newline at end of file diff --git a/core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportOutputStreamTest.java b/core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportOutputStreamTest.java deleted file mode 100644 index b9daf8cc..00000000 --- a/core/src/test/java/org/springframework/ws/transport/http/HttpServletTransportOutputStreamTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2006 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 java.util.Arrays; -import java.util.Collections; - -import junit.framework.TestCase; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.util.FileCopyUtils; - -public class HttpServletTransportOutputStreamTest extends TestCase { - - private HttpServletTransportOutputStream tos; - - private MockHttpServletResponse response; - - protected void setUp() throws Exception { - response = new MockHttpServletResponse(); - tos = new HttpServletTransportOutputStream(response); - } - - public void testWriteOutputStream() throws Exception { - byte[] content = "content".getBytes("UTF-8"); - FileCopyUtils.copy(content, tos); - assertTrue("Invalid contents", Arrays.equals(content, response.getContentAsByteArray())); - } - - public void testHeaders() throws Exception { - String headerName = "Header"; - String headerValue = "Value"; - tos.addHeader(headerName, headerValue); - assertTrue("No header set", response.getHeaderNames().contains(headerName)); - assertEquals("Invalid header value set", Collections.singletonList(headerValue), - response.getHeaders(headerName)); - } - -} \ No newline at end of file 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 4d7bb615..cf1a0bdb 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 @@ -16,7 +16,6 @@ package org.springframework.ws.transport.http; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import junit.framework.TestCase; @@ -53,10 +52,13 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { private WebServiceMessage requestMock; + private HttpServletConnection connection; + protected void setUp() throws Exception { adapter = new WebServiceMessageReceiverHandlerAdapter(); httpRequest = new MockHttpServletRequest(); httpResponse = new MockHttpServletResponse(); + connection = new HttpServletConnection(httpRequest, httpResponse); factoryControl = MockControl.createControl(WebServiceMessageFactory.class); factoryMock = (WebServiceMessageFactory) factoryControl.getMock(); adapter.setMessageFactory(factoryMock); @@ -73,13 +75,9 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { public void receive(MessageContext messageContext) throws Exception { } }; - try { - adapter.handle(httpRequest, httpResponse, endpoint); - fail("ServletException expected"); - } - catch (ServletException ex) { - // expected - } + adapter.handle(httpRequest, httpResponse, endpoint); + assertEquals("METHOD_NOT_ALLOWED expected", HttpServletResponse.SC_METHOD_NOT_ALLOWED, + httpResponse.getStatus()); verifyMockControls(); } @@ -88,7 +86,7 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { httpRequest.setContent(REQUEST.getBytes("UTF-8")); httpRequest.setContentType("text/xml; charset=\"utf-8\""); httpRequest.setCharacterEncoding("UTF-8"); - factoryMock.createWebServiceMessage(new HttpServletTransportInputStream(httpRequest)); + factoryMock.createWebServiceMessage(connection.getTransportInputStream()); factoryControl.setMatcher(MockControl.ALWAYS_MATCHER); factoryControl.setReturnValue(responseMock); @@ -101,7 +99,7 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { adapter.handle(httpRequest, httpResponse, endpoint); - assertEquals("Invalid status code on response", HttpServletResponse.SC_NO_CONTENT, httpResponse.getStatus()); + assertEquals("Invalid status code on response", HttpServletResponse.SC_ACCEPTED, httpResponse.getStatus()); assertEquals("Response written", 0, httpResponse.getContentAsString().length()); verifyMockControls(); } @@ -111,12 +109,12 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { httpRequest.setContent(REQUEST.getBytes("UTF-8")); httpRequest.setContentType("text/xml; charset=\"utf-8\""); httpRequest.setCharacterEncoding("UTF-8"); - factoryMock.createWebServiceMessage(new HttpServletTransportInputStream(httpRequest)); + factoryMock.createWebServiceMessage(connection.getTransportInputStream()); factoryControl.setMatcher(MockControl.ALWAYS_MATCHER); factoryControl.setReturnValue(requestMock); factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); messageControl.expectAndReturn(responseMock.hasFault(), false); - responseMock.writeTo(new HttpServletTransportOutputStream(httpResponse)); + responseMock.writeTo(connection.getTransportOutputStream()); messageControl.setMatcher(MockControl.ALWAYS_MATCHER); replayMockControls(); @@ -138,12 +136,12 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { httpRequest.setContent(REQUEST.getBytes("UTF-8")); httpRequest.setContentType("text/xml; charset=\"utf-8\""); httpRequest.setCharacterEncoding("UTF-8"); - factoryMock.createWebServiceMessage(new HttpServletTransportInputStream(httpRequest)); + factoryMock.createWebServiceMessage(connection.getTransportInputStream()); factoryControl.setMatcher(MockControl.ALWAYS_MATCHER); factoryControl.setReturnValue(requestMock); factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), responseMock); messageControl.expectAndReturn(responseMock.hasFault(), true); - responseMock.writeTo(new HttpServletTransportOutputStream(httpResponse)); + responseMock.writeTo(connection.getTransportOutputStream()); messageControl.setMatcher(MockControl.ALWAYS_MATCHER); replayMockControls(); @@ -166,7 +164,7 @@ public class WebServiceMessageReceiverHandlerAdapterTest extends TestCase { httpRequest.setContent(REQUEST.getBytes("UTF-8")); httpRequest.setContentType("text/xml; charset=\"utf-8\""); httpRequest.setCharacterEncoding("UTF-8"); - factoryMock.createWebServiceMessage(new HttpServletTransportInputStream(httpRequest)); + factoryMock.createWebServiceMessage(connection.getTransportInputStream()); factoryControl.setMatcher(MockControl.ALWAYS_MATCHER); factoryControl.setReturnValue(requestMock);