INT-2837 Fix Accept Hdr for Serializable Response

When the expected-response-type was Serializable,
no Accept: header was added.

The SerializingHttpMessageConverter overrides
canRead() from the superclass.

The superclass method allows null in the mediaType
parameter to return true. RestTemplate.AcceptHeaderRequestCallback
calls canRead() with null in the mediaType, preventing
the Accept header from being completed.

Remove the overridden method.

INT-2837 Disable Java Serialization over Http

Previously, Java serialization was enabled by default by
adding the SerializingHttpMessageConverter to the converter
lists (inbound and outbound endpoints). However, there was
a problem in that the Accept header was not set properly on
outbound requests.

Correcting the header could cause other side effects, such as
existing JSON applications using Java serialization instead.

It was felt that it would be safest to disable Java serialization
by default, while retaining the ability to use Java serialization
by explicitly configuring the message converter.

Update tests to deal with the fact the converter is not
configured by default.

INT-2837 Fix Schema Version in Test

Inadvertently added a spring 3.1 schema version.
This commit is contained in:
Gary Russell
2012-11-28 17:09:27 -05:00
committed by Mark Fisher
parent 4ce08ad6dc
commit 7ef66e2e3f
10 changed files with 179 additions and 19 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@@ -31,8 +31,9 @@ import org.springframework.util.FileCopyUtils;
/**
* An {@link HttpMessageConverter} implementation for {@link Serializable} instances.
*
*
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class SerializingHttpMessageConverter extends AbstractHttpMessageConverter<Serializable> {
@@ -51,11 +52,6 @@ public class SerializingHttpMessageConverter extends AbstractHttpMessageConverte
return Serializable.class.isAssignableFrom(clazz);
}
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return (Serializable.class.isAssignableFrom(clazz) && APPLICATION_JAVA_SERIALIZED_OBJECT.includes(mediaType));
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return (Serializable.class.isAssignableFrom(clazz) && APPLICATION_JAVA_SERIALIZED_OBJECT.includes(mediaType));

View File

@@ -54,7 +54,6 @@ import org.springframework.integration.context.OrderlyShutdownCapable;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.gateway.MessagingGatewaySupport;
import org.springframework.integration.http.converter.MultipartAwareFormHttpMessageConverter;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.http.multipart.MultipartHttpInputMessage;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.mapping.HeaderMapper;
@@ -147,7 +146,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
public HttpRequestHandlingEndpointSupport(boolean expectReply) {
this.expectReply = expectReply;
this.messageConverters.add(new MultipartAwareFormHttpMessageConverter());
this.messageConverters.add(new SerializingHttpMessageConverter());
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());

View File

@@ -51,7 +51,6 @@ import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.support.MessageBuilder;
@@ -152,7 +151,6 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
public HttpRequestExecutingMessageHandler(Expression uriExpression, RestTemplate restTemplate) {
Assert.notNull(uriExpression, "URI Expression is required");
this.restTemplate = (restTemplate == null ? new RestTemplate() : restTemplate);
this.restTemplate.getMessageConverters().add(0, new SerializingHttpMessageConverter());
this.uriExpression = uriExpression;
StandardEvaluationContext sec = new StandardEvaluationContext();
sec.addPropertyAccessor(new MapAccessor());

View File

@@ -25,6 +25,7 @@ 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;
@@ -40,16 +41,19 @@ import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.http.inbound.HttpRequestHandlingController;
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.MultiValueMap;
@@ -60,6 +64,7 @@ import org.springframework.util.MultiValueMap;
* @author Gary Russell
* @author Gunnar Hillert
* @author Artem Bilan
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@@ -239,7 +244,7 @@ public class HttpInboundChannelAdapterParserTests {
assertEquals("test", message.getPayload());
}
@Test
@Test @DirtiesContext
public void postRequestWithSerializedObjectContentOk() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
@@ -253,6 +258,11 @@ public class HttpInboundChannelAdapterParserTests {
request.addHeader("Content-Type", "application/x-java-serialized-object");
MockHttpServletResponse response = new MockHttpServletResponse();
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
postOnlyAdapter.setMessageConverters(converters);
postOnlyAdapter.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Message<?> message = requests.receive(0);

View File

@@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
import static org.springframework.integration.test.util.TestUtils.handlerExpecting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -40,17 +41,20 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.http.HttpHeaders;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.http.inbound.HttpRequestHandlingController;
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -103,7 +107,7 @@ public class HttpInboundGatewayParserTests {
assertEquals(Long.valueOf(4567), TestUtils.getPropertyValue(messagingTemplate, "receiveTimeout"));
}
@Test(timeout=1000)
@Test(timeout=1000) @DirtiesContext
public void checkFlow() throws Exception {
requests.subscribe(handlerExpecting(any(Message.class)));
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -112,6 +116,10 @@ public class HttpInboundGatewayParserTests {
request.setParameter("foo", "bar");
MockHttpServletResponse response = new MockHttpServletResponse();
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
gateway.setMessageConverters(converters);
gateway.handleRequest(request, response);
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));

View File

@@ -3,9 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.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">
<int-http:outbound-gateway url="http://localhost:51235/testApps/outboundResponse"
@@ -27,6 +29,17 @@
reply-channel="replyChannel"
expected-response-type-expression="payload"/>
<int-http:outbound-gateway url="http://localhost:51235/testApps/outboundResponse"
request-channel="resTypeExpressionSetSerializationChannel"
reply-channel="replyChannel"
message-converters="stringAndSerializingConverters"
expected-response-type-expression="payload"/>
<util:list id="stringAndSerializingConverters">
<bean class="org.springframework.integration.http.converter.SerializingHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
</util:list>
<int:channel id="replyChannel">
<int:queue/>
</int:channel>

View File

@@ -26,11 +26,9 @@ import java.net.InetSocketAddress;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.http.MediaType;
@@ -39,6 +37,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sun.net.httpserver.HttpExchange;
@@ -48,6 +47,7 @@ import com.sun.net.httpserver.HttpServer;
/**
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @since 2.2
*
* <p/>
@@ -76,6 +76,9 @@ public class OutboundResponseTypeTests {
@Autowired
private MessageChannel resTypeExpressionSetChannel;
@Autowired
private MessageChannel resTypeExpressionSetSerializationChannel;
@BeforeClass
public static void createServer() throws Exception {
httpHandler = new MyHandler();
@@ -115,7 +118,7 @@ public class OutboundResponseTypeTests {
@Test
public void testWithResponseTypeExpressionSetAsClass() throws Exception {
this.resTypeExpressionSetChannel.send(new GenericMessage<Class<?>>(String.class));
this.resTypeExpressionSetSerializationChannel.send(new GenericMessage<Class<?>>(String.class));
Message<?> message = this.replyChannel.receive(5000);
assertNotNull(message);
assertTrue(message.getPayload() instanceof String);

View File

@@ -39,6 +39,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -198,6 +199,11 @@ public class HttpRequestHandlingMessagingGatewayTests {
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false);
gateway.setRequestPayloadType(TestBean.class);
gateway.setRequestChannel(channel);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
gateway.setMessageConverters(converters);
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test");
//request.setContentType("application/x-java-serialized-object"); //Works in Spring 3.1.2.RELEASE but NOT in 3.0.7.RELEASE

View File

@@ -21,9 +21,13 @@ import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -47,14 +51,21 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.util.MultiValueMap;
@@ -735,6 +746,96 @@ public class HttpRequestExecutingMessageHandlerTests {
assertSame(mockConversionService, TestUtils.getPropertyValue(handler, "conversionService"));
}
@Test
public void acceptHeaderForSerializableResponse() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
handler.setHttpMethod(HttpMethod.GET);
handler.setExpectedResponseType(Foo.class);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
handler.setMessageConverters(converters);
handler.afterPropertiesSet();
RestTemplate restTemplate = TestUtils.getPropertyValue(handler, "restTemplate", RestTemplate.class);
HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate);
Message<?> message = MessageBuilder.withPayload("foo").build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertTrue(requestHeaders.getAccept() != null);
assertTrue(requestHeaders.getAccept().size() > 0);
assertEquals("404 null", exception.getCause().getMessage());
List<MediaType> accept = requestHeaders.getAccept();
assertTrue(accept != null && accept.size() > 0);
assertEquals("application", accept.get(0).getType());
assertEquals("x-java-serialized-object", accept.get(0).getSubtype());
}
@Test
public void acceptHeaderForSerializableResponseMessageExchange() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
handler.setHttpMethod(HttpMethod.GET);
handler.setExtractPayload(false);
handler.setExpectedResponseType(GenericMessage.class);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new SerializingHttpMessageConverter());
handler.setMessageConverters(converters);
handler.afterPropertiesSet();
RestTemplate restTemplate = TestUtils.getPropertyValue(handler, "restTemplate", RestTemplate.class);
HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate);
Message<?> message = MessageBuilder.withPayload("foo").build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertTrue(requestHeaders.getAccept() != null);
assertTrue(requestHeaders.getAccept().size() > 0);
assertEquals("404 null", exception.getCause().getMessage());
List<MediaType> accept = requestHeaders.getAccept();
assertTrue(accept != null && accept.size() > 0);
assertEquals("application", accept.get(0).getType());
assertEquals("x-java-serialized-object", accept.get(0).getSubtype());
}
private HttpHeaders setUpMocksToCaptureSentHeaders(RestTemplate restTemplate) throws IOException {
HttpHeaders headers = new HttpHeaders();
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
ClientHttpRequest clientRequest = mock(ClientHttpRequest.class);
when(clientRequest.getHeaders()).thenReturn(headers);
when(requestFactory.createRequest(any(URI.class), any(HttpMethod.class)))
.thenReturn(clientRequest );
ClientHttpResponse response = mock(ClientHttpResponse.class);
when(response.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);
HttpHeaders responseHeaders = new HttpHeaders();
when(response.getHeaders()).thenReturn(responseHeaders);
when(clientRequest.execute()).thenReturn(response);
restTemplate.setRequestFactory(requestFactory);
return headers;
}
public static class City{
private final String name;
public City(String name){
@@ -770,4 +871,9 @@ public class HttpRequestExecutingMessageHandlerTests {
}
}
private static class Foo implements Serializable {
private static final long serialVersionUID = 1L;
}
}

View File

@@ -190,6 +190,28 @@
by default. For more information see <xref linkend="transformer"/>.
</para>
</section>
<section id="httpChanges">
<title>Http Support</title>
<para>
Java serialization over http is no longer enabled by default. Previously, when
setting a <code>expected-response-type</code> to a <code>Serializable</code>
object, the <code>Accept</code> header was not properly set up. The
<classname>SerializingHttpMessageConverter</classname> has now been updated
to set the Accept header to <code>application/x-java-serialized-object</code>.
However, because this could cause incompatibility with existing applications,
it was decided to no longer automatically add this converter to the http endpoints.
</para>
<para>
If you wish to use Java serialization, you will need to add the
<classname>SerializingHttpMessageConverter</classname> to the appropriate
endpoints, using the <code>message-converters</code> attribute, when using
XML configuration, or using the <code>setMessageConverters()</code> method.
</para>
<para>
Alternatively, you may wish to consider using JSON instead which is enabled
by simply having <code>Jackson</code> on the classpath.
</para>
</section>
</section>
<section id="2.2-new-components">