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:
@@ -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")
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user