From 0bed56e780cb74313064dbfc940a8cf75fc392e3 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Mon, 27 Sep 2010 15:01:39 -0400 Subject: [PATCH] INT-1481, added support for serialized content type, mroe refactoring and test cases --- .../HttpRequestExecutingMessageHandler.java | 100 ++++++------- .../DefaultOutboundRequestMapperTests.java | 133 +++++++++++------- ...tpRequestExecutingMessageHandlerTests.java | 8 +- 3 files changed, 140 insertions(+), 101 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java index 991533e7b1..13932f8572 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java @@ -16,9 +16,9 @@ package org.springframework.integration.http; -import java.io.Serializable; import java.net.URI; import java.nio.charset.Charset; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -248,6 +248,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe : this.createHttpEntityWithMessageAsBody(message); } + @SuppressWarnings({ "unchecked", "rawtypes" }) private HttpEntity createHttpEntityWithPayloadAsBody(Message requestMessage) { if (requestMessage.getPayload() instanceof HttpEntity) { return (HttpEntity) requestMessage.getPayload(); @@ -255,11 +256,14 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe HttpHeaders httpHeaders = new HttpHeaders(); this.headerMapper.fromHeaders(requestMessage.getHeaders(), httpHeaders); Object payload = requestMessage.getPayload(); + if (payload instanceof Map && !(payload instanceof MultiValueMap)){ + payload = this.convertToMultipartValueMap((Map) payload); + } MediaType contentType = (payload instanceof String) ? this.resolveContentType((String) payload, this.charset) : this.resolveContentType(payload); httpHeaders.setContentType(contentType); if (HttpMethod.POST.equals(this.httpMethod) || HttpMethod.PUT.equals(this.httpMethod)) { - return new HttpEntity(requestMessage.getPayload(), httpHeaders); + return new HttpEntity(payload, httpHeaders); } return new HttpEntity(httpHeaders); } @@ -279,23 +283,26 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe else if (content instanceof Source) { contentType = MediaType.TEXT_XML; } - else { - if (content instanceof Map){ - Map multiValueMap = (Map) content; - if (!(content instanceof MultiValueMap)){ - multiValueMap = this.convertToMultipartValueMap((Map) content); - } + else if (content instanceof Map){ + Map multiValueMap = (Map) content; + if (!(content instanceof MultiValueMap)){ + multiValueMap = this.convertToMultipartValueMap((Map) content); + } + /* + * We need to check separately for MULTIPART as well as URLENCODED simply because + * MultiValueMap is actually valid content for serialization + */ + if (this.isFormData((MultiValueMap)multiValueMap)){ if (this.isMultipart((MultiValueMap)multiValueMap)){ contentType = MediaType.MULTIPART_FORM_DATA; - } else { + } + else { contentType = MediaType.APPLICATION_FORM_URLENCODED; - //contentType = new MediaType("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()); + contentType = new MediaType("application", "x-java-serialized-object"); } return contentType; } @@ -304,51 +311,50 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe return new MediaType("text", "plain", Charset.forName(charset)); } + @SuppressWarnings("unchecked") - private MultiValueMap convertToMultipartValueMap(Map simpleContentMap){ - MultiValueMap multipartValueMap = new LinkedMultiValueMap(); - try { - for (String key : simpleContentMap.keySet()) { - Object value = simpleContentMap.get(key); - if (value != null){ - if (value instanceof Object[]){ - Object[] valueArray = (Object[]) value; - for (Object objectValue : valueArray) { - if (objectValue != null){ - multipartValueMap.add(key, objectValue); - } - } - } - else if (value instanceof Collection){ - Collection stringCollection = (Collection) value; - for (Object objectValue : stringCollection) { - if (objectValue != null){ - multipartValueMap.add(key, objectValue); - } - } - } - else { - multipartValueMap.add(key, value); - } - } - } - } catch (ClassCastException cce) { - throw new MessageConversionException("Content map contains unsupported type for 'key'.", cce); - } + private MultiValueMap convertToMultipartValueMap(Map simpleContentMap){ + LinkedMultiValueMap multipartValueMap = new LinkedMultiValueMap(); + for (Object key : simpleContentMap.keySet()) { + Object value = simpleContentMap.get(key); + if (value instanceof Object[]){ + Object[] valueArray = (Object[]) value; + value = Arrays.asList(valueArray); + } + if (value instanceof Collection){ + multipartValueMap.put(key, (List) value); + } + else { + multipartValueMap.add(key, value); + } + } return multipartValueMap; } /** - * If all keys are Strings, we'll consider the Map to be form data. + * If all keys are Strings, and some values are not Strings we'll consider + * the Map to be multipart/form-data */ private boolean isMultipart(MultiValueMap> map) { - for (List listValues : map.values()) { - for (Object listValue : listValues) { - if (!(listValue instanceof String)) { + for (String key : map.keySet()) { + List values = map.get(key); + for (Object value : values) { + if (value != null && !(value instanceof String)) { return true; } - } + } } return false; } + /** + * If all keys and values are Strings, we'll consider the Map to be form data. + */ + private boolean isFormData(MultiValueMap> map) { + for (Object key : map.keySet()) { + if (!(key instanceof String)){ + return false; + } + } + return true; + } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java index c43a1ae4b6..56b09702f8 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/DefaultOutboundRequestMapperTests.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -38,6 +39,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; @@ -69,11 +71,11 @@ public class DefaultOutboundRequestMapperTests { assertEquals("intentional", exception.getCause().getMessage()); HttpEntity request = template.lastRequestEntity.get(); 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")); + assertTrue(body instanceof MultiValueMap); + MultiValueMap map = (MultiValueMap ) body; + assertEquals("1", map.get("a").iterator().next()); + assertEquals("2", map.get("b").iterator().next()); + assertEquals("3", map.get("c").iterator().next()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @@ -99,26 +101,26 @@ public class DefaultOutboundRequestMapperTests { assertEquals("intentional", exception.getCause().getMessage()); HttpEntity request = template.lastRequestEntity.get(); 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); + assertTrue(body instanceof MultiValueMap); + MultiValueMap map = (MultiValueMap ) body; + + List aValue = map.get("a"); + assertEquals(3, aValue.size()); + assertEquals("1", aValue.get(0)); + assertEquals("2", aValue.get(1)); + assertEquals("3", aValue.get(2)); + + List bValue = map.get("b"); + assertEquals(1, bValue.size()); + assertEquals("4", bValue.get(0)); + + List cValue = map.get("c"); + assertEquals(1, cValue.size()); + assertEquals("5", cValue.get(0)); + + List dValue = map.get("d"); + assertEquals(1, dValue.size()); + assertEquals("6", dValue.get(0)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @@ -146,23 +148,22 @@ public class DefaultOutboundRequestMapperTests { assertEquals("intentional", exception.getCause().getMessage()); HttpEntity request = template.lastRequestEntity.get(); 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)); + assertTrue(body instanceof MultiValueMap); + MultiValueMap map = (MultiValueMap ) body; + + + List aValue = map.get("a"); + assertEquals(2, aValue.size()); + assertEquals("1", aValue.get(0)); + assertEquals("2", aValue.get(1)); + + List bValue = map.get("b"); + assertEquals(0, bValue.size()); + + List cValue = map.get("c"); + assertEquals(1, cValue.size()); + assertEquals("3", cValue.get(0)); + assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @@ -187,14 +188,16 @@ public class DefaultOutboundRequestMapperTests { assertEquals("intentional", exception.getCause().getMessage()); HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); - assertTrue(body instanceof Map); - Map map = (Map) body; + assertTrue(body instanceof MultiValueMap); + MultiValueMap map = (MultiValueMap) body; assertTrue(map.containsKey("a")); - assertNull(map.get("a")); - Object entryB = map.get("b"); - assertEquals("foo", entryB); + assertTrue(map.get("a").size() == 1); + assertNull(map.get("a").get(0)); + List entryB = map.get("b"); + assertEquals("foo", entryB.get(0)); assertTrue(map.containsKey("c")); - assertNull(map.get("c")); + assertTrue(map.get("c").size() == 1); + assertNull(map.get("c").get(0)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType()); } @@ -217,10 +220,38 @@ public class DefaultOutboundRequestMapperTests { } assertEquals("intentional", exception.getCause().getMessage()); HttpEntity request = template.lastRequestEntity.get(); - Map map = (Map) request.getBody(); + MultiValueMap map = (MultiValueMap) request.getBody(); assertEquals(2, map.size()); - assertEquals(TestBean.class, map.get("A").getClass()); - assertEquals(TestBean.class, map.get("B").getClass()); + assertEquals(TestBean.class, map.get("A").get(0).getClass()); + assertEquals(TestBean.class, map.get("B").get(0).getClass()); + assertEquals(MediaType.MULTIPART_FORM_DATA, request.getHeaders().getContentType()); + } + @Test + public void nonFormAndNonMultipartDataInMap() throws Exception { + HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration"); + MockRestTemplate template = new MockRestTemplate(); + new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); + handler.setHttpMethod(HttpMethod.POST); + Map form = new LinkedHashMap(); + form.put(1, new TestBean()); + form.put(2, new TestBean()); + Message message = MessageBuilder.withPayload(form).build(); + Exception exception = null; + try { + handler.handleMessage(message); + } + catch (Exception e) { + exception = e; + } + assertEquals("intentional", exception.getCause().getMessage()); + HttpEntity request = template.lastRequestEntity.get(); + MultiValueMap map = (MultiValueMap) request.getBody(); + assertEquals(2, map.size()); + assertEquals(TestBean.class, map.get(1).get(0).getClass()); + assertEquals(TestBean.class, map.get(2).get(0).getClass()); + System.out.println(request.getHeaders().getContentType()); + assertEquals("application", request.getHeaders().getContentType().getType()); + assertEquals("x-java-serialized-object", request.getHeaders().getContentType().getSubtype()); } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java index bc090b91cf..51a52023ab 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java @@ -114,8 +114,9 @@ public class HttpRequestExecutingMessageHandlerTests { simpleMap.put("city", cities); simpleMap.put("state", "PA"); MultiValueMap multiValueMap = (MultiValueMap) convertToMultipartValueMap.invoke(handler, simpleMap); - assertEquals(1, multiValueMap.get("city").size()); + assertEquals(2, multiValueMap.get("city").size()); assertEquals(philadelphia, multiValueMap.get("city").get(0)); + assertNull(multiValueMap.get("city").get(1)); assertEquals("PA", multiValueMap.get("state").iterator().next()); } @Test @@ -130,12 +131,13 @@ public class HttpRequestExecutingMessageHandlerTests { simpleMap.put("city", cities); simpleMap.put("state", "PA"); MultiValueMap multiValueMap = (MultiValueMap) convertToMultipartValueMap.invoke(handler, simpleMap); - assertEquals(1, multiValueMap.get("city").size()); + assertEquals(2, multiValueMap.get("city").size()); assertEquals(philadelphia, multiValueMap.get("city").get(0)); + assertNull(multiValueMap.get("city").get(1)); assertEquals("PA", multiValueMap.get("state").iterator().next()); } - @Test(expected=InvocationTargetException.class) + @Test public void validateMapWithNonStrigKeysConversionToMvp() throws Exception { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("localhost"); Method convertToMultipartValueMap = ReflectionUtils.findMethod(handler.getClass(), "convertToMultipartValueMap", Map.class);