INT-916, INT-939 replaced HttpOutboundEndpoint with the RestTemplate-based HttpRequestExecutingMessageHandler
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<String, String[]> 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 <code>null</code>.
|
||||
*/
|
||||
private Map<String, String[]> createParameterMap(Map<?,?> map) {
|
||||
Map<String, String[]> parameterMap = new HashMap<String, String[]>();
|
||||
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<Object> createHttpEntityWithMessageAsBody(Message<?> requestMessage) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(new MediaType("application", "x-java-serialized-object"));
|
||||
return new HttpEntity<Object>(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<String> 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<String>();
|
||||
while (iterator.hasNext()) {
|
||||
Object nextValue = iterator.next();
|
||||
if (!(nextValue instanceof String)) {
|
||||
return null;
|
||||
}
|
||||
values.add((String) nextValue);
|
||||
}
|
||||
}
|
||||
Iterator<String> 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<String, String[]> 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<String, String[]> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<HttpMessageConverter<?>> 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.
|
||||
* <p>
|
||||
* 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<String, ?> uriVariables = this.determineUriVariables(requestMessage);
|
||||
HttpEntity<?> httpRequest = this.requestMapper.fromMessage(requestMessage);
|
||||
if (!isWritableRequestMethod(httpMethod) && httpRequest.getBody() != null) {
|
||||
httpRequest = new HttpEntity<Object>(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<String, ?> determineUriVariables(Message<?> requestMessage) {
|
||||
Map<String, Object> uriVariables = new HashMap<String, Object>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<HttpRequest> {
|
||||
public interface OutboundRequestMapper extends OutboundMessageMapper<HttpEntity<?>> {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,13 @@
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="gatewayType">
|
||||
<xsd:attribute name="default-url" type="xsd:string"/>
|
||||
<xsd:attribute name="default-url" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
URL to be used as a fallback for any request Message does not contain the request URL Message header.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="extract-request-payload" type="xsd:string"/>
|
||||
<xsd:attribute name="charset" type="xsd:string"/>
|
||||
<xsd:attribute name="request-mapper" type="xsd:string">
|
||||
@@ -117,11 +123,11 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-executor" type="xsd:string">
|
||||
<xsd:attribute name="request-factory" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.http.HttpRequestExecutor"/>
|
||||
<tool:expected-type type="org.springframework.http.client.ClientHttpRequestFactory"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
@@ -129,8 +135,7 @@
|
||||
<xsd:attribute name="order" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies the order for invocation when this gateway is connected as a
|
||||
subscriber to a SubscribableChannel.
|
||||
Specifies the order for invocation when this gateway is connected as a subscriber to a SubscribableChannel.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
@@ -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<String, String> form = new LinkedHashMap<String, String>();
|
||||
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<String> listA = new ArrayList<String>();
|
||||
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<String, String> form = new LinkedHashMap<String, String>();
|
||||
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<String, TestBean> form = new LinkedHashMap<String, TestBean>();
|
||||
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<String, TestBean> resultMap = (Map<String, TestBean>) 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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
|
||||
<outbound-gateway id="fullConfigWithMapper"
|
||||
request-channel="requests"
|
||||
request-mapper="mapper"
|
||||
request-executor="executor"
|
||||
default-url="http://localhost/test1"
|
||||
request-mapper="testMapper"
|
||||
request-factory="testRequestFactory"
|
||||
request-timeout="1234"
|
||||
reply-channel="replies"
|
||||
order="77"
|
||||
@@ -29,19 +30,18 @@
|
||||
|
||||
<outbound-gateway id="fullConfigWithoutMapper"
|
||||
request-channel="requests"
|
||||
default-url="http://localhost/test"
|
||||
default-url="http://localhost/test2"
|
||||
extract-request-payload="false"
|
||||
charset="UTF-8"
|
||||
request-executor="executor"
|
||||
request-factory="testRequestFactory"
|
||||
request-timeout="1234"
|
||||
reply-channel="replies"/>
|
||||
|
||||
<beans:bean id="mapper" class="org.springframework.integration.http.DefaultOutboundRequestMapper">
|
||||
<beans:property name="defaultUrl" value="http://localhost/test"/>
|
||||
<beans:bean id="testMapper" class="org.springframework.integration.http.DefaultOutboundRequestMapper">
|
||||
<beans:property name="charset" value="UTF-8"/>
|
||||
<beans:property name="extractPayload" value="false"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="executor" class="org.springframework.integration.http.SimpleHttpRequestExecutor"/>
|
||||
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user