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:
committed by
Gary Russell
parent
a9ec7c8107
commit
bb6aebc587
@@ -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");
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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><http:outbound-gateway/></code> and <code><http:outbound-channel-adapter/></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><http:outbound-gateway/></code>
|
||||
and <code><http:outbound-channel-adapter/></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><http:inbound-gateway/></code> and <code><http:inbound-channel-adapter/></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>
|
||||
|
||||
Reference in New Issue
Block a user