From 341a08f7ff24df162e9126560fd235b3e9b765fd Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 23 Jun 2010 20:19:59 +0000 Subject: [PATCH] INT-916, INT-939 replaced HttpOutboundEndpoint with the RestTemplate-based HttpRequestExecutingMessageHandler --- .../integration/http/ContentTypeResolver.java | 45 +++ .../http/DefaultOutboundRequestMapper.java | 363 +++--------------- .../http/HttpOutboundEndpoint.java | 149 ------- .../HttpRequestExecutingMessageHandler.java | 235 ++++++++++++ .../http/OutboundRequestMapper.java | 7 +- .../config/HttpOutboundGatewayParser.java | 30 +- .../config/spring-integration-http-2.0.xsd | 15 +- .../DefaultOutboundRequestMapperTests.java | 126 +++--- ...HttpOutboundGatewayParserTests-context.xml | 16 +- .../HttpOutboundGatewayParserTests.java | 80 ++-- 10 files changed, 495 insertions(+), 571 deletions(-) create mode 100644 spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java delete mode 100644 spring-integration-http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java create mode 100755 spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java b/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java new file mode 100644 index 0000000000..62040fdcce --- /dev/null +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2010 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 org.springframework.http.MediaType; + +/** + * Strategy for resolving the content type of a given object. The content type + * will be represented as an instance of the {@link MediaType} enum. + * + * @author Mark Fisher + * @since 2.0 + */ +public interface ContentTypeResolver { + + /** + * Resolves the content type of a given object. + * + * @param content the object whose content type should be resolved + */ + MediaType resolveContentType(Object content); + + /** + * Resolves the content type of a given String instance and charset name. + * + * @param content the String whose content type should be resolved + * @param charset charset name + */ + MediaType resolveContentType(String content, String charset); + +} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultOutboundRequestMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultOutboundRequestMapper.java index 6eba71d2be..6a5c3b2bf1 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultOutboundRequestMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultOutboundRequestMapper.java @@ -16,26 +16,16 @@ package org.springframework.integration.http; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLEncoder; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.Map; +import javax.xml.transform.Source; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.integration.core.Message; -import org.springframework.integration.message.MessageDeliveryException; import org.springframework.util.Assert; /** @@ -46,38 +36,13 @@ import org.springframework.util.Assert; */ public class DefaultOutboundRequestMapper implements OutboundRequestMapper { - private volatile URL defaultUrl; - private volatile boolean extractPayload = true; + private volatile ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver(); + private volatile String charset = "UTF-8"; - /** - * Create a DefaultOutboundRequestMapper with no default URL. - */ - public DefaultOutboundRequestMapper() { - } - - /** - * Create a DefaultOutboundRequestMapper with the given default URL. - */ - public DefaultOutboundRequestMapper(URL defaultUrl) { - this.defaultUrl = defaultUrl; - } - - - /** - * Specify the default URL to use when the outbound message does not - * contain a value for the {@link HttpHeaders#REQUEST_URL} header. - * This default is optional, but if no value is provided, and a Message - * does not contain the header, then a MessageDeliveryException will be - * thrown at runtime. - */ - public void setDefaultUrl(URL defaultUrl) { - this.defaultUrl = defaultUrl; - } - /** * Specify whether the outbound message's payload should be extracted * when preparing the request body. Otherwise the Message instance itself @@ -96,284 +61,80 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper { this.charset = charset; } - public HttpRequest fromMessage(Message message) throws Exception { + public HttpEntity fromMessage(Message message) throws Exception { Assert.notNull(message, "message must not be null"); - URL url = this.resolveUrl(message); - if (url == null) { - throw new MessageDeliveryException(message, "failed to determine a target URL for Message"); - } - Object requestMethodHeader = message.getHeaders().get(HttpHeaders.REQUEST_METHOD); - String requestMethod = (requestMethodHeader != null) ? - requestMethodHeader.toString().toUpperCase() : "POST"; - if (this.extractPayload) { - Object payload = message.getPayload(); - Assert.notNull(payload, "payload must not be null"); - return this.createRequestFromPayload(payload, url, requestMethod); - } - return this.createRequestFromMessage(message, url, requestMethod); + return (this.extractPayload) ? this.createHttpEntityWithPayloadAsBody(message) + : this.createHttpEntityWithMessageAsBody(message); } @SuppressWarnings("unchecked") - private HttpRequest createRequestFromPayload(Object payload, URL url, String requestMethod) throws Exception { - ByteArrayOutputStream requestBody = new ByteArrayOutputStream(); - String contentType = null; - if ("POST".equals(requestMethod) || "PUT".equals(requestMethod)) { - contentType = this.writeToRequestBody(payload, requestBody); + private HttpEntity createHttpEntityWithPayloadAsBody(Message requestMessage) { + if (requestMessage.getPayload() instanceof HttpEntity) { + return (HttpEntity) requestMessage.getPayload(); } - else { - Assert.isTrue(payload instanceof Map, - "Message payload must be a Map for a '" + requestMethod + "' request."); - Map parameterMap = this.createParameterMap((Map) payload); - Assert.notNull(parameterMap, "Payload must be a Map with String typed keys and " + - "String or String array typed values for a '" + requestMethod + "' request."); - url = this.addQueryParametersToUrl(url, parameterMap); - } - return new DefaultHttpRequest(url, requestMethod, requestBody, contentType); - } - - private HttpRequest createRequestFromMessage(Message message, URL url, String requestMethod) throws Exception { - Assert.isTrue("POST".equals(requestMethod) || "PUT".equals(requestMethod), - "POST or PUT request method is required when the 'extractPayload' value is false."); - ByteArrayOutputStream requestBody = new ByteArrayOutputStream(); - String contentType = this.writeToRequestBody(message, requestBody); - return new DefaultHttpRequest(url, requestMethod, requestBody, contentType); - } - - /** - * Creates a parameter map with String keys and String array values from - * the provided map if possible. If the provided map contains any keys that - * are not String typed, or any values that are not String or String array - * typed, then this method will return null. - */ - private Map createParameterMap(Map map) { - Map parameterMap = new HashMap(); - for (Object key : map.keySet()) { - if (!(key instanceof String)) { - return null; - } - String[] stringArrayValue = null; - Object value = map.get(key); + // TODO: provide more fine-grained control over header mapping + HttpHeaders httpHeaders = new HttpHeaders(); + for (String headerName : requestMessage.getHeaders().keySet()) { + Object value = requestMessage.getHeaders().get(headerName); if (value instanceof String) { - stringArrayValue = new String[] { (String) value }; + httpHeaders.add(headerName, (String) value); } - else if (value instanceof String[]) { - stringArrayValue = (String[]) value; + } + Object payload = requestMessage.getPayload(); + MediaType contentType = (payload instanceof String) ? this.contentTypeResolver.resolveContentType((String) payload, this.charset) + : this.contentTypeResolver.resolveContentType(payload); + httpHeaders.setContentType(contentType); + return new HttpEntity(requestMessage.getPayload(), httpHeaders); + } + + private HttpEntity createHttpEntityWithMessageAsBody(Message requestMessage) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(new MediaType("application", "x-java-serialized-object")); + return new HttpEntity(requestMessage, headers); + } + + + private static class DefaultContentTypeResolver implements ContentTypeResolver { + + @SuppressWarnings("unchecked") + public MediaType resolveContentType(Object content) { + MediaType contentType = null; + if (content instanceof byte[]) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + else if (content instanceof Source) { + contentType = MediaType.TEXT_XML; } else { - return null; - } - parameterMap.put((String) key, stringArrayValue); - } - return parameterMap; - } - - @SuppressWarnings("unchecked") - private String writeToRequestBody(Object object, ByteArrayOutputStream byteStream) throws Exception { - String contentType = null; - if (object instanceof byte[]) { - byteStream.write((byte[]) object); - contentType = "application/octet-stream"; - } - else if (object instanceof String) { - byteStream.write(((String) object).getBytes(this.charset)); - contentType = "text/plain; charset=" + this.charset; - } - else { - if (object instanceof Map && isFormData((Map) object)) { - byte[] data = this.formDataAsBytes((Map) object); - if (data != null) { - byteStream.write(data); - contentType = "application/x-www-form-urlencoded"; + if (content instanceof Map && isFormData((Map) content)) { + contentType = MediaType.APPLICATION_FORM_URLENCODED; + } + if (contentType == null && content instanceof Serializable) { + contentType = new MediaType("application", "x-java-serialized-object"); } } - if (contentType == null && object instanceof Serializable) { - byteStream.write(this.serializeObject((Serializable) object)); - contentType = "application/x-java-serialized-object"; + if (contentType == null) { + throw new IllegalArgumentException("payload must be a byte array, " + + "String, Map, Source, or Serializable object, received: " + content.getClass()); } + return contentType; } - if (contentType == null) { - throw new IllegalArgumentException("payload must be a byte array, " + - "String, Map, or Serializable object for a 'POST' or 'PUT' request"); - } - return contentType; - } - /** - * If all keys are Strings, we'll consider the Map to be form data. - */ - @SuppressWarnings("unchecked") - private boolean isFormData(Map map) { - for (Object key : map.keySet()) { - if (!(key instanceof String)) { - return false; - } + public MediaType resolveContentType(String content, String charset) { + return new MediaType("text", "plain", Charset.forName(charset)); } - return true; - } - @SuppressWarnings("unchecked") - private byte[] formDataAsBytes(Map form) throws UnsupportedEncodingException { - StringBuilder builder = new StringBuilder(); - Iterator nameIterator = form.keySet().iterator(); - while (nameIterator.hasNext()) { - Object next = nameIterator.next(); - Assert.isTrue(next instanceof String, "Form map keys must be Strings."); - String name = (String) next; - Object value = form.get(name); - if (value == null) { - builder.append(URLEncoder.encode(name, this.charset)); - } - else { - List values = null; - if (value instanceof String) { - values = Collections.singletonList((String) value); - } - else if (value instanceof String[]) { - values = Arrays.asList((String[]) value); - } - else { - if (!(value instanceof Iterable)) { - return null; - } - Iterator iterator = ((Iterable) value).iterator(); - values = new ArrayList(); - while (iterator.hasNext()) { - Object nextValue = iterator.next(); - if (!(nextValue instanceof String)) { - return null; - } - values.add((String) nextValue); - } - } - Iterator valueIterator = values.iterator(); - builder.append(URLEncoder.encode(name, this.charset)); - while (valueIterator.hasNext()) { - builder.append('=' + URLEncoder.encode(valueIterator.next(), this.charset)); - if (valueIterator.hasNext()) { - builder.append('&' + URLEncoder.encode(name, this.charset)); - } + /** + * If all keys are Strings, we'll consider the Map to be form data. + */ + private boolean isFormData(Map map) { + for (Object key : map.keySet()) { + if (!(key instanceof String)) { + return false; } } - if (nameIterator.hasNext()) { - builder.append('&'); - } + return true; } - return builder.toString().getBytes(this.charset); - } - - private byte[] serializeObject(Serializable object) throws IOException { - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - ObjectOutputStream objectStream = new ObjectOutputStream(byteStream); - objectStream.writeObject(object); - objectStream.flush(); - objectStream.close(); - return byteStream.toByteArray(); - } - - /** - * Resolve the request URL for the given Message. This implementation - * returns the value associated with the {@link HttpHeaders#REQUEST_URL} - * key if available in the Message's headers. Otherwise, it falls back to - * the default URL as provided to the constructor of this mapper instance. - * @throws MalformedURLException if an error occurs while constructing the URL - */ - private URL resolveUrl(Message message) throws MalformedURLException { - Object urlHeader = message.getHeaders().get(HttpHeaders.REQUEST_URL); - if (urlHeader == null) { - return this.defaultUrl; - } - if (urlHeader instanceof URL) { - return (URL) urlHeader; - } - if (urlHeader instanceof URI) { - return ((URI) urlHeader).toURL(); - } - if (urlHeader instanceof String) { - return new URL((String) urlHeader); - } - throw new IllegalArgumentException("Target URL in Message header must be a URL, URI, or String."); - } - - /** - * Constructs a query string by appending the parameter map values to the URL. - * @throws Exception if an error occurs encoding or constructing the URL - */ - private URL addQueryParametersToUrl(URL url, Map parameterMap) throws Exception { - if (parameterMap == null || parameterMap.size() == 0) { - return url; - } - String urlString = url.toExternalForm(); - String fragment = ""; - int fragmentStartIndex = urlString.indexOf('#'); - if (fragmentStartIndex != -1) { - fragment = urlString.substring(fragmentStartIndex); - urlString = urlString.substring(0, fragmentStartIndex); - } - StringBuilder sb = new StringBuilder(urlString); - if (urlString.indexOf('?') == -1) { - sb.append('?'); - } - for (Map.Entry entry : parameterMap.entrySet()) { - String[] values = entry.getValue(); - for (String value : values) { - char lastChar = sb.charAt(sb.length() -1); - if (lastChar != '?' && lastChar != '&') { - sb.append('&'); - } - sb.append(URLEncoder.encode(entry.getKey(), this.charset) + "="); - sb.append(URLEncoder.encode(value, this.charset)); - } - } - sb.append(fragment); - return new URL(sb.toString()); - } - - - /** - * Default implementation of {@link HttpRequest}. - */ - class DefaultHttpRequest implements HttpRequest { - - private final URL targetUrl; - - private final String requestMethod; - - private final String contentType; - - private volatile ByteArrayOutputStream requestBody; - - - DefaultHttpRequest( - URL targetUrl, String requestMethod, ByteArrayOutputStream requestBody, String contentType) - throws IOException { - Assert.notNull(targetUrl, "target url must not be null"); - this.targetUrl = targetUrl; - this.requestMethod = (requestMethod != null) ? requestMethod : "POST"; - this.requestBody = requestBody; - this.contentType = contentType; - } - - - public URL getTargetUrl() { - return this.targetUrl; - } - - public String getRequestMethod() { - return this.requestMethod; - } - - public String getContentType() { - return this.contentType; - } - - public Integer getContentLength() { - return (this.requestBody != null) ? this.requestBody.size() : null; - } - - public ByteArrayOutputStream getBody() { - return this.requestBody; - } - } } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java deleted file mode 100644 index f5f9c74b4e..0000000000 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpOutboundEndpoint.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectStreamException; -import java.net.URL; - -import org.springframework.integration.core.Message; -import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; -import org.springframework.integration.message.MessageHandlingException; -import org.springframework.util.Assert; -import org.springframework.util.FileCopyUtils; - -/** - * An outbound endpoint that maps a request Message to an {@link HttpRequest}, - * executes that request, and then maps the response to a reply Message. - * - * @author Mark Fisher - * @since 1.0.2 - */ -public class HttpOutboundEndpoint extends AbstractReplyProducingMessageHandler { - - private volatile OutboundRequestMapper requestMapper; - - private volatile HttpRequestExecutor requestExecutor = new SimpleHttpRequestExecutor(); - - - /** - * Create an HttpOutboundEndpoint with no default URL. - */ - public HttpOutboundEndpoint() { - this.requestMapper = new DefaultOutboundRequestMapper(); - } - - /** - * Create an HttpOutboundEndpoint that will send requests to the provided - * URL by default. If a Message contains a valid value for the - * {@link HttpHeaders#REQUEST_URL} header, that will take precedence. - * If a custom {@link OutboundRequestMapper} instance is registered - * through the {@link #setRequestMapper(OutboundRequestMapper)} method, - * this default URL will not be used. - */ - public HttpOutboundEndpoint(URL defaultUrl) { - this.requestMapper = new DefaultOutboundRequestMapper(defaultUrl); - } - - - /** - * Specify an {@link OutboundRequestMapper} implementation to map from - * Messages to outbound {@link HttpRequest} objects. The default - * implementation is {@link DefaultOutboundRequestMapper}. - */ - public void setRequestMapper(OutboundRequestMapper requestMapper) { - Assert.notNull(requestMapper, "requestMapper must not be null"); - this.requestMapper = requestMapper; - } - - /** - * Specify the {@link HttpRequestExecutor} to use for executing the - * {@link HttpRequest} instances at runtime. The default implementation - * is {@link SimpleHttpRequestExecutor}. - */ - public void setRequestExecutor(HttpRequestExecutor requestExecutor) { - Assert.notNull(requestExecutor, "requestExecutor must not be null"); - this.requestExecutor = requestExecutor; - } - - @Override - protected Object handleRequestMessage(Message requestMessage) { - try { - HttpRequest request = this.requestMapper.fromMessage(requestMessage); - HttpResponse response = this.requestExecutor.executeRequest(request); - Object reply = this.createReplyFromResponse(response); - return reply; - } - catch (Exception e) { - throw new MessageHandlingException(requestMessage, "failed to execute HTTP request", e); - } - } - - private Object createReplyFromResponse(HttpResponse response) throws Exception { - InputStream responseBody = response.getBody(); - Assert.notNull(responseBody, "received null response body"); - String contentType = response.getFirstHeader("Content-Type"); - if (contentType != null && contentType.startsWith("application/x-java-serialized-object")) { - // may be either a payload or a serialized Message instance - return this.deserializePayload(responseBody); - } - ByteArrayOutputStream responseByteStream = new ByteArrayOutputStream(); - FileCopyUtils.copy(responseBody, responseByteStream); - if (contentType != null && contentType.startsWith("text")) { - String charsetName = this.getCharsetName(response); - if (charsetName == null) { - charsetName = "ISO-8859-1"; - } - return responseByteStream.toString(charsetName); - } - return responseByteStream.toByteArray(); - } - - private String getCharsetName(HttpResponse httpResponse) { - String contentType = httpResponse.getFirstHeader("Content-Type"); - if (contentType != null) { - int beginIndex = contentType.indexOf("charset="); - if (beginIndex != -1) { - return contentType.substring(beginIndex + "charset=".length()).trim(); - } - } - return null; - } - - private Object deserializePayload(InputStream responseBody) throws IOException, ClassNotFoundException { - ObjectInputStream objectStream = null; - try { - objectStream = new ObjectInputStream(responseBody); - return objectStream.readObject(); - } - catch (ObjectStreamException e) { - throw new IllegalArgumentException("failed to deserialize response", e); - } - finally { - try { - objectStream.close(); - } - catch (Exception e) { - // ignore - } - } - } - -} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java new file mode 100755 index 0000000000..dccf209c07 --- /dev/null +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java @@ -0,0 +1,235 @@ +/* + * Copyright 2002-2010 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.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessagingException; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessageHandler; +import org.springframework.integration.message.MessageHandlingException; +import org.springframework.util.Assert; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestTemplate; + +/** + * A {@link MessageHandler} implementation that executes HTTP requests by delegating + * to a {@link RestTemplate} instance. + * + * @author Mark Fisher + * @since 2.0 + */ +public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMessageHandler { + + private final String defaultUri; + + private volatile HttpMethod defaultHttpMethod = HttpMethod.POST; + + private volatile OutboundRequestMapper requestMapper = new DefaultOutboundRequestMapper(); + + private volatile Class expectedResponseType = Object.class; + + private final RestTemplate restTemplate = new RestTemplate(); + + + /** + * Create an adapter that has no default URI. Any Message sent to this handler will be + * required to contain a valid value for the {@link HttpHeaders#REQUEST_URL} header. + */ + public HttpRequestExecutingMessageHandler() { + this((String) null); + } + + /** + * Create an HttpOutboundEndpoint that will send requests to the provided + * URI by default. If a Message contains a valid value for the + * {@link HttpHeaders#REQUEST_URL} header, that will take precedence. + */ + public HttpRequestExecutingMessageHandler(URI defaultUri) { + this(defaultUri.toString()); + } + + /** + * Create an HttpOutboundEndpoint that will send requests to the provided + * URI by default. If a Message contains a valid value for the + * {@link HttpHeaders#REQUEST_URL} header, that will take precedence. + */ + public HttpRequestExecutingMessageHandler(String defaultUri) { + this.restTemplate.getMessageConverters().add(0, new SerializingHttpMessageConverter()); + this.defaultUri = defaultUri; + } + + + /** + * Specify the default {@link HttpMethod}. This will provide a fallback in the case + * that a Message does not contain the HTTP method as a header. If this is not + * explicitly specified, then the default method will be POST. + */ + public void setDefaultHttpMethod(HttpMethod defaultHttpMethod) { + this.defaultHttpMethod = defaultHttpMethod; + } + + /** + * Specify the expected response type for the REST request. + */ + public void setExpectedResponseType(Class expectedResponseType) { + this.expectedResponseType = (expectedResponseType != null) ? expectedResponseType : byte[].class; + } + + /** + * Set the {@link ResponseErrorHandler} for the underlying {@link RestTemplate}. + * @see RestTemplate#setErrorHandler(ResponseErrorHandler) + */ + public void setErrorHandler(ResponseErrorHandler errorHandler) { + this.restTemplate.setErrorHandler(errorHandler); + } + + /** + * Set a list of {@link HttpMessageConverter}s to be used by the underlying {@link RestTemplate}. + * Converters configured via this method will override the default converters. + * @see RestTemplate#setMessageConverters(java.util.List) + */ + public void setMessageConverters(List> messageConverters) { + this.restTemplate.setMessageConverters(messageConverters); + } + + /** + * Set the {@link ClientHttpRequestFactory} for the underlying {@link RestTemplate}. + * @see RestTemplate#setRequestFactory(ClientHttpRequestFactory) + */ + public void setRequestFactory(ClientHttpRequestFactory requestFactory) { + this.restTemplate.setRequestFactory(requestFactory); + } + + /** + * Specify the {@link OutboundRequestMapper} implementation to use for mapping a + * {@link Message} into an {@link HttpEntity} when executing an HTTP request. + *

+ * If not provided explicitly, the default implementation is {@link DefaultOutboundRequestMapper}. + */ + public void setRequestMapper(OutboundRequestMapper requestMapper) { + Assert.notNull(requestMapper, "requestMapper must not be null"); + this.requestMapper = requestMapper; + } + + @Override + protected Object handleRequestMessage(Message requestMessage) { + String uri = null; + try { + uri = this.resolveUri(requestMessage); + HttpMethod httpMethod = this.resolveHttpMethod(requestMessage); + // TODO: allow a boolean flag for treating Map as queryParams vs. uriVariables? + Map uriVariables = this.determineUriVariables(requestMessage); + HttpEntity httpRequest = this.requestMapper.fromMessage(requestMessage); + if (!isWritableRequestMethod(httpMethod) && httpRequest.getBody() != null) { + httpRequest = new HttpEntity(null, httpRequest.getHeaders()); + } + HttpEntity httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, this.expectedResponseType, uriVariables); + Object responseBody = httpResponse.getBody(); + MessageBuilder replyBuilder = (responseBody instanceof Message) ? + MessageBuilder.fromMessage((Message) responseBody) : MessageBuilder.withPayload(responseBody); + return replyBuilder.copyHeaders(httpResponse.getHeaders().toSingleValueMap()).build(); + } + catch (MessagingException e) { + throw e; + } + catch (Exception e) { + throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI [" + uri + "]", e); + } + } + + private boolean isWritableRequestMethod(HttpMethod httpMethod) { + switch (httpMethod) { + case POST: case PUT: return true; + default: return false; + } + } + + /** + * Resolve the request URL for the given Message. This implementation + * returns the value associated with the {@link HttpHeaders#REQUEST_URL} + * key if available in the Message's headers. Otherwise, it falls back to + * the default URI as provided to the constructor of this handler instance. + * @throws MalformedURLException if an error occurs while constructing the URL + */ + private String resolveUri(Message message) throws MalformedURLException { + Object urlHeader = message.getHeaders().get(HttpHeaders.REQUEST_URL); + if (urlHeader == null) { + Assert.notNull(this.defaultUri, + "No request URL header available in request Message, and no default has been provided."); + return this.defaultUri; + } + if (urlHeader instanceof URL) { + return ((URL) urlHeader).toString(); + } + if (urlHeader instanceof URI) { + return ((URI) urlHeader).toString(); + } + if (urlHeader instanceof String) { + return (String) urlHeader; + } + throw new IllegalArgumentException("Target URL in Message header must be a URL, URI, or String."); + } + + private HttpMethod resolveHttpMethod(Message requestMessage) { + HttpMethod httpMethod = null; + Object methodFromMessage = requestMessage.getHeaders().get(HttpHeaders.REQUEST_METHOD); + if (methodFromMessage instanceof HttpMethod) { + httpMethod = (HttpMethod) methodFromMessage; + } + else if (methodFromMessage instanceof String) { + httpMethod = HttpMethod.valueOf((String) methodFromMessage); + } + else if (methodFromMessage != null) { + throw new IllegalArgumentException("expected an HttpMethod enum instance or String for " + + "the REQUEST_METHOD header, but received type: " + methodFromMessage.getClass()); + } + if (httpMethod == null) { + httpMethod = this.defaultHttpMethod; + } + return httpMethod; + } + + private Map determineUriVariables(Message requestMessage) { + Map uriVariables = new HashMap(); + if (requestMessage.getPayload() instanceof Map) { + Map payloadMap = (Map) requestMessage.getPayload(); + for (Object key : payloadMap.keySet()) { + if (key instanceof String) { + System.out.println("adding value for key: " + key); + uriVariables.put((String) key, payloadMap.get(key).toString()); + } + else if (logger.isDebugEnabled()) { + logger.debug("ignoring Map value for non-String key: " + key); + } + } + } + return uriVariables; + } + +} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/OutboundRequestMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/OutboundRequestMapper.java index 9a88a5b4ff..754817a9bd 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/OutboundRequestMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/OutboundRequestMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,15 @@ package org.springframework.integration.http; +import org.springframework.http.HttpEntity; import org.springframework.integration.message.OutboundMessageMapper; /** - * Strategy for mapping to an {@link HttpRequest} from a message. + * Strategy for mapping to an {@link HttpEntity} from a message. * * @author Mark Fisher * @since 1.0.2 */ -public interface OutboundRequestMapper extends OutboundMessageMapper { +public interface OutboundRequestMapper extends OutboundMessageMapper> { } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java index 452f63d03b..e558b553f6 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -19,7 +19,6 @@ package org.springframework.integration.http.config; import org.w3c.dom.Element; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; @@ -42,44 +41,39 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + PACKAGE_PATH + ".HttpRequestExecutingMessageHandler"); String defaultUrl = element.getAttribute("default-url"); + if (StringUtils.hasText(defaultUrl)) { + builder.addConstructorArgValue(defaultUrl); + } String charset = element.getAttribute("charset"); String extractPayload = element.getAttribute("extract-request-payload"); String requestMapperRef = element.getAttribute("request-mapper"); if (StringUtils.hasText(requestMapperRef)) { - if (StringUtils.hasText(defaultUrl)) { - this.requestMapperConflictError("default-url", parserContext, element); - return null; - } - else if (StringUtils.hasText(charset)) { + if (StringUtils.hasText(charset)) { this.requestMapperConflictError("charset", parserContext, element); return null; } - else if (StringUtils.hasText(extractPayload)) { + if (StringUtils.hasText(extractPayload)) { this.requestMapperConflictError("extract-request-payload", parserContext, element); return null; } + builder.addPropertyReference("requestMapper", requestMapperRef); } - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_PATH + ".HttpOutboundEndpoint"); - if (!StringUtils.hasText(requestMapperRef)) { + else { BeanDefinitionBuilder mapperBuilder = BeanDefinitionBuilder.genericBeanDefinition( PACKAGE_PATH + ".DefaultOutboundRequestMapper"); - if (StringUtils.hasText(defaultUrl)) { - mapperBuilder.addConstructorArgValue(defaultUrl); - } if (StringUtils.hasText(charset)) { mapperBuilder.addPropertyValue("charset", charset); } if (StringUtils.hasText(extractPayload)) { mapperBuilder.addPropertyValue("extractPayload", extractPayload); } - requestMapperRef = BeanDefinitionReaderUtils.registerWithGeneratedName( - mapperBuilder.getBeanDefinition(), parserContext.getRegistry()); + builder.addPropertyValue("requestMapper", mapperBuilder.getBeanDefinition()); } - builder.addPropertyReference("requestMapper", requestMapperRef); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout", "sendTimeout"); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-executor"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-factory"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel"); return builder; } diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.0.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.0.xsd index c2ad1310bf..7e2e7ba5d9 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.0.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.0.xsd @@ -105,7 +105,13 @@ - + + + + URL to be used as a fallback for any request Message does not contain the request URL Message header. + + + @@ -117,11 +123,11 @@ - + - + @@ -129,8 +135,7 @@ diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java index f852442fcc..c7f32239ae 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java @@ -17,11 +17,10 @@ package org.springframework.integration.http; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; import java.io.Serializable; -import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -30,6 +29,8 @@ import java.util.Map; import org.junit.Test; +import org.springframework.http.HttpEntity; +import org.springframework.http.MediaType; import org.springframework.integration.core.Message; import org.springframework.integration.message.MessageBuilder; @@ -40,38 +41,61 @@ public class DefaultOutboundRequestMapperTests { @Test public void simpleStringValueFormData() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); + DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(); Map form = new LinkedHashMap(); form.put("a", "1"); form.put("b", "2"); form.put("c", "3"); Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - String bodyText = request.getBody().toString("UTF-8"); - assertEquals("a=1&b=2&c=3", bodyText); - assertEquals("application/x-www-form-urlencoded", request.getContentType()); + HttpEntity request = mapper.fromMessage(message); + Object body = request.getBody(); + assertTrue(body instanceof Map); + Map map = (Map ) body; + assertEquals("1", map.get("a")); + assertEquals("2", map.get("b")); + assertEquals("3", map.get("c")); + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @Test @SuppressWarnings("unchecked") public void stringArrayValueFormData() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); + DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(); Map form = new LinkedHashMap(); form.put("a", new String[] { "1", "2", "3" }); form.put("b", "4"); form.put("c", new String[] { "5" }); form.put("d", "6"); Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - String bodyText = request.getBody().toString("UTF-8"); - assertEquals("a=1&a=2&a=3&b=4&c=5&d=6", bodyText); - assertEquals("application/x-www-form-urlencoded", request.getContentType()); + HttpEntity request = mapper.fromMessage(message); + Object body = request.getBody(); + assertTrue(body instanceof Map); + Map map = (Map ) body; + Object entryA = map.get("a"); + assertEquals(String[].class, entryA.getClass()); + String[] resultA = (String[]) entryA; + assertEquals(3, resultA.length); + assertEquals("1", resultA[0]); + assertEquals("2", resultA[1]); + assertEquals("3", resultA[2]); + Object entryB = map.get("b"); + assertEquals(String.class, entryB.getClass()); + assertEquals("4", entryB); + Object entryC = map.get("c"); + assertEquals(String[].class, entryC.getClass()); + String[] resultC = (String[]) entryC; + assertEquals(1, resultC.length); + assertEquals("5", resultC[0]); + Object entryD = map.get("d"); + assertEquals(String.class, entryD.getClass()); + assertEquals("6", entryD); + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @Test @SuppressWarnings("unchecked") - public void stringListValueFormData() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); + public void listValueFormData() throws Exception { + DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(); Map form = new LinkedHashMap(); List listA = new ArrayList(); listA.add("1"); @@ -80,58 +104,62 @@ public class DefaultOutboundRequestMapperTests { form.put("b", Collections.EMPTY_LIST); form.put("c", Collections.singletonList("3")); Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - String bodyText = request.getBody().toString("UTF-8"); - assertEquals("a=1&a=2&b&c=3", bodyText); - assertEquals("application/x-www-form-urlencoded", request.getContentType()); + HttpEntity request = mapper.fromMessage(message); + Object body = request.getBody(); + assertTrue(body instanceof Map); + Map map = (Map ) body; + Object entryA = map.get("a"); + assertTrue(entryA instanceof List); + List resultA = (List) entryA; + assertEquals(2, resultA.size()); + assertEquals("1", resultA.get(0)); + assertEquals("2", resultA.get(1)); + Object entryB = map.get("b"); + assertTrue(entryB instanceof List); + List resultB = (List) entryB; + assertEquals(0, resultB.size()); + Object entryC = map.get("c"); + assertTrue(entryC instanceof List); + List resultC = (List) entryC; + assertEquals(1, resultC.size()); + assertEquals("3", resultC.get(0)); + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @Test @SuppressWarnings("unchecked") public void nameOnlyWithNullValues() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); + DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(); Map form = new LinkedHashMap(); form.put("a", null); form.put("b", "foo"); form.put("c", null); Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - String bodyText = request.getBody().toString("UTF-8"); - assertEquals("a&b=foo&c", bodyText); - assertEquals("application/x-www-form-urlencoded", request.getContentType()); + HttpEntity request = mapper.fromMessage(message); + Object body = request.getBody(); + assertTrue(body instanceof Map); + Map map = (Map) body; + assertTrue(map.containsKey("a")); + assertNull(map.get("a")); + Object entryB = map.get("b"); + assertEquals("foo", entryB); + assertTrue(map.containsKey("c")); + assertNull(map.get("c")); + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @Test - public void encodedFormData() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); - Map form = new LinkedHashMap(); - form.put("a", "1 + 2 + 3"); - form.put("b", "4+5"); - form.put("c", "97%"); - Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - String bodyText = request.getBody().toString("UTF-8"); - assertEquals("a=1+%2B+2+%2B+3&b=4%2B5&c=97%25", bodyText); - assertEquals("application/x-www-form-urlencoded", request.getContentType()); - } - - @Test - @SuppressWarnings("unchecked") public void nonFormDataInMap() throws Exception { - DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org")); + DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(); Map form = new LinkedHashMap(); form.put("A", new TestBean()); form.put("B", new TestBean()); Message message = MessageBuilder.withPayload(form).build(); - HttpRequest request = mapper.fromMessage(message); - byte[] body = request.getBody().toByteArray(); - ByteArrayInputStream byteStream = new ByteArrayInputStream(body); - Object result = new ObjectInputStream(byteStream).readObject(); - assertEquals(LinkedHashMap.class, result.getClass()); - Map resultMap = (Map) result; - assertEquals(2, resultMap.size()); - assertEquals(TestBean.class, resultMap.get("A").getClass()); - assertEquals(TestBean.class, resultMap.get("B").getClass()); + HttpEntity request = mapper.fromMessage(message); + Map map = (Map) request.getBody(); + assertEquals(2, map.size()); + assertEquals(TestBean.class, map.get("A").getClass()); + assertEquals(TestBean.class, map.get("B").getClass()); } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml index bc40e0e9e3..242efacead 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/http - http://www.springframework.org/schema/integration/http/spring-integration-http.xsd"> + http://www.springframework.org/schema/integration/http/spring-integration-http.xsd"> @@ -20,8 +20,9 @@ - - + - + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java index a5e2b97040..130faf3220 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -22,8 +22,6 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import java.net.URL; - import org.junit.Test; import org.junit.runner.RunWith; @@ -31,13 +29,13 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.core.MessageChannel; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.http.DefaultOutboundRequestMapper; -import org.springframework.integration.http.HttpOutboundEndpoint; -import org.springframework.integration.http.HttpRequestExecutor; +import org.springframework.integration.http.HttpRequestExecutingMessageHandler; import org.springframework.integration.http.OutboundRequestMapper; -import org.springframework.integration.http.SimpleHttpRequestExecutor; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -63,22 +61,24 @@ public class HttpOutboundGatewayParserTests { @Test public void minimalConfig() { - HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) new DirectFieldAccessor( + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor( this.minimalConfigEndpoint).getPropertyValue("handler"); MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor( this.minimalConfigEndpoint).getPropertyValue("inputChannel"); assertEquals(this.applicationContext.getBean("requests"), requestChannel); - DirectFieldAccessor accessor = new DirectFieldAccessor(gateway); - Object replyChannel = accessor.getPropertyValue("outputChannel"); + DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); + Object replyChannel = handlerAccessor.getPropertyValue("outputChannel"); assertNull(replyChannel); - OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper"); - HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor"); + OutboundRequestMapper mapper = (OutboundRequestMapper) handlerAccessor.getPropertyValue("requestMapper"); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) + templateAccessor.getPropertyValue("requestFactory"); assertTrue(mapper instanceof DefaultOutboundRequestMapper); - assertTrue(executor instanceof SimpleHttpRequestExecutor); - Object mapperBean = this.applicationContext.getBean("mapper"); + assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory); + Object mapperBean = this.applicationContext.getBean("testMapper"); assertNotSame(mapperBean, mapper); DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper); - assertNull(mapperAccessor.getPropertyValue("defaultUrl")); + assertNull(handlerAccessor.getPropertyValue("defaultUri")); assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset")); assertEquals(true, mapperAccessor.getPropertyValue("extractPayload")); } @@ -86,59 +86,63 @@ public class HttpOutboundGatewayParserTests { @Test public void fullConfigWithMapper() throws Exception { DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.fullConfigWithMapperEndpoint); - HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor( this.fullConfigWithMapperEndpoint).getPropertyValue("inputChannel"); assertEquals(this.applicationContext.getBean("requests"), requestChannel); - DirectFieldAccessor accessor = new DirectFieldAccessor(gateway); - assertEquals(77, accessor.getPropertyValue("order")); + DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); + assertEquals(77, handlerAccessor.getPropertyValue("order")); assertEquals(Boolean.FALSE, endpointAccessor.getPropertyValue("autoStartup")); - Object replyChannel = accessor.getPropertyValue("outputChannel"); + Object replyChannel = handlerAccessor.getPropertyValue("outputChannel"); assertNotNull(replyChannel); assertEquals(this.applicationContext.getBean("replies"), replyChannel); - OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper"); - HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor"); + OutboundRequestMapper mapper = (OutboundRequestMapper) handlerAccessor.getPropertyValue("requestMapper"); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) + templateAccessor.getPropertyValue("requestFactory"); assertTrue(mapper instanceof DefaultOutboundRequestMapper); - assertTrue(executor instanceof SimpleHttpRequestExecutor); - Object mapperBean = this.applicationContext.getBean("mapper"); + assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory); + Object mapperBean = this.applicationContext.getBean("testMapper"); assertEquals(mapperBean, mapper); DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper); - assertEquals(new URL("http://localhost/test"), mapperAccessor.getPropertyValue("defaultUrl")); + assertEquals("http://localhost/test1", handlerAccessor.getPropertyValue("defaultUri")); assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset")); assertEquals(false, mapperAccessor.getPropertyValue("extractPayload")); - Object executorBean = this.applicationContext.getBean("executor"); - assertEquals(executorBean, executor); + Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory"); + assertEquals(requestFactoryBean, requestFactory); Object sendTimeout = new DirectFieldAccessor( - accessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout"); + handlerAccessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout"); assertEquals(new Long("1234"), sendTimeout); } @Test public void fullConfigWithoutMapper() throws Exception { - HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) new DirectFieldAccessor( + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor( this.fullConfigWithoutMapperEndpoint).getPropertyValue("handler"); MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor( this.fullConfigWithoutMapperEndpoint).getPropertyValue("inputChannel"); assertEquals(this.applicationContext.getBean("requests"), requestChannel); - DirectFieldAccessor accessor = new DirectFieldAccessor(gateway); - Object replyChannel = accessor.getPropertyValue("outputChannel"); + DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); + Object replyChannel = handlerAccessor.getPropertyValue("outputChannel"); assertNotNull(replyChannel); assertEquals(this.applicationContext.getBean("replies"), replyChannel); - OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper"); - HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor"); + OutboundRequestMapper mapper = (OutboundRequestMapper) handlerAccessor.getPropertyValue("requestMapper"); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) + templateAccessor.getPropertyValue("requestFactory"); assertTrue(mapper instanceof DefaultOutboundRequestMapper); - assertTrue(executor instanceof SimpleHttpRequestExecutor); - Object mapperBean = this.applicationContext.getBean("mapper"); + assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory); + Object mapperBean = this.applicationContext.getBean("testMapper"); assertNotSame(mapperBean, mapper); DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper); - assertEquals(new URL("http://localhost/test"), mapperAccessor.getPropertyValue("defaultUrl")); + assertEquals("http://localhost/test2", handlerAccessor.getPropertyValue("defaultUri")); assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset")); assertEquals(false, mapperAccessor.getPropertyValue("extractPayload")); - Object executorBean = this.applicationContext.getBean("executor"); - assertEquals(executorBean, executor); + Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory"); + assertEquals(requestFactoryBean, requestFactory); Object sendTimeout = new DirectFieldAccessor( - accessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout"); + handlerAccessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout"); assertEquals(new Long("1234"), sendTimeout); - } + } }