From 64cc1faafe5592c851622cba68394a2eed7f9c5d Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 17 Dec 2013 17:17:13 +0200 Subject: [PATCH] INT-3052 RestTemplate with TypeReference for HTTP JIRA: https://jira.springsource.org/browse/INT-3052 Add support of `ParameterizedTypeReference` as a result of `expectedResponseTypeExpression` evaluation for `RestTemplate.exchange` with `ParameterizedTypeReference` parameter Now `expectedResponseTypeExpression` can be evaluated to `Class`, `String` and `ParameterizedTypeReference` --- .../HttpRequestExecutingMessageHandler.java | 33 ++++++++++++------- .../OutboundResponseTypeTests-context.xml | 5 +++ .../config/OutboundResponseTypeTests.java | 26 +++++++++++++-- .../HttpOutboundWithinChainTests-context.xml | 2 +- ...tpRequestExecutingMessageHandlerTests.java | 25 ++++++++++---- 5 files changed, 71 insertions(+), 20 deletions(-) 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 c6a1b127bd..860c160743 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 @@ -28,6 +28,7 @@ import java.util.Map; import javax.xml.transform.Source; import org.springframework.beans.factory.BeanFactory; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterRegistry; @@ -44,7 +45,6 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.messaging.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.expression.ExpressionEvalMap; import org.springframework.integration.expression.ExpressionUtils; @@ -52,8 +52,9 @@ import org.springframework.integration.handler.AbstractReplyProducingMessageHand import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; -import org.springframework.messaging.MessagingException; +import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -363,13 +364,19 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe } } - Class expectedResponseType = this.determineExpectedResponseType(requestMessage); + Object expectedResponseType = this.determineExpectedResponseType(requestMessage); HttpEntity httpRequest = this.generateHttpRequest(requestMessage, httpMethod); Map uriVariables = this.determineUriVariables(requestMessage); UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables); URI realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString()); - ResponseEntity httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, expectedResponseType); + ResponseEntity httpResponse; + if (expectedResponseType instanceof ParameterizedTypeReference) { + httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, (ParameterizedTypeReference) expectedResponseType); + } + else { + httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, (Class) expectedResponseType); + } if (this.expectReply) { HttpHeaders httpHeaders = httpResponse.getHeaders(); Map headers = this.headerMapper.toHeaders(httpHeaders); @@ -565,17 +572,21 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe return HttpMethod.valueOf(strHttpMethod); } - private Class determineExpectedResponseType(Message requestMessage) throws Exception{ - Class expectedResponseType = null; - String expectedResponseTypeName = null; + private Object determineExpectedResponseType(Message requestMessage) throws Exception{ + Object expectedResponseType = null; if (this.expectedResponseTypeExpression != null){ - expectedResponseTypeName = this.expectedResponseTypeExpression.getValue(this.evaluationContext, requestMessage, String.class); + expectedResponseType = this.expectedResponseTypeExpression.getValue(this.evaluationContext, requestMessage); } - if (StringUtils.hasText(expectedResponseTypeName)){ - expectedResponseType = ClassUtils.forName(expectedResponseTypeName, ClassUtils.getDefaultClassLoader()); + if (expectedResponseType != null) { + Assert.isTrue(expectedResponseType instanceof Class + || expectedResponseType instanceof String + || expectedResponseType instanceof ParameterizedTypeReference, + "'expectedResponseType' can be an instance of 'Class', 'String' or 'ParameterizedTypeReference'."); + if (expectedResponseType instanceof String && StringUtils.hasText((String) expectedResponseType)){ + expectedResponseType = ClassUtils.forName((String) expectedResponseType, ClassUtils.getDefaultClassLoader()); + } } return expectedResponseType; - } @SuppressWarnings("unchecked") 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 a50115a3ae..18890c47e0 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 @@ -36,6 +36,11 @@ message-converters="stringAndSerializingConverters" expected-response-type-expression="payload"/> + + + 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 8dd0f46892..fa3c640cf7 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 @@ -16,6 +16,7 @@ package org.springframework.integration.http.config; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -23,21 +24,24 @@ import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; + 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; import org.springframework.http.ResponseEntity; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.test.util.SocketUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; -import org.springframework.integration.channel.QueueChannel; import org.springframework.messaging.support.GenericMessage; -import org.springframework.integration.test.util.SocketUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -80,6 +84,9 @@ public class OutboundResponseTypeTests { @Autowired private MessageChannel resTypeExpressionSetSerializationChannel; + @Autowired + private MessageChannel invalidResponseTypeChannel; + private static int port = SocketUtils.findAvailableServerSocket(); @BeforeClass @@ -143,6 +150,21 @@ public class OutboundResponseTypeTests { assertTrue(message.getPayload() instanceof byte[]); } + @Test + public void testInt3052InvalidResponseType() throws Exception { + try { + this.invalidResponseTypeChannel.send(new GenericMessage("hello".getBytes())); + fail("IllegalArgumentException expected."); + } + catch (Exception e) { + assertThat(e, Matchers.instanceOf(MessageHandlingException.class)); + Throwable t = e.getCause(); + assertThat(t, Matchers.instanceOf(IllegalArgumentException.class)); + assertThat(t.getMessage(), + Matchers.containsString("'expectedResponseType' can be an instance of 'Class', 'String' or 'ParameterizedTypeReference'")); + } + } + @Test public void testMutuallyExclusivityInMethodAndMethodExpression() throws Exception { try { diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml index a4ca379b94..e18a4c2d8d 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml @@ -22,7 +22,7 @@ + expected-response-type-expression="T (org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests).testParameterizedTypeReference()"> 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 961ef7551d..0f8adfd45b 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 @@ -52,6 +52,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -65,14 +66,14 @@ 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.messaging.Message; -import org.springframework.messaging.MessageChannel; import org.springframework.integration.channel.QueueChannel; -import org.springframework.messaging.PollableChannel; import org.springframework.integration.http.converter.SerializingHttpMessageConverter; -import org.springframework.messaging.support.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.PollableChannel; +import org.springframework.messaging.support.GenericMessage; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; @@ -86,6 +87,10 @@ import org.springframework.web.client.RestTemplate; */ public class HttpRequestExecutingMessageHandlerTests { + public static ParameterizedTypeReference> testParameterizedTypeReference() { + return new ParameterizedTypeReference>() {}; + } + @Test public void simpleStringKeyStringValueFormData() throws Exception { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration"); @@ -726,10 +731,12 @@ public class HttpRequestExecutingMessageHandlerTests { PollableChannel output = ctx.getBean("replyChannel", PollableChannel.class); Message receive = output.receive(); - assertEquals(HttpStatus.OK, ((ResponseEntity)receive.getPayload()).getStatusCode()); + assertEquals(HttpStatus.OK, ((ResponseEntity) receive.getPayload()).getStatusCode()); Mockito.verify(restTemplate) .exchange(Mockito.eq(new URI("http://localhost:51235/%2f/testApps?param=http%20Outbound%20Gateway%20Within%20Chain")), - Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(String.class)); + Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(new ParameterizedTypeReference>() { + + })); } @Test @@ -938,6 +945,12 @@ public class HttpRequestExecutingMessageHandlerTests { Class responseType) throws RestClientException { return new ResponseEntity(HttpStatus.OK); } + + @Override + public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, + ParameterizedTypeReference responseType) throws RestClientException { + return new ResponseEntity(HttpStatus.OK); + } } private static class Foo implements Serializable {