From bb6aebc587f9691513b87c8b4f086e183dad4ddc Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Fri, 13 Sep 2013 20:45:10 -0400 Subject: [PATCH] INT-3142 Allow Merging of HTTP Message Converters INT-3142: A flag to retain default converters when the user has customized messageconverters for HttpRequestHandlingController, HttpInboundGateway * Introduced a new flag in the base class of HttpRequHttpRequestHandlingController, HttpInboundGateway - registerDefaultConverters * Introduced a new attribute "register-default-converters" for inbound-channel-adapter and inbound-gateway to indicate if the default HttpMessageConverter's need to be registered * Http namespace parser related changes to set the new flag * Added additional tests to test the flag and the namespace parser related changes Changes per feedback from Artem - additional javadoc comments, cleaned up tests Updated: Setting the register-default-converters by default to false now, for backward compatibility reasons Changed adapter name per feedback from Artem Changed adapter name per feedback from Artem Changed attribute name from register-default-converters to merge-with-default-converters INT-3142: Atomic operation for messageConverters 'What's New' polishing Polishing Fix a few comments, error messages, docs, whitespace. --- .../config/HttpInboundEndpointParser.java | 2 + .../HttpRequestHandlingEndpointSupport.java | 81 ++++++++++++++----- .../config/spring-integration-http-3.0.xsd | 22 +++++ ...boundChannelAdapterParserTests-context.xml | 26 ++++-- .../HttpInboundChannelAdapterParserTests.java | 52 ++++++++++-- .../HttpInboundGatewayParserTests-context.xml | 31 +++++-- .../config/HttpInboundGatewayParserTests.java | 61 +++++++++++++- .../HttpRequestHandlingControllerTests.java | 10 +++ ...pRequestHandlingMessagingGatewayTests.java | 8 ++ ...gMessagingGatewayWithPathMappingTests.java | 4 + src/reference/docbook/http.xml | 4 +- src/reference/docbook/whats-new.xml | 44 ++++++---- 12 files changed, 287 insertions(+), 58 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java index 556e8efa41..26cc330366 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java @@ -47,6 +47,7 @@ import org.w3c.dom.Element; * @author Mark Fisher * @author Oleg Zhurakousky * @author Gary Russell + * @author Biju Kunjummen */ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParser { @@ -159,6 +160,7 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "errors-key"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "error-code"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converters"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "merge-with-default-converters"); //IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "header-mapper"); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index f2ddc0d69d..957e6fc879 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -41,6 +41,7 @@ import org.springframework.http.converter.ResourceHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter; import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.http.converter.xml.SourceHttpMessageConverter; @@ -57,6 +58,7 @@ import org.springframework.integration.http.multipart.MultipartHttpInputMessage; import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.support.json.JacksonJsonUtils; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -95,6 +97,7 @@ import org.springframework.web.util.UrlPathHelper; * @author Oleg Zhurakousky * @author Gary Russell * @author Artem Bilan + * @author Biju Kunjummen * @since 2.0 */ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySupport @@ -103,19 +106,21 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", HttpRequestHandlingEndpointSupport.class.getClassLoader()); - private static final boolean jacksonPresent = ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", - HttpRequestHandlingEndpointSupport.class.getClassLoader()) - && ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", HttpRequestHandlingEndpointSupport.class - .getClassLoader()); private static boolean romePresent = ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", HttpRequestHandlingEndpointSupport.class.getClassLoader()); + private final List> defaultMessageConverters = new ArrayList>(); + + private volatile List> messageConverters = new ArrayList>(); + private volatile List supportedMethods = Arrays.asList(HttpMethod.GET, HttpMethod.POST); private volatile Class requestPayloadType = null; - private volatile List> messageConverters = new ArrayList>(); + private volatile boolean convertersMerged; + + private volatile boolean mergeWithDefaultConverters = false; private volatile HeaderMapper headerMapper = DefaultHttpHeaderMapper.inboundMapper(); @@ -143,25 +148,42 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa this(true); } - @SuppressWarnings("rawtypes") public HttpRequestHandlingEndpointSupport(boolean expectReply) { this.expectReply = expectReply; - this.messageConverters.add(new MultipartAwareFormHttpMessageConverter()); - this.messageConverters.add(new ByteArrayHttpMessageConverter()); + this.defaultMessageConverters.add(new MultipartAwareFormHttpMessageConverter()); + this.defaultMessageConverters.add(new ByteArrayHttpMessageConverter()); StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); - this.messageConverters.add(stringHttpMessageConverter); - this.messageConverters.add(new ResourceHttpMessageConverter()); - this.messageConverters.add(new SourceHttpMessageConverter()); + this.defaultMessageConverters.add(stringHttpMessageConverter); + this.defaultMessageConverters.add(new ResourceHttpMessageConverter()); + @SuppressWarnings("rawtypes") + SourceHttpMessageConverter sourceConverter = new SourceHttpMessageConverter(); + this.defaultMessageConverters.add(sourceConverter); if (jaxb2Present) { - this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); + this.defaultMessageConverters.add(new Jaxb2RootElementHttpMessageConverter()); + if (logger.isDebugEnabled()) { + logger.debug("'Jaxb2RootElementHttpMessageConverter' was added to the 'messageConverters'."); + } } - if (jacksonPresent) { - this.messageConverters.add(new MappingJacksonHttpMessageConverter()); + if (JacksonJsonUtils.isJackson2Present()) { + this.defaultMessageConverters.add(new MappingJackson2HttpMessageConverter()); + if (logger.isDebugEnabled()) { + logger.debug("'MappingJackson2HttpMessageConverter' was added to the 'messageConverters'."); + } + } + else if (JacksonJsonUtils.isJacksonPresent()) { + this.defaultMessageConverters.add(new MappingJacksonHttpMessageConverter()); + if (logger.isDebugEnabled()) { + logger.debug("'MappingJacksonHttpMessageConverter' was added to the 'messageConverters'."); + } } if (romePresent) { - this.messageConverters.add(new AtomFeedHttpMessageConverter()); - this.messageConverters.add(new RssChannelHttpMessageConverter()); + this.defaultMessageConverters.add(new AtomFeedHttpMessageConverter()); + this.defaultMessageConverters.add(new RssChannelHttpMessageConverter()); + if (logger.isDebugEnabled()) { + logger.debug("'AtomFeedHttpMessageConverter' was added to the 'messageConverters'."); + logger.debug("'RssChannelHttpMessageConverter' was added to the 'messageConverters'."); + } } } @@ -210,14 +232,28 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa * responses. */ public void setMessageConverters(List> messageConverters) { - Assert.notEmpty(messageConverters, "'messageConverters' must not be empty"); - this.messageConverters = messageConverters; + Assert.noNullElements(messageConverters.toArray(), "'messageConverters' must not contain null entries"); + List> localConverters = new ArrayList>(messageConverters); + if (this.mergeWithDefaultConverters) { + localConverters.addAll(this.defaultMessageConverters); + this.convertersMerged = true; + } + this.messageConverters = localConverters; } protected List> getMessageConverters() { return this.messageConverters; } + + /** + * Flag which determines if the default converters should be available after + * custom converters. + */ + public void setMergeWithDefaultConverters(boolean mergeWithDefaultConverters) { + this.mergeWithDefaultConverters = mergeWithDefaultConverters; + } + /** * Set the {@link HeaderMapper} to use when mapping between HTTP headers and MessageHeaders. */ @@ -284,6 +320,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Locates the {@link MultipartResolver} bean based on the default name defined by the * {@link DispatcherServlet#MULTIPART_RESOLVER_BEAN_NAME} constant if available. + * Sets up default converters if no converters set, or {@link #setMergeWithDefaultConverters(boolean)} + * was called with true after the converters were set. */ @Override protected void onInit() throws Exception { @@ -306,11 +344,12 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa } } } - + if (this.messageConverters.size() == 0 || (this.mergeWithDefaultConverters && !this.convertersMerged)) { + this.messageConverters.addAll(this.defaultMessageConverters); + } this.validateSupportedMethods(); } - @Override protected void doStart() { this.shuttingDown = false; @@ -468,7 +507,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa * Prepares an instance of {@link ServletServerHttpRequest} from the raw {@link HttpServletRequest}. Also converts * the request into a multipart request to make multiparts available if necessary. If no multipart resolver is set, * simply returns the existing request. - * @param request current HTTP request + * @param servletRequest current HTTP request * @return the processed request (multipart wrapper if necessary) * @see MultipartResolver#resolveMultipart */ diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-3.0.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-3.0.xsd index 3a2e979c55..ed26457783 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-3.0.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-3.0.xsd @@ -129,6 +129,17 @@ + + + + Flag to indicate if the default converters should be registered after any + custom converters. This flag is used only if message-converters + are provided, otherwise all default converters will be registered. + + Defaults to "false" + + + @@ -287,6 +298,17 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re + + + + Flag to indicate if the default converters should be registered after any custom + converters. This flag is used only if message-converters + are provided, otherwise all default converters will be registered. + + Defaults to "false" + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml index 546fdbb6e2..044117155e 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml @@ -1,14 +1,14 @@ - + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation="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://www.springframework.org/schema/integration/spring-integration.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> @@ -20,6 +20,18 @@ + + + + + + + + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java index c01ecbbe90..19fa1ddbed 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +16,8 @@ package org.springframework.integration.http.config; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -25,7 +27,6 @@ import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -34,6 +35,7 @@ import javax.servlet.http.HttpServletResponse; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -65,6 +67,7 @@ import org.springframework.util.MultiValueMap; * @author Gunnar Hillert * @author Artem Bilan * @author Gary Russell + * @author Biju Kunjummen */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -79,6 +82,10 @@ public class HttpInboundChannelAdapterParserTests { @Autowired private HttpRequestHandlingMessagingGateway postOnlyAdapter; + @Autowired + @Qualifier("adapterWithCustomConverterWithDefaults") + private HttpRequestHandlingMessagingGateway adapterWithCustomConverterWithDefaults; + @Autowired private HttpRequestHandlingMessagingGateway putOrDeleteAdapter; @@ -96,6 +103,14 @@ public class HttpInboundChannelAdapterParserTests { @Qualifier("/fname/{f}/lname/{l}") private HttpRequestHandlingMessagingGateway inboundAdapterWithNameNoPath; + @Autowired + @Qualifier("adapterWithCustomConverterNoDefaults") + private HttpRequestHandlingMessagingGateway adapterWithCustomConverterNoDefaults; + + @Autowired + @Qualifier("adapterNoCustomConverterNoDefaults") + private HttpRequestHandlingMessagingGateway adapterNoCustomConverterNoDefaults; + @Autowired private HttpRequestHandlingController inboundController; @@ -259,11 +274,7 @@ public class HttpInboundChannelAdapterParserTests { MockHttpServletResponse response = new MockHttpServletResponse(); - List> converters = new ArrayList>(); - converters.add(new SerializingHttpMessageConverter()); - postOnlyAdapter.setMessageConverters(converters); - - postOnlyAdapter.handleRequest(request, response); + adapterWithCustomConverterWithDefaults.handleRequest(request, response); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); Message message = requests.receive(0); assertNotNull(message); @@ -271,6 +282,7 @@ public class HttpInboundChannelAdapterParserTests { assertEquals("testObject", ((TestObject) message.getPayload()).text); } + @Test @SuppressWarnings("unchecked") public void putOrDeleteMethodsSupported() throws Exception { @@ -300,6 +312,32 @@ public class HttpInboundChannelAdapterParserTests { assertSame(autoChannel, TestUtils.getPropertyValue(autoChannelAdapter, "requestChannel")); } + @Test + public void testInboundAdapterWithMessageConverterDefaults() { + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(adapterWithCustomConverterWithDefaults, "messageConverters", List.class); + assertTrue("There should be more than 1 message converter. The customized one and the defaults.", messageConverters.size() > 1); + + //First converter should be the customized one + assertThat(messageConverters.get(0), instanceOf(SerializingHttpMessageConverter.class)); + } + + @Test + public void testInboundAdapterWithNoMessageConverterDefaults() { + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(adapterWithCustomConverterNoDefaults, "messageConverters", List.class); + //First converter should be the customized one + assertThat(messageConverters.get(0), instanceOf(SerializingHttpMessageConverter.class)); + assertTrue("There should be only the customized messageconverter registered.", messageConverters.size() == 1); + } + + @Test + public void testInboundAdapterWithNoMessageConverterNoDefaults() { + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(adapterNoCustomConverterNoDefaults, "messageConverters", List.class); + assertTrue("There should be more than 1 message converter. The defaults.", messageConverters.size() > 1); + } + @SuppressWarnings("serial") private static class TestObject implements Serializable { diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests-context.xml index 375af22d4b..3060c83c3f 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests-context.xml @@ -26,6 +26,28 @@ reply-timeout="4567" error-channel="errorChannel"/> + + + + + + + + + + + - + - - + - + - + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java index 0d47aca5b7..502218da31 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -17,8 +17,10 @@ package org.springframework.integration.http.config; import static org.hamcrest.CoreMatchers.any; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -34,6 +36,7 @@ import javax.servlet.http.HttpServletResponse; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -64,6 +67,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author Oleg Zhurakousky * @author Gary Russell * @author Gunnar Hillert + * @author Biju Kunjummen */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -73,6 +77,18 @@ public class HttpInboundGatewayParserTests { @Qualifier("inboundGateway") private HttpRequestHandlingMessagingGateway gateway; + @Autowired + @Qualifier("inboundGatewayWithOneCustomConverter") + private HttpRequestHandlingMessagingGateway gatewayWithOneCustomConverter; + + @Autowired + @Qualifier("inboundGatewayNoDefaultConverters") + private HttpRequestHandlingMessagingGateway gatewayNoDefaultConverters; + + @Autowired + @Qualifier("inboundGatewayWithCustomAndDefaultConverters") + private HttpRequestHandlingMessagingGateway gatewayWithCustomAndDefaultConverters; + @Autowired @Qualifier("withMappedHeaders") private HttpRequestHandlingMessagingGateway withMappedHeaders; @@ -105,6 +121,14 @@ public class HttpInboundGatewayParserTests { gateway, "messagingTemplate", MessagingTemplate.class); assertEquals(Long.valueOf(1234), TestUtils.getPropertyValue(messagingTemplate, "sendTimeout")); assertEquals(Long.valueOf(4567), TestUtils.getPropertyValue(messagingTemplate, "receiveTimeout")); + + boolean registerDefaultConverters = TestUtils.getPropertyValue(gateway,"mergeWithDefaultConverters", Boolean.class); + assertFalse("By default the register-default-converters flag should be false", registerDefaultConverters); + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(gateway,"messageConverters", List.class); + + assertTrue("The default converters should have been registered, given there are no custom converters", + messageConverters.size() > 0); } @Test(timeout=1000) @DirtiesContext @@ -119,6 +143,7 @@ public class HttpInboundGatewayParserTests { List> converters = new ArrayList>(); converters.add(new SerializingHttpMessageConverter()); gateway.setMessageConverters(converters); + gateway.afterPropertiesSet(); gateway.handleRequest(request, response); assertThat(response.getStatus(), is(HttpServletResponse.SC_OK)); @@ -200,6 +225,40 @@ public class HttpInboundGatewayParserTests { assertEquals("Oleg", personHeaders.get(0)); } + @Test + public void testInboundGatewayWithMessageConverterDefaults() { + @SuppressWarnings("unchecked") + List> messageConverters = + TestUtils.getPropertyValue(gatewayWithOneCustomConverter, "messageConverters", List.class); + assertThat("There should be only 1 message converter, by default register-default-converters is off", + messageConverters.size(), is(1)); + + //The converter should be the customized one + assertThat(messageConverters.get(0), instanceOf(SerializingHttpMessageConverter.class)); + } + + @Test + public void testInboundGatewayWithNoMessageConverterDefaults() { + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(gatewayNoDefaultConverters, "messageConverters", List.class); + //First converter should be the customized one + assertThat(messageConverters.get(0), instanceOf(SerializingHttpMessageConverter.class)); + + assertThat("There should be only 1 message converter, the register-default-converters is false", + messageConverters.size(), is(1)); + } + + @Test + public void testInboundGatewayWithCustomAndDefaultMessageConverters() { + @SuppressWarnings("unchecked") + List> messageConverters = TestUtils.getPropertyValue(gatewayWithCustomAndDefaultConverters, "messageConverters", List.class); + //First converter should be the customized one + assertThat(messageConverters.get(0), instanceOf(SerializingHttpMessageConverter.class)); + + assertTrue("There should be more than one converter", + messageConverters.size() > 1); + } + public static class Person{ private String name; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java index 4ee71f2da7..8dfb3db725 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java @@ -51,6 +51,7 @@ import org.springframework.web.servlet.View; * @author Mark Fisher * @author Gary Russell * @author Gunnar Hillert + * @author Biju Kunjummen * @since 2.0 */ public class HttpRequestHandlingControllerTests { @@ -62,6 +63,7 @@ public class HttpRequestHandlingControllerTests { controller.setBeanFactory(mock(BeanFactory.class)); controller.setRequestChannel(requestChannel); controller.setViewName("foo"); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); @@ -87,6 +89,7 @@ public class HttpRequestHandlingControllerTests { controller.setRequestChannel(requestChannel); Expression viewExpression = new SpelExpressionParser().parseExpression("'baz'"); controller.setViewExpression(viewExpression); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); @@ -118,6 +121,7 @@ public class HttpRequestHandlingControllerTests { controller.setBeanFactory(mock(BeanFactory.class)); controller.setRequestChannel(requestChannel); controller.setViewName("foo"); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -151,6 +155,7 @@ public class HttpRequestHandlingControllerTests { controller.setRequestChannel(requestChannel); Expression viewExpression = new SpelExpressionParser().parseExpression("headers['bar']"); controller.setViewExpression(viewExpression); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); @@ -181,6 +186,7 @@ public class HttpRequestHandlingControllerTests { controller.setRequestChannel(requestChannel); Expression viewExpression = new SpelExpressionParser().parseExpression("headers['bar']"); controller.setViewExpression(viewExpression); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); @@ -209,6 +215,7 @@ public class HttpRequestHandlingControllerTests { controller.setRequestChannel(requestChannel); controller.setViewName("foo"); controller.setReplyKey("myReply"); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("howdy".getBytes()); @@ -241,6 +248,7 @@ public class HttpRequestHandlingControllerTests { controller.setRequestChannel(requestChannel); controller.setViewName("foo"); controller.setExtractReplyPayload(false); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("abc".getBytes()); @@ -270,6 +278,7 @@ public class HttpRequestHandlingControllerTests { HttpRequestHandlingController controller = new HttpRequestHandlingController(false); controller.setBeanFactory(mock(BeanFactory.class)); controller.setRequestChannel(requestChannel); + controller.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); @@ -308,6 +317,7 @@ public class HttpRequestHandlingControllerTests { controller.setBeanFactory(mock(BeanFactory.class)); controller.setRequestChannel(requestChannel); controller.setViewName("foo"); + controller.afterPropertiesSet(); final MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContent("hello".getBytes()); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java index d9dccfe670..7a22860198 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java @@ -55,6 +55,7 @@ import org.springframework.util.SerializationUtils; * @author Gary Russell * @author Gunnar Hillert * @author Artem Bilan + * @author Biju Kunjummen * @since 2.0 */ public class HttpRequestHandlingMessagingGatewayTests { @@ -66,6 +67,7 @@ public class HttpRequestHandlingMessagingGatewayTests { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestChannel(requestChannel); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("GET"); request.setParameter("foo", "bar"); @@ -86,6 +88,7 @@ public class HttpRequestHandlingMessagingGatewayTests { gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestPayloadType(String.class); gateway.setRequestChannel(requestChannel); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -115,6 +118,7 @@ public class HttpRequestHandlingMessagingGatewayTests { gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestPayloadType(String.class); gateway.setRequestChannel(requestChannel); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.addHeader("Accept", "x-application/octet-stream"); @@ -142,6 +146,7 @@ public class HttpRequestHandlingMessagingGatewayTests { gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestPayloadType(String.class); gateway.setRequestChannel(requestChannel); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -170,6 +175,7 @@ public class HttpRequestHandlingMessagingGatewayTests { gateway.setRequestChannel(requestChannel); gateway.setConvertExceptions(true); gateway.setMessageConverters(Arrays.>asList(new TestHttpMessageConverter())); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "application/x-java-serialized-object"); request.setMethod("GET"); @@ -185,6 +191,7 @@ public class HttpRequestHandlingMessagingGatewayTests { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestChannel(channel); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); request.setParameter("foo", "123"); request.addParameter("bar", "456"); @@ -217,6 +224,7 @@ public class HttpRequestHandlingMessagingGatewayTests { List> converters = new ArrayList>(); converters.add(new SerializingHttpMessageConverter()); gateway.setMessageConverters(converters); + gateway.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test"); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index 83c792c1db..c5ceeb25ab 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -40,6 +40,7 @@ import org.springframework.mock.web.MockHttpServletResponse; * @author Gary Russell * @author Gunnar Hillert * @author Gary Russell + * @author Biju Kunjummen */ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { @@ -70,6 +71,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); + gateway.afterPropertiesSet(); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -102,6 +104,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); gateway.setPayloadExpression(PARSER.parseExpression("#pathVariables.f")); + gateway.afterPropertiesSet(); Object result = gateway.doHandleRequest(request, response); assertTrue(result instanceof Message); @@ -134,6 +137,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); gateway.setPayloadExpression(PARSER.parseExpression("#pathVariables")); + gateway.afterPropertiesSet(); Object result = gateway.doHandleRequest(request, response); assertTrue(result instanceof Message); diff --git a/src/reference/docbook/http.xml b/src/reference/docbook/http.xml index 58c32cbc41..5011079de3 100644 --- a/src/reference/docbook/http.xml +++ b/src/reference/docbook/http.xml @@ -62,7 +62,9 @@ customization of the mapping from HttpServletRequest to Message. The default converters encapsulate simple strategies, which for example will create a String message for a POST request where the content type starts with "text", see the Javadoc for - full details. + full details. An additional flag (mergeWithDefaultConverters) can be set along with the list of + custom HttpMessageConverter to add the default converters after the custom converters. + By default this flag is set to false, meaning that the custom converters replace the default list. Starting with this release MultiPart File support was implemented. If the request has been wrapped as a MultipartHttpServletRequest, when using the default converters, that request will be converted diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index a032b884e0..444a45bcde 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -233,7 +233,8 @@ Payloads to persist or merge can now be of type - java.lang.Iterable. + java.lang.Iterable. In that case, each object returned by the @@ -252,12 +253,33 @@ see 'JSON Transformers' in . -
- HTTP Outbound Endpoint 'encode-uri' property +
+ HTTP Endpoint Changes - <http:outbound-gateway/> and <http:outbound-channel-adapter/> now - provide an encode-uri attribute to allow disabling the encoding of the URI object - before sending the request. For more information see . + + + Outbound Endpoint 'encode-uri' - <http:outbound-gateway/> + and <http:outbound-channel-adapter/> now + provides an encode-uri attribute to allow disabling the encoding of the URI object + before sending the request. + + + Inbound Endpoint 'merge-with-default-converters' - + <http:inbound-gateway/> and <http:inbound-channel-adapter/> now + have a merge-with-default-converters attribute to include the list of default + HttpMessageConverter after the custom message converters. + + + 'If-(Un)Modified-Since' HTTP headers - previously, + 'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed + within from/to HTTP headers mapping in the DefaultHttpHeaderMapper. + Now, in addition correcting that issue, DefaultHttpHeaderMapper provides + date parsing from formatted strings for any HTTP headers that accept date-time values. + + + + + For more information see .
@@ -410,16 +432,6 @@ For more information see .
-
- DefaultHttpHeaderMapper and 'If-(Un)Modified-Since' HTTP headers - - Previously, 'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed - within from/to HTTP headers mapping in the DefaultHttpHeaderMapper. - Now, in addition to the fix of that issue, DefaultHttpHeaderMapper provides date parsing - from formatted strings for HTTP headers, which accept date-time values. - For more information see . - -
PublishSubscribeChannel Behavior