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<?>`
This commit is contained in:
Artem Bilan
2013-12-17 17:17:13 +02:00
parent 73a104c955
commit 64cc1faafe
5 changed files with 71 additions and 20 deletions

View File

@@ -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<String, ?> 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<String, Object> 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")

View File

@@ -36,6 +36,11 @@
message-converters="stringAndSerializingConverters"
expected-response-type-expression="payload"/>
<int-http:outbound-gateway url="http://localhost:#{portBean.port}/testApps/outboundResponse"
request-channel="invalidResponseTypeChannel"
expected-response-type-expression="new java.util.Date()"/>
<util:list id="stringAndSerializingConverters">
<bean class="org.springframework.integration.http.converter.SerializingHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />

View File

@@ -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<byte[]>("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 {

View File

@@ -22,7 +22,7 @@
<outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
rest-template="restTemplate"
encode-uri="false"
expected-response-type="java.lang.String">
expected-response-type-expression="T (org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests).testParameterizedTypeReference()">
<uri-variable name="param" expression="T(org.apache.commons.httpclient.util.URIUtil).encodeWithinQuery('http Outbound Gateway Within Chain')"/>
</outbound-gateway>
</si:chain>

View File

@@ -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<List<String>> testParameterizedTypeReference() {
return new ParameterizedTypeReference<List<String>>() {};
}
@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<List<String>>() {
}));
}
@Test
@@ -938,6 +945,12 @@ public class HttpRequestExecutingMessageHandlerTests {
Class<T> responseType) throws RestClientException {
return new ResponseEntity<T>(HttpStatus.OK);
}
@Override
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType) throws RestClientException {
return new ResponseEntity<T>(HttpStatus.OK);
}
}
private static class Foo implements Serializable {