diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/DefaultInboundRequestMapper.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/DefaultInboundRequestMapper.java
index cd09351e29..b38ec48070 100644
--- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/DefaultInboundRequestMapper.java
+++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/DefaultInboundRequestMapper.java
@@ -17,6 +17,7 @@
package org.springframework.integration.http;
import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
@@ -26,7 +27,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -36,23 +36,30 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
+import org.springframework.web.multipart.MultipartException;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.multipart.MultipartResolver;
/**
* Default implementation of {@link InboundRequestMapper} for inbound HttpServletRequests.
* The request will be mapped according to the following rules:
*
- * - For a GET request, the parameter Map will be copied as the payload.
- * The map's keys will be Strings, and the values will be String arrays
+ *
- For a GET request or a POST request with a Content-Type of
+ * "application/x-www-form-urlencoded", the parameter Map will be copied as the
+ * payload. The map's keys will be Strings, and the values will be String arrays
* as described for {@link ServletRequest#getParameterMap()}.
+ * - If a MultipartResolver has been provided, and a multipart request is
+ * detected, the multipart file content will be converted to String for any
+ * "text" content type, or byte arrays otherwise.
* - For other request types, the request body will be used as the payload
- * and the type will depend on the Content-Type header value. If it
- * begins with "text", a String will be created. If the Content-Type
- * is "application/x-java-serialized-object", the request body will be
- * expected to contain a Serializable Object, and that will be used as
- * the message payload. Otherwise, the payload will be a byte array.
- * The parameter Map values will then be added as Message headers.
+ * and the type will depend on the Content-Type header value. If it begins with
+ * "text", a String will be created. If the Content-Type is
+ * "application/x-java-serialized-object", the request body will be expected to
+ * contain a Serializable Object, and that will be used as the message payload.
+ * Otherwise, the payload will be a byte array.
*
- * In both cases, the original request headers will be passed in the
+ * In all cases, the original request headers will be passed in the
* MessageHeaders. Likewise, the following headers will be added:
*
* - {@link HttpHeaders#REQUEST_URL}
@@ -65,73 +72,179 @@ import org.springframework.integration.message.MessageBuilder;
*/
public class DefaultInboundRequestMapper implements InboundRequestMapper {
- private Log logger = LogFactory.getLog(getClass());
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private volatile MultipartResolver multipartResolver;
+
+ private String multipartCharset = null;
+
+
+ /**
+ * Specify the {@link MultipartResolver} to use when checking requests.
+ * If no resolver is provided, this mapper will not support multipart
+ * requests.
+ */
+ public void setMultipartResolver(MultipartResolver multipartResolver) {
+ this.multipartResolver = multipartResolver;
+ }
+
+ /**
+ * Specify the charset name to use when converting multipart file content
+ * into Strings.
+ */
+ public void setMultipartCharset(String multipartCharset) {
+ this.multipartCharset = multipartCharset;
+ }
public Message> toMessage(HttpServletRequest request) throws Exception {
- Message> message = null;
- String contentType = request.getContentType();
- if (request.getMethod().equals("GET")) {
- message = this.createMessageFromGetRequest(request);
+ try {
+ request = this.checkMultipart(request);
+ Object payload = createPayloadFromRequest(request);
+ MessageBuilder> builder = MessageBuilder.withPayload(payload);
+ this.populateHeaders(request, builder);
+ return builder.build();
}
- else {
- Object payload = null;
- if (contentType != null && contentType.startsWith("text")) {
- if (logger.isDebugEnabled()) {
- logger.debug("received " + request.getMethod()
- + " request, creating payload with text content");
- }
- StringBuilder sb = new StringBuilder();
- BufferedReader reader = request.getReader();
- String line = reader.readLine();
- while (line != null) {
- sb.append(line);
- line = reader.readLine();
- }
- payload = sb.toString();
- }
- else if (contentType != null && contentType.equals("application/x-java-serialized-object")) {
- try {
- payload = new ObjectInputStream(request.getInputStream()).readObject();
- }
- catch (ClassNotFoundException e) {
- throw new ServletException("failed to deserialize Object in request", e);
- }
+ finally {
+ this.cleanupMultipart(request);
+ }
+ }
+
+ /**
+ * Convert the request into a multipart request to make multiparts available.
+ * If no multipart resolver is set, simply use the existing request.
+ * @param request current HTTP request
+ * @return the processed request (multipart wrapper if necessary)
+ * @see MultipartResolver#resolveMultipart
+ */
+ private HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
+ if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
+ if (request instanceof MultipartHttpServletRequest) {
+ logger.debug("Request is already a MultipartHttpServletRequest");
}
else {
- InputStream stream = request.getInputStream();
- int length = request.getContentLength();
- if (length == -1) {
- throw new ResponseStatusCodeException(HttpServletResponse.SC_LENGTH_REQUIRED);
- }
- if (logger.isDebugEnabled()) {
- logger.debug("received " + request.getMethod() + " request, "
- + "creating byte array payload with content lenth: " + length);
- }
- byte[] bytes = new byte[length];
- stream.read(bytes, 0, length);
- payload = bytes;
+ return this.multipartResolver.resolveMultipart(request);
}
- MessageBuilder> builder = MessageBuilder.withPayload(payload);
- this.populateHeaders(request, builder, true);
- message = builder.build();
}
- return message;
+ return request;
+ }
+
+ /**
+ * Clean up any resources used by the given multipart request (if any).
+ * @param request current HTTP request
+ * @see MultipartResolver#cleanupMultipart
+ */
+ private void cleanupMultipart(HttpServletRequest request) {
+ if (this.multipartResolver != null && request instanceof MultipartHttpServletRequest) {
+ this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
+ }
+ }
+
+ private Object createPayloadFromRequest(HttpServletRequest request) throws Exception {
+ Object payload = null;
+ String contentType = request.getContentType() != null ? request.getContentType() : "";
+ if (request instanceof MultipartHttpServletRequest) {
+ payload = this.createPayloadFromMultipartRequest((MultipartHttpServletRequest) request);
+ }
+ else if (contentType.startsWith("multipart/form-data")) {
+ throw new IllegalArgumentException("Content-Type of 'multipart/form-data' requires a MultipartResolver." +
+ " Try configuring a MultipartResolver within the ApplicationContext.");
+ }
+ else if (request.getMethod().equals("GET")) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("received GET request, using parameter map as payload");
+ }
+ payload = this.createPayloadFromParameterMap(request);
+ }
+ else if (contentType.startsWith("application/x-www-form-urlencoded")) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("received " + request.getMethod()
+ + " request with form data, using parameter map as payload");
+ }
+ payload = createPayloadFromParameterMap(request);
+ }
+ else if (contentType.startsWith("text")) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("received " + request.getMethod()
+ + " request, creating payload with text content");
+ }
+ payload = createPayloadFromTextContent(request);
+ }
+ else if (contentType.startsWith("application/x-java-serialized-object")) {
+ payload = createPayloadFromSerializedObject(request);
+ }
+ else {
+ payload = createPayloadFromInputStream(request);
+ }
+ return payload;
}
@SuppressWarnings("unchecked")
- private Message> createMessageFromGetRequest(HttpServletRequest request) {
- if (logger.isDebugEnabled()) {
- logger.debug("received GET request, using parameter map as payload");
+ private Object createPayloadFromMultipartRequest(MultipartHttpServletRequest multipartRequest) {
+ Map payloadMap = new HashMap(multipartRequest.getParameterMap());
+ Map fileMap = (Map) multipartRequest.getFileMap();
+ for (Map.Entry entry : fileMap.entrySet()) {
+ MultipartFile multipartFile = entry.getValue();
+ try {
+ if (multipartFile.getContentType() != null && multipartFile.getContentType().startsWith("text")) {
+ String multipartFileAsString = this.multipartCharset != null ?
+ new String(multipartFile.getBytes(), this.multipartCharset) :
+ new String(multipartFile.getBytes());
+ payloadMap.put(entry.getKey(), multipartFileAsString);
+ }
+ else {
+ payloadMap.put(entry.getKey(), multipartFile.getBytes());
+ }
+ }
+ catch (IOException e) {
+ throw new IllegalArgumentException("Cannot read contents of multipart file", e);
+ }
}
+ return Collections.unmodifiableMap(payloadMap);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object createPayloadFromParameterMap(HttpServletRequest request) {
Map parameterMap = new HashMap(request.getParameterMap());
- MessageBuilder> builder = MessageBuilder.withPayload(Collections.unmodifiableMap(parameterMap));
- this.populateHeaders(request, builder, false);
- return builder.build();
+ return Collections.unmodifiableMap(parameterMap);
}
- @SuppressWarnings("unchecked")
- private void populateHeaders(HttpServletRequest request, MessageBuilder> builder, boolean includeParameters) {
+ private Object createPayloadFromTextContent(HttpServletRequest request) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader reader = request.getReader();
+ String line = reader.readLine();
+ while (line != null) {
+ sb.append(line);
+ line = reader.readLine();
+ }
+ return sb.toString();
+ }
+
+ private Object createPayloadFromSerializedObject(HttpServletRequest request) {
+ try {
+ return new ObjectInputStream(request.getInputStream()).readObject();
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("failed to deserialize Object in request", e);
+ }
+ }
+
+ private byte[] createPayloadFromInputStream(HttpServletRequest request) throws Exception {
+ InputStream stream = request.getInputStream();
+ int length = request.getContentLength();
+ if (length == -1) {
+ throw new ResponseStatusCodeException(HttpServletResponse.SC_LENGTH_REQUIRED);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("received " + request.getMethod() + " request, "
+ + "creating byte array payload with content lenth: " + length);
+ }
+ byte[] bytes = new byte[length];
+ stream.read(bytes, 0, length);
+ return bytes;
+ }
+
+ private void populateHeaders(HttpServletRequest request, MessageBuilder> builder) {
Enumeration> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
@@ -151,9 +264,6 @@ public class DefaultInboundRequestMapper implements InboundRequestMapper {
}
}
}
- if (includeParameters) {
- builder.copyHeaders(request.getParameterMap());
- }
builder.setHeader(HttpHeaders.REQUEST_URL, request.getRequestURL().toString());
builder.setHeader(HttpHeaders.REQUEST_METHOD, request.getMethod());
builder.setHeader(HttpHeaders.USER_PRINCIPAL, request.getUserPrincipal());
diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java
index 93e9eda67f..5b25ec0c51 100644
--- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java
+++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java
@@ -29,11 +29,14 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.integration.core.Message;
import org.springframework.integration.gateway.SimpleMessagingGateway;
import org.springframework.integration.message.MessageTimeoutException;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestHandler;
+import org.springframework.web.multipart.MultipartResolver;
+import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.View;
/**
@@ -87,7 +90,7 @@ public class HttpInboundEndpoint extends SimpleMessagingGateway implements HttpR
private volatile boolean expectReply;
- private volatile InboundRequestMapper requestMapper = new DefaultInboundRequestMapper();
+ private volatile InboundRequestMapper requestMapper;
private volatile boolean extractReplyPayload = true;
@@ -172,7 +175,37 @@ public class HttpInboundEndpoint extends SimpleMessagingGateway implements HttpR
this.replyKey = (replyKey != null) ? replyKey : DEFAULT_REPLY_KEY;
}
+ @Override
+ protected void onInit() throws Exception {
+ if (this.requestMapper == null) {
+ this.configureDefaultRequestMapper();
+ }
+ super.onInit();
+ }
+
+ private void configureDefaultRequestMapper() {
+ DefaultInboundRequestMapper defaultMapper = new DefaultInboundRequestMapper();
+ if (this.getBeanFactory() != null) {
+ try {
+ MultipartResolver multipartResolver = (MultipartResolver)
+ this.getBeanFactory().getBean(DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Using MultipartResolver [" + multipartResolver + "]");
+ }
+ defaultMapper.setMultipartResolver(multipartResolver);
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unable to locate MultipartResolver with name '" + DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME +
+ "': no multipart request handling will be supported.");
+ }
+ }
+ }
+ this.requestMapper = defaultMapper;
+ }
+
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ Assert.notNull(this.requestMapper, "HttpInboundEndpoint has not been initialized.");
if (!this.supportedMethods.contains(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
diff --git a/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java b/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java
index fca65658ac..53bb5f97da 100644
--- a/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java
+++ b/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java
@@ -95,6 +95,7 @@ public class HttpInboundEndpointTests {
endpoint = new HttpInboundEndpoint();
endpoint.setRequestChannel(requestChannel);
endpoint.setReplyChannel(replyChannel);
+ endpoint.afterPropertiesSet();
reset(allmocks);
request = new MockHttpServletRequest("GET", "/anyurl");
response = new MockHttpServletResponse();
@@ -220,18 +221,18 @@ public class HttpInboundEndpointTests {
}
@Test
- public void handleRequest_withPOSTRequestAndParamsOnly_sameInMessageHeader()
+ public void handleRequest_withPOSTRequestAndFormContent_sameInMessagePayload()
throws ServletException, IOException {
- addRequestContent("POST", "text/plain", ANY_ENCODING, new byte[0]);
+ addRequestContent("POST", "application/x-www-form-urlencoded", ANY_ENCODING, new byte[0]);
final Map sourceParams = addAnyParametersToRequest();
request.setParameters(sourceParams);
expect(requestChannel.send(isA(Message.class))).andAnswer(
new IAnswer() {
@SuppressWarnings("unchecked")
public Boolean answer() throws Throwable {
- MessageHeaders headers = ((Message) getCurrentArguments()[0]).getHeaders();
+ Map payloadMap = (Map) ((Message) getCurrentArguments()[0]).getPayload();
for (String key : sourceParams.keySet()) {
- assertThat(headers.get(key),
+ assertThat(payloadMap.get(key),
is((Object) new String[] { sourceParams.get(key) }));
}
return true;
@@ -388,6 +389,7 @@ public class HttpInboundEndpointTests {
HttpInboundEndpoint.class.getConstructor(new Class[0]),
new Object[0]),
HttpInboundEndpoint.class.getMethod("sendAndReceive", Object.class));
+ endpoint.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);