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.
This commit is contained in:
Biju Kunjummen
2013-09-13 20:45:10 -04:00
committed by Gary Russell
parent a9ec7c8107
commit bb6aebc587
12 changed files with 287 additions and 58 deletions

View File

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

View File

@@ -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<HttpMessageConverter<?>> defaultMessageConverters = new ArrayList<HttpMessageConverter<?>>();
private volatile List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
private volatile List<HttpMethod> supportedMethods = Arrays.asList(HttpMethod.GET, HttpMethod.POST);
private volatile Class<?> requestPayloadType = null;
private volatile List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
private volatile boolean convertersMerged;
private volatile boolean mergeWithDefaultConverters = false;
private volatile HeaderMapper<HttpHeaders> 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<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
Assert.noNullElements(messageConverters.toArray(), "'messageConverters' must not contain null entries");
List<HttpMessageConverter<?>> localConverters = new ArrayList<HttpMessageConverter<?>>(messageConverters);
if (this.mergeWithDefaultConverters) {
localConverters.addAll(this.defaultMessageConverters);
this.convertersMerged = true;
}
this.messageConverters = localConverters;
}
protected List<HttpMessageConverter<?>> 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
*/

View File

@@ -129,6 +129,17 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="merge-with-default-converters" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation>
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"
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="header-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -287,6 +298,17 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="merge-with-default-converters" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation>
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"
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="header-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>

View File

@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
<beans:beans
xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
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">
<si:message-history/>
@@ -20,6 +20,18 @@
<inbound-channel-adapter id="postOnlyAdapter" channel="requests" supported-methods="POST"/>
<inbound-channel-adapter id="adapterWithCustomConverterWithDefaults" message-converters="customConverters"
channel="requests" supported-methods="POST" merge-with-default-converters="true"/>
<inbound-channel-adapter id="adapterWithCustomConverterNoDefaults" message-converters="customConverters"
channel="requests" supported-methods="POST" />
<inbound-channel-adapter id="adapterNoCustomConverterNoDefaults" channel="requests" supported-methods="POST" />
<util:list id="customConverters">
<beans:bean class="org.springframework.integration.http.converter.SerializingHttpMessageConverter"/>
</util:list>
<inbound-channel-adapter id="putOrDeleteAdapter" channel="requests" supported-methods="PUT, delete"/>
<inbound-channel-adapter id="inboundController" channel="requests" view-name="foo" error-code="oops"/>

View File

@@ -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<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
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<HttpMessageConverter<?>> 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<HttpMessageConverter<?>> 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<HttpMessageConverter<?>> 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 {

View File

@@ -26,6 +26,28 @@
reply-timeout="4567"
error-channel="errorChannel"/>
<inbound-gateway id="inboundGatewayWithOneCustomConverter"
request-channel="requests"
reply-channel="responses"
message-converters="customConverters"/>
<inbound-gateway id="inboundGatewayNoDefaultConverters"
request-channel="requests"
reply-channel="responses"
message-converters="customConverters"
merge-with-default-converters="false"/>
<inbound-gateway id="inboundGatewayWithCustomAndDefaultConverters"
request-channel="requests"
reply-channel="responses"
message-converters="customConverters"
merge-with-default-converters="true"/>
<util:list id="customConverters">
<beans:bean class="org.springframework.integration.http.converter.SerializingHttpMessageConverter"/>
</util:list>
<inbound-gateway id="inboundController" request-channel="requests" reply-channel="responses" view-name="foo" error-code="oops"/>
<inbound-gateway id="inboundControllerViewExp"
@@ -37,15 +59,14 @@
<inbound-gateway id="withMappedHeaders" request-channel="requests"
mapped-response-headers="abc, xyz"
mapped-request-headers="foo,bar"/>
<inbound-gateway id="withMappedHeadersAndConverter" request-channel="requests"
mapped-response-headers="abc, xyz, person"
mapped-request-headers="foo,bar"/>
<si:converter ref="personConverter"/>
<beans:bean id="personConverter" class="org.springframework.integration.http.config.HttpInboundGatewayParserTests.PersonConverter"/>
</beans:beans>

View File

@@ -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<HttpMessageConverter<?>> 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<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
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<HttpMessageConverter<?>> 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<HttpMessageConverter<?>> 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<HttpMessageConverter<?>> 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;

View File

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

View File

@@ -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.<HttpMessageConverter<?>>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<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
gateway.setMessageConverters(converters);
gateway.afterPropertiesSet();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test");

View File

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

View File

@@ -62,7 +62,9 @@
customization of the mapping from <interfacename>HttpServletRequest</interfacename> to <interfacename>Message</interfacename>. The default converters
encapsulate simple strategies, which for
example will create a String message for a <emphasis>POST</emphasis> request where the content type starts with "text", see the Javadoc for
full details.
full details. An additional flag (<code>mergeWithDefaultConverters</code>) can be set along with the list of
custom <interfacename>HttpMessageConverter</interfacename> 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.
</para>
<para>Starting with this release MultiPart File support was implemented. If the request has been wrapped as a
<emphasis>MultipartHttpServletRequest</emphasis>, when using the default converters, that request will be converted

View File

@@ -233,7 +233,8 @@
<para>
Payloads to <emphasis>persist</emphasis> or
<emphasis>merge</emphasis> can now be of type
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">java.lang.Iterable</ulink></interfacename>.
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"
>java.lang.Iterable</ulink></interfacename>.
</para>
<para>
In that case, each object returned by the
@@ -252,12 +253,33 @@
see 'JSON Transformers' in <xref linkend="transformer"/>.
</para>
</section>
<section id="3.0-http-encode-uri">
<title>HTTP Outbound Endpoint 'encode-uri' property</title>
<section id="3.0-http-endpointss">
<title>HTTP Endpoint Changes</title>
<para>
<code>&lt;http:outbound-gateway/&gt;</code> and <code>&lt;http:outbound-channel-adapter/&gt;</code> now
provide an <code>encode-uri</code> attribute to allow disabling the encoding of the URI object
before sending the request. For more information see <xref linkend="http"/>.
<itemizedlist>
<listitem>
<emphasis role="bold">Outbound Endpoint 'encode-uri'</emphasis> - <code>&lt;http:outbound-gateway/&gt;</code>
and <code>&lt;http:outbound-channel-adapter/&gt;</code> now
provides an <code>encode-uri</code> attribute to allow disabling the encoding of the URI object
before sending the request.
</listitem>
<listitem>
<emphasis role="bold">Inbound Endpoint 'merge-with-default-converters'</emphasis> -
<code>&lt;http:inbound-gateway/&gt;</code> and <code>&lt;http:inbound-channel-adapter/&gt;</code> now
have a <code>merge-with-default-converters</code> attribute to include the list of default
<interfacename>HttpMessageConverter</interfacename> after the custom message converters.
</listitem>
<listitem>
<emphasis role="bold">'If-(Un)Modified-Since' HTTP headers</emphasis> - previously,
'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed
within from/to HTTP headers mapping in the <classname>DefaultHttpHeaderMapper</classname>.
Now, in addition correcting that issue, <classname>DefaultHttpHeaderMapper</classname> provides
date parsing from formatted strings for any HTTP headers that accept date-time values.
</listitem>
</itemizedlist>
</para>
<para>
For more information see <xref linkend="http"/>.
</para>
</section>
<section id="3.0-amqp-mapping">
@@ -410,16 +432,6 @@
For more information see <xref linkend="delayer"/>.
</para>
</section>
<section id="3.0-http-header-mapper">
<title>DefaultHttpHeaderMapper and 'If-(Un)Modified-Since' HTTP headers</title>
<para>
Previously, 'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed
within from/to HTTP headers mapping in the <classname>DefaultHttpHeaderMapper</classname>.
Now, in addition to the fix of that issue, <classname>DefaultHttpHeaderMapper</classname> provides date parsing
from formatted strings for HTTP headers, which accept date-time values.
For more information see <xref linkend="http"/>.
</para>
</section>
<section id="3.0-pub-sub">
<title>PublishSubscribeChannel Behavior</title>
<para>