Skip to content
Browse files

INT-2455: HTTP outbound 'encode-uri' flag support

Now `<http:outbound-channel-adapter>` and `<http:outbound-gateway>` provide
`encode-uri="false"` option do not encode request uri.
It can be useful in some scenarios with non standard URL, e.g. RabbitMQ REST API:
'http://foo.RabbitMQ.com/api/queues/%2f/bar.queue'

https://jira.springsource.org/browse/INT-2455
  • Loading branch information...
1 parent 9efb70f commit 10177086b1190384af21ce93a3da3ad5205cc263 @artembilan committed Feb 26, 2013
View
5 ...in/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -32,6 +32,7 @@
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
+ * @author Artem Bilan
* @since 2.0
*/
public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@@ -42,6 +43,8 @@ protected AbstractBeanDefinition parseConsumer(Element element, ParserContext pa
builder.addPropertyValue("expectReply", false);
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
+
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
String restTemplate = element.getAttribute("rest-template");
View
5 .../src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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,6 +31,7 @@
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
+ * @author Artem Bilan
*/
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -45,6 +46,8 @@ protected BeanDefinitionBuilder parseHandler(Element element, ParserContext pars
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
+
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
String restTemplate = element.getAttribute("rest-template");
View
20 ...ava/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -62,6 +62,8 @@
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
/**
* A {@link MessageHandler} implementation that executes HTTP requests by delegating
@@ -90,6 +92,8 @@
private final Expression uriExpression;
+ private volatile boolean encodeUri = true;
+
private volatile Expression httpMethodExpression = new LiteralExpression(HttpMethod.POST.name());
private volatile boolean expectReply = true;
@@ -158,6 +162,16 @@ public HttpRequestExecutingMessageHandler(Expression uriExpression, RestTemplate
}
/**
+ * Specify whether the real URI should be encoded after <code>uriVariables</code>
+ * expanding and before send request via {@link RestTemplate}. The default value is <code>true</code>.
+ *
+ * @see UriComponentsBuilder
+ */
+ public void setEncodeUri(boolean encodeUri) {
+ this.encodeUri = encodeUri;
+ }
+
+ /**
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} dynamically
*
* @param httpMethodExpression
@@ -348,7 +362,9 @@ protected Object handleRequestMessage(Message<?> requestMessage) {
Class<?> expectedResponseType = this.determineExpectedResponseType(requestMessage);
HttpEntity<?> httpRequest = this.generateHttpRequest(requestMessage, httpMethod);
- ResponseEntity<?> httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, expectedResponseType, uriVariables);
+ UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(uri).buildAndExpand(uriVariables);
+ URI realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString());
+ ResponseEntity<?> httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, expectedResponseType);
if (this.expectReply) {
HttpHeaders httpHeaders = httpResponse.getHeaders();
Map<String, Object> headers = this.headerMapper.toHeaders(httpHeaders);
View
18 ...ain/resources/org/springframework/integration/http/config/spring-integration-http-3.0.xsd
@@ -390,6 +390,15 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="encode-uri" type="xsd:string" default="true">
+ <xsd:annotation>
+ <xsd:documentation>
+ When set to "false", the real URI won't be encoded before send request. It may be useful
+ in some scenarios. It allows to make some partial encoding manually, if needed,
+ e.g. through "url-expression". Default is "true".
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="http-method">
<xsd:annotation>
<xsd:documentation>
@@ -560,6 +569,15 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="encode-uri" type="xsd:string" default="true">
+ <xsd:annotation>
+ <xsd:documentation>
+ When set to "false", the real URI won't be encoded before send request. It may be useful
+ in some scenarios. It allows to make some partial encoding manually, if needed,
+ e.g. through "url-expression". Default is "true".
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="http-method">
<xsd:annotation>
<xsd:documentation>
View
15 ...va/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml
@@ -9,15 +9,22 @@
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<si:chain input-channel="httpOutboundChannelAdapterWithinChain">
- <outbound-channel-adapter url="http://localhost/test1" rest-template="restTemplate"/>
+ <outbound-channel-adapter url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"/>
</si:chain>
- <beans:bean id="restTemplate" class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
+ <beans:bean id="restTemplate" class="org.mockito.Mockito" factory-method="spy">
+ <beans:constructor-arg>
+ <beans:bean class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
+ </beans:constructor-arg>
+ </beans:bean>
<si:chain input-channel="httpOutboundGatewayWithinChain" output-channel="replyChannel">
- <outbound-gateway url="http://localhost:51235/testApps/httpOutboundGatewayWithinChain"
+ <outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
rest-template="restTemplate"
- expected-response-type="java.lang.String"/>
+ encode-uri="false"
+ expected-response-type="java.lang.String">
+ <uri-variable name="param" expression="T(org.apache.commons.httpclient.util.URIUtil).encodeWithinQuery('http Outbound Gateway Within Chain')"/>
+ </outbound-gateway>
</si:chain>
<si:channel id="replyChannel">
View
42 ...rg/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 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.
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -42,6 +43,7 @@
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
+import org.mockito.Mockito;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -672,23 +674,28 @@ public void contentTypeIsNotSetForGetRequest() throws Exception {
}
@Test //INT-2275
- public void testOutboundChannelAdapterWithinChain() {
+ public void testOutboundChannelAdapterWithinChain() throws URISyntaxException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("HttpOutboundWithinChainTests-context.xml", this.getClass());
MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class);
+ RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
channel.send(MessageBuilder.withPayload("test").build());
-// It's just enough if it was sent successfully from chain without any failures
+ Mockito.verify(restTemplate).exchange(Mockito.eq(new URI("http://localhost/test1/%2f")), Mockito.eq(HttpMethod.POST),
+ Mockito.any(HttpEntity.class), Mockito.eq((Class<?>) null));
}
@Test //INT-1029
- public void testHttpOutboundGatewayWithinChain() throws IOException {
+ public void testHttpOutboundGatewayWithinChain() throws IOException, URISyntaxException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("HttpOutboundWithinChainTests-context.xml", this.getClass());
MessageChannel channel = ctx.getBean("httpOutboundGatewayWithinChain", MessageChannel.class);
+ RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
channel.send(MessageBuilder.withPayload("test").build());
PollableChannel output = ctx.getBean("replyChannel", PollableChannel.class);
Message<?> receive = output.receive();
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));
}
@Test
@@ -707,6 +714,21 @@ public void testUriExpression() {
}
@Test
+ public void testInt2455UriNotEncoded() {
+ MockRestTemplate restTemplate = new MockRestTemplate();
+ HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
+ new SpelExpressionParser().parseExpression("'http://my.RabbitMQ.com/api/' + payload"),
+ restTemplate);
+ handler.setEncodeUri(false);
+ Message<?> message = MessageBuilder.withPayload("queues/%2f/si.test.queue").build();
+ try {
+ handler.handleRequestMessage(message);
+ }
+ catch (Exception e) {}
+ assertEquals("http://my.RabbitMQ.com/api/queues/%2f/si.test.queue", restTemplate.actualUrl.get());
+ }
+
+ @Test
public void nonCompatibleConversionService() throws Exception {
HttpRequestExecutingMessageHandler handler =
new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -856,9 +878,9 @@ public String toString(){
private final AtomicReference<String> actualUrl = new AtomicReference<String>();
@Override
- public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
- Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
- this.actualUrl.set(url);
+ public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
+ Class<T> responseType) throws RestClientException {
+ this.actualUrl.set(uri.toString());
this.lastRequestEntity.set(requestEntity);
throw new RuntimeException("intentional");
}
@@ -868,8 +890,8 @@ public String toString(){
private static class MockRestTemplate2 extends RestTemplate {
@Override
- public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
- Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
+ public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
+ Class<T> responseType) throws RestClientException {
return new ResponseEntity<T>(HttpStatus.OK);
}
}

0 comments on commit 1017708

Please sign in to comment.
Something went wrong with that request. Please try again.