Map typed payloads with String keys and values now map to GET requests by default.
This commit is contained in:
@@ -21,7 +21,10 @@ import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -60,7 +63,16 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
|
||||
Assert.notNull(payload, "payload must not be null");
|
||||
String contentType = null;
|
||||
byte[] bytes = null;
|
||||
if (payload instanceof byte[]) {
|
||||
Map<String, String[]> parameterMap = null;
|
||||
if (payload instanceof Map) {
|
||||
parameterMap = this.createParameterMap((Map<?,?>) payload);
|
||||
if (parameterMap == null) {
|
||||
Assert.isInstanceOf(Serializable.class, payload);
|
||||
bytes = this.serializePayload((Serializable) payload);
|
||||
contentType = "application/x-java-serialized-object";
|
||||
}
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
bytes = (byte[]) payload;
|
||||
}
|
||||
else if (payload instanceof String) {
|
||||
@@ -68,23 +80,59 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
|
||||
contentType = "text/plain";
|
||||
}
|
||||
else if (payload instanceof Serializable) {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
|
||||
objectStream.writeObject(payload);
|
||||
objectStream.flush();
|
||||
objectStream.close();
|
||||
bytes = byteStream.toByteArray();
|
||||
bytes = this.serializePayload((Serializable) payload);
|
||||
contentType = "application/x-java-serialized-object";
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"payload must be a byte array, String, or Serializable object");
|
||||
"payload must be a byte array, String, Serializable object, or a " +
|
||||
"Map with String typed keys and String or String array typed values.");
|
||||
}
|
||||
URL url = this.resolveUrl(message);
|
||||
String method = "POST"; // TODO: support GET for Map payload
|
||||
String method = (parameterMap != null) ? "GET" : "POST";
|
||||
if (method.equals("GET")) {
|
||||
url = this.addQueryParameters(url, parameterMap);
|
||||
}
|
||||
return new DefaultHttpRequest(url, method, bytes, 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);
|
||||
if (value instanceof String) {
|
||||
stringArrayValue = new String[] { (String) value };
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
stringArrayValue = (String[]) value;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
parameterMap.put((String) key, stringArrayValue);
|
||||
}
|
||||
return parameterMap;
|
||||
}
|
||||
|
||||
private byte[] serializePayload(Serializable payload) throws IOException {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
|
||||
objectStream.writeObject(payload);
|
||||
objectStream.flush();
|
||||
objectStream.close();
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the request URL for the given Message. This implementation
|
||||
* simply returns the default URL as provided to the constructor.
|
||||
@@ -93,6 +141,36 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
|
||||
return this.defaultUrl;
|
||||
}
|
||||
|
||||
private URL addQueryParameters(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 = urlString.charAt(urlString.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());
|
||||
}
|
||||
|
||||
|
||||
class DefaultHttpRequest implements HttpRequest {
|
||||
|
||||
@@ -100,21 +178,23 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
|
||||
|
||||
private final String requestMethod;
|
||||
|
||||
private final ByteArrayOutputStream requestBody;
|
||||
|
||||
private final String contentType;
|
||||
|
||||
private volatile ByteArrayOutputStream requestBody;
|
||||
|
||||
|
||||
DefaultHttpRequest(URL targetUrl, String requestMethod, byte[] content, String contentType) throws IOException {
|
||||
Assert.notNull(targetUrl, "target url must not be null");
|
||||
this.targetUrl = targetUrl;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(content);
|
||||
this.requestBody = baos;
|
||||
if (content != null && content.length > 0) {
|
||||
this.requestBody = new ByteArrayOutputStream();
|
||||
this.requestBody.write(content);
|
||||
}
|
||||
this.contentType = contentType;
|
||||
this.requestMethod = (requestMethod != null) ? requestMethod : "POST";
|
||||
}
|
||||
|
||||
|
||||
public URL getTargetUrl() {
|
||||
return this.targetUrl;
|
||||
}
|
||||
@@ -123,17 +203,18 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
|
||||
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;
|
||||
}
|
||||
|
||||
public Integer getContentLength() {
|
||||
return this.requestBody.size();
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ public interface HttpRequest {
|
||||
Integer getContentLength();
|
||||
|
||||
/**
|
||||
* Return the request body as a {@link ByteArrayOutputStream}.
|
||||
* Return the request body as a {@link ByteArrayOutputStream},
|
||||
* or <code>null</code> if this request has no body content.
|
||||
*/
|
||||
ByteArrayOutputStream getBody();
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -56,11 +57,11 @@ public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor {
|
||||
* @see java.net.URL#openConnection()
|
||||
*/
|
||||
private HttpURLConnection openConnection(URL url) throws IOException {
|
||||
URLConnection con = url.openConnection();
|
||||
if (!(con instanceof HttpURLConnection)) {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (!(connection instanceof HttpURLConnection)) {
|
||||
throw new IOException("target URL [" + url + "] is not an HTTP URL");
|
||||
}
|
||||
return (HttpURLConnection) con;
|
||||
return (HttpURLConnection) connection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,42 +69,55 @@ public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor {
|
||||
* <p>
|
||||
* The request method (e.g. "POST), "Content-Type" header, and content
|
||||
* length will be determined from the provided {@link HttpRequest}.
|
||||
* @param connection HttpURLConnection the connection to prepare
|
||||
* @param request HttpRequest for which the connection should be prepared
|
||||
* @throws IOException if thrown by HttpURLConnection methods
|
||||
* @see java.net.HttpURLConnection#setRequestMethod
|
||||
* @see java.net.HttpURLConnection#setRequestProperty
|
||||
*/
|
||||
private void prepareConnection(HttpURLConnection con, HttpRequest request) throws IOException {
|
||||
con.setDoOutput(true);
|
||||
con.setRequestMethod(request.getRequestMethod());
|
||||
private void prepareConnection(HttpURLConnection connection, HttpRequest request) throws IOException {
|
||||
connection.setDoInput(true);
|
||||
String requestMethod = request.getRequestMethod();
|
||||
if ("PUT".equals(requestMethod) || "POST".equals(requestMethod)) {
|
||||
connection.setDoOutput(true);
|
||||
}
|
||||
else {
|
||||
connection.setDoOutput(false);
|
||||
}
|
||||
connection.setRequestMethod(request.getRequestMethod());
|
||||
String contentType = request.getContentType();
|
||||
if (contentType != null) {
|
||||
con.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, contentType);
|
||||
connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, contentType);
|
||||
}
|
||||
Integer contentLength = request.getContentLength();
|
||||
if (contentLength != null) {
|
||||
con.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, contentLength.toString());
|
||||
connection.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, contentLength.toString());
|
||||
}
|
||||
LocaleContext locale = LocaleContextHolder.getLocaleContext();
|
||||
if (locale != null) {
|
||||
con.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE,
|
||||
connection.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE,
|
||||
StringUtils.toLanguageTag(locale.getLocale()));
|
||||
}
|
||||
if (isAcceptGzipEncoding()) {
|
||||
con.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
|
||||
connection.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRequestBody(HttpURLConnection con, ByteArrayOutputStream baos) throws IOException {
|
||||
baos.writeTo(con.getOutputStream());
|
||||
private void writeRequestBody(HttpURLConnection connection, ByteArrayOutputStream body) throws IOException {
|
||||
if (body != null) {
|
||||
byte[] bytes = body.toByteArray();
|
||||
if (bytes.length > 0) {
|
||||
FileCopyUtils.copy(bytes, connection.getOutputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateResponse(HttpURLConnection con) throws IOException {
|
||||
if (con.getResponseCode() >= 300) {
|
||||
private void validateResponse(HttpURLConnection connection) throws IOException {
|
||||
if (connection.getResponseCode() >= 300) {
|
||||
throw new IOException(
|
||||
"Did not receive successful HTTP response: status code = "
|
||||
+ con.getResponseCode() + ", status message = ["
|
||||
+ con.getResponseMessage() + "]");
|
||||
+ connection.getResponseCode() + ", status message = ["
|
||||
+ connection.getResponseMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,21 +128,21 @@ public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor {
|
||||
* This implementation simply reads the HttpURLConnection's InputStream.
|
||||
* If the response is recognized as GZIP response, the InputStream will be
|
||||
* wrapped in a GZIPInputStream.
|
||||
* @param con the HttpURLConnection to read the response body from
|
||||
* @param connection the HttpURLConnection to read the response body from
|
||||
* @return an InputStream for the response body
|
||||
* @throws IOException if thrown by I/O methods
|
||||
* @see #isGzipResponse
|
||||
* @see java.util.zip.GZIPInputStream
|
||||
* @see java.net.HttpURLConnection#getInputStream()
|
||||
*/
|
||||
private InputStream readResponseBody(HttpURLConnection con) throws IOException {
|
||||
if (isGzipResponse(con)) {
|
||||
private InputStream readResponseBody(HttpURLConnection connection) throws IOException {
|
||||
if (isGzipResponse(connection)) {
|
||||
// GZIP response found - need to unzip.
|
||||
return new GZIPInputStream(con.getInputStream());
|
||||
return new GZIPInputStream(connection.getInputStream());
|
||||
}
|
||||
else {
|
||||
// Plain response found.
|
||||
return con.getInputStream();
|
||||
return connection.getInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +151,10 @@ public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor {
|
||||
* <p>
|
||||
* This implementation checks whether the HTTP "Content-Encoding" header
|
||||
* contains "gzip" (in any casing).
|
||||
* @param con the HttpURLConnection to check
|
||||
* @param connection the HttpURLConnection to check
|
||||
*/
|
||||
private boolean isGzipResponse(HttpURLConnection con) {
|
||||
String encodingHeader = con.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);
|
||||
private boolean isGzipResponse(HttpURLConnection connection) {
|
||||
String encodingHeader = connection.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);
|
||||
return (encodingHeader != null
|
||||
&& encodingHeader.toLowerCase().indexOf(ENCODING_GZIP) != -1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user