From 9121e6d9185697fc4dc4eddaae82bd7a578652d3 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Fri, 20 Mar 2009 18:19:17 +0000 Subject: [PATCH] Added HttpResponse abstraction to encapsulate response body and headers. --- .../http/AbstractHttpRequestExecutor.java | 44 +++++++++++++-- .../http/CommonsHttpRequestExecutor.java | 26 +++++++-- .../http/HttpOutboundEndpoint.java | 8 +-- .../integration/http/HttpRequestExecutor.java | 7 +-- .../integration/http/HttpResponse.java | 53 +++++++++++++++++++ .../http/SimpleHttpRequestExecutor.java | 11 +++- 6 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpResponse.java diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/AbstractHttpRequestExecutor.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/AbstractHttpRequestExecutor.java index bfeafbd758..0ab160c1bb 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/AbstractHttpRequestExecutor.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/AbstractHttpRequestExecutor.java @@ -18,6 +18,9 @@ package org.springframework.integration.http; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -70,11 +73,11 @@ public abstract class AbstractHttpRequestExecutor implements HttpRequestExecutor /** * Execute a request to send its content to its target URL. * @param request the request to execute - * @return the InputStream result + * @return the HttpResponse result * @throws IOException if thrown by I/O operations * @throws Exception in case of general errors */ - public final InputStream executeRequest(HttpRequest request) throws Exception { + public final HttpResponse executeRequest(HttpRequest request) throws Exception { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder("Sending HTTP request to [" + request.getTargetUrl() + "]"); Integer contentLength = request.getContentLength(); @@ -89,6 +92,41 @@ public abstract class AbstractHttpRequestExecutor implements HttpRequestExecutor /** * Subclasses must implement this method to execute the request. */ - protected abstract InputStream doExecuteRequest(HttpRequest request) throws Exception; + protected abstract HttpResponse doExecuteRequest(HttpRequest request) throws Exception; + + + /** + * Default implementation of {@link HttpResponse}. + */ + class DefaultHttpResponse implements HttpResponse { + + private final InputStream body; + + private final Map> headers; + + + public DefaultHttpResponse(InputStream body, Map> headers) { + this.body = body; + this.headers = (headers != null) ? headers : Collections.>emptyMap(); + } + + + public InputStream getBody() { + return this.body; + } + + public String getFirstHeader(String key) { + List values = this.headers.get(key); + return (values != null && values.size() > 0) ? values.get(0) : null; + } + + public Map> getHeaders() { + return this.headers; + } + + public List getHeaders(String key) { + return this.headers.get(key); + } + } } diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/CommonsHttpRequestExecutor.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/CommonsHttpRequestExecutor.java index 51341365bd..92a3caa795 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/CommonsHttpRequestExecutor.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/CommonsHttpRequestExecutor.java @@ -20,6 +20,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.zip.GZIPInputStream; import org.apache.commons.httpclient.Header; @@ -39,8 +44,6 @@ import org.apache.commons.httpclient.methods.TraceMethod; import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.integration.http.AbstractHttpRequestExecutor; -import org.springframework.integration.http.HttpRequest; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -115,7 +118,7 @@ public class CommonsHttpRequestExecutor extends AbstractHttpRequestExecutor { } @Override - protected InputStream doExecuteRequest(HttpRequest request) throws Exception { + protected HttpResponse doExecuteRequest(HttpRequest request) throws Exception { HttpMethod httpMethod = createHttpMethod(request); try { if (httpMethod instanceof EntityEnclosingMethod) { @@ -123,7 +126,7 @@ public class CommonsHttpRequestExecutor extends AbstractHttpRequestExecutor { } executeHttpMethod(getHttpClient(), httpMethod); validateResponse(httpMethod); - return readResponseBody(httpMethod); + return new DefaultHttpResponse(readResponseBody(httpMethod), getResponseHeaders(httpMethod)); } finally { // Need to explicitly release because it might be pooled. @@ -257,6 +260,21 @@ public class CommonsHttpRequestExecutor extends AbstractHttpRequestExecutor { return responseStream; } + private Map> getResponseHeaders(HttpMethod httpMethod) { + Map> headers = new HashMap>(); + for (Header header : httpMethod.getResponseHeaders()) { + String name = header.getName(); + String value = header.getValue(); + List values = headers.get(name); + if (values == null) { + values = new ArrayList(); + } + values.add(value); + headers.put(name, values); + } + return Collections.unmodifiableMap(headers); + } + /** * Determine whether the given response indicates a GZIP response. *

This implementation checks whether the HTTP "Content-Encoding" diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java index 08e84e03cb..4b1a110668 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java @@ -79,8 +79,8 @@ public class HttpOutboundEndpoint extends AbstractReplyProducingMessageHandler { protected void handleRequestMessage(Message requestMessage, ReplyMessageHolder replyMessageHolder) { try { HttpRequest request = this.requestMapper.fromMessage(requestMessage); - InputStream responseBody = this.requestExecutor.executeRequest(request); - Object replyPayload = this.createReplyPayloadFromResponse(responseBody); + HttpResponse response = this.requestExecutor.executeRequest(request); + Object replyPayload = this.createReplyPayloadFromResponse(response); replyMessageHolder.set(replyPayload); } catch (Exception e) { @@ -89,8 +89,10 @@ public class HttpOutboundEndpoint extends AbstractReplyProducingMessageHandler { } } - private Object createReplyPayloadFromResponse(InputStream responseBody) throws Exception { + private Object createReplyPayloadFromResponse(HttpResponse response) throws Exception { ByteArrayOutputStream responseByteStream = new ByteArrayOutputStream(); + InputStream responseBody = response.getBody(); + Assert.notNull(responseBody, "received null response body"); FileCopyUtils.copy(responseBody, responseByteStream); return responseByteStream.toByteArray(); } diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpRequestExecutor.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpRequestExecutor.java index 707b37879b..bef208c105 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpRequestExecutor.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpRequestExecutor.java @@ -16,11 +16,8 @@ package org.springframework.integration.http; -import java.io.InputStream; - /** - * Strategy that will allow a http request response exchange with a remote - * server. + * Strategy for executing an http request response exchange with a remote server. * * @author Iwein Fuld * @author Mark Fisher @@ -28,6 +25,6 @@ import java.io.InputStream; */ public interface HttpRequestExecutor { - InputStream executeRequest(HttpRequest request) throws Exception; + HttpResponse executeRequest(HttpRequest request) throws Exception; } diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpResponse.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpResponse.java new file mode 100644 index 0000000000..59a777989c --- /dev/null +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpResponse.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2009 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.integration.http; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +/** + * Representation of an HTTP response as returned by an implementation of + * the {@link HttpRequestExecutor} strategy. + * + * @author Mark Fisher + * @since 1.0.2 + */ +public interface HttpResponse { + + /** + * Return all response headers as a map. There may be multiple values per + * key in the map. Hence, the value type is a List of Strings. + */ + Map> getHeaders(); + + /** + * Return all header values for a given key, or null if it has no values. + */ + List getHeaders(String key); + + /** + * Return the first header value for a given key, or null if it has no values. + */ + String getFirstHeader(String key); + + /** + * Return the body of the response as an InputStream. + */ + InputStream getBody(); + +} diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/SimpleHttpRequestExecutor.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/SimpleHttpRequestExecutor.java index d5dc328936..5d60c3b1cf 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/SimpleHttpRequestExecutor.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/SimpleHttpRequestExecutor.java @@ -22,6 +22,8 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.List; +import java.util.Map; import java.util.zip.GZIPInputStream; import org.springframework.context.i18n.LocaleContext; @@ -42,12 +44,13 @@ import org.springframework.util.StringUtils; public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor { @Override - protected InputStream doExecuteRequest(HttpRequest request) throws Exception { + protected HttpResponse doExecuteRequest(HttpRequest request) throws Exception { HttpURLConnection connection = this.openConnection(request.getTargetUrl()); this.prepareConnection(connection, request); this.writeRequestBody(connection, request.getBody()); this.validateResponse(connection); - return this.readResponseBody(connection); + InputStream responseBody = this.readResponseBody(connection); + return new DefaultHttpResponse(responseBody, this.getResponseHeaders(connection)); } /** @@ -146,6 +149,10 @@ public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor { } } + private Map> getResponseHeaders(HttpURLConnection connection) { + return connection.getHeaderFields(); + } + /** * Determine whether the given response is a GZIP response. *