Map typed payloads with String keys and values now map to GET requests by default.

This commit is contained in:
Mark Fisher
2009-03-20 03:29:46 +00:00
parent 1a0fa63616
commit 5b45423566
3 changed files with 142 additions and 46 deletions

View File

@@ -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;
}
}
}

View File

@@ -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();

View File

@@ -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);
}