From 7ef66e2e3f84acd3e41d50632c5df71977a3ef05 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 28 Nov 2012 17:09:27 -0500 Subject: [PATCH] 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. --- .../SerializingHttpMessageConverter.java | 10 +- .../HttpRequestHandlingEndpointSupport.java | 2 - .../HttpRequestExecutingMessageHandler.java | 2 - .../HttpInboundChannelAdapterParserTests.java | 12 +- .../config/HttpInboundGatewayParserTests.java | 10 +- .../OutboundResponseTypeTests-context.xml | 17 ++- .../config/OutboundResponseTypeTests.java | 11 +- ...pRequestHandlingMessagingGatewayTests.java | 6 + ...tpRequestExecutingMessageHandlerTests.java | 106 ++++++++++++++++++ src/reference/docbook/whats-new.xml | 22 ++++ 10 files changed, 179 insertions(+), 19 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java index f613b8e5c62..f497cd9b848 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java @@ -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 @@ /** * An {@link HttpMessageConverter} implementation for {@link Serializable} instances. - * + * * @author Mark Fisher + * @author Gary Russell * @since 2.0 */ public class SerializingHttpMessageConverter extends AbstractHttpMessageConverter { @@ -51,11 +52,6 @@ public boolean supports(Class clazz) { 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)); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index 7e9d2325e37..9223f3b641b 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -54,7 +54,6 @@ 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 HttpRequestHandlingEndpointSupport() { 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()); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index 15a17846f1e..d76929d3e67 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -51,7 +51,6 @@ 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 HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) 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()); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java index 785ac664455..c01ecbbe90d 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java @@ -25,6 +25,7 @@ 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.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 @@ * @author Gary Russell * @author Gunnar Hillert * @author Artem Bilan + * @author Gary Russell */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -239,7 +244,7 @@ public void postRequestWithTextContentOk() throws Exception { assertEquals("test", message.getPayload()); } - @Test + @Test @DirtiesContext public void postRequestWithSerializedObjectContentOk() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -253,6 +258,11 @@ public void postRequestWithSerializedObjectContentOk() throws Exception { request.addHeader("Content-Type", "application/x-java-serialized-object"); MockHttpServletResponse response = new MockHttpServletResponse(); + + List> converters = new ArrayList>(); + converters.add(new SerializingHttpMessageConverter()); + postOnlyAdapter.setMessageConverters(converters); + postOnlyAdapter.handleRequest(request, response); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); Message message = requests.receive(0); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java index 0e9159642bf..0d47aca5b7e 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java @@ -25,6 +25,7 @@ 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.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 void checkConfig() { 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 void checkFlow() throws Exception { request.setParameter("foo", "bar"); MockHttpServletResponse response = new MockHttpServletResponse(); + List> converters = new ArrayList>(); + converters.add(new SerializingHttpMessageConverter()); + gateway.setMessageConverters(converters); + gateway.handleRequest(request, response); assertThat(response.getStatus(), is(HttpServletResponse.SC_OK)); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests-context.xml index 74c1768ed95..4dcd7146161 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests-context.xml @@ -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"> + + + + + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests.java index 3b6f72330bf..080e54d1ea4 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/OutboundResponseTypeTests.java @@ -26,11 +26,9 @@ 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.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 @@ /** * @author Oleg Zhurakousky * @author Artem Bilan + * @author Gary Russell * @since 2.2 * *

@@ -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 void testWithResponseTypeExpressionSet() throws Exception { @Test public void testWithResponseTypeExpressionSetAsClass() throws Exception { - this.resTypeExpressionSetChannel.send(new GenericMessage>(String.class)); + this.resTypeExpressionSetSerializationChannel.send(new GenericMessage>(String.class)); Message message = this.replyChannel.receive(5000); assertNotNull(message); assertTrue(message.getPayload() instanceof String); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java index 6fa61090e55..9d87c1fe39f 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java @@ -39,6 +39,7 @@ 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 void serializableRequestBody() throws Exception { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); gateway.setRequestPayloadType(TestBean.class); gateway.setRequestChannel(channel); + + List> converters = new ArrayList>(); + 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 diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index 0dfacb0a10e..94b2e13ce8a 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java @@ -21,9 +21,13 @@ 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.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 Object invoke(MethodInvocation invocation) throws Throwable { 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> converters = new ArrayList>(); + 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 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> converters = new ArrayList>(); + 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 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 ResponseEntity exchange(String url, HttpMethod method, HttpEntity< } } + private static class Foo implements Serializable { + + private static final long serialVersionUID = 1L; + + } } diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index c44398422d7..38a2f41a00f 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -190,6 +190,28 @@ by default. For more information see . +

+ Http Support + + Java serialization over http is no longer enabled by default. Previously, when + setting a expected-response-type to a Serializable + object, the Accept header was not properly set up. The + SerializingHttpMessageConverter has now been updated + to set the Accept header to application/x-java-serialized-object. + However, because this could cause incompatibility with existing applications, + it was decided to no longer automatically add this converter to the http endpoints. + + + If you wish to use Java serialization, you will need to add the + SerializingHttpMessageConverter to the appropriate + endpoints, using the message-converters attribute, when using + XML configuration, or using the setMessageConverters() method. + + + Alternatively, you may wish to consider using JSON instead which is enabled + by simply having Jackson on the classpath. + +