INT-2570 Support url-expression on Outbound Http

Add url-expression as an alternative to url for outbound
http adapters. One of url or url-expression must be
provided (but not both).

The Message is the root object for the expression's
evaluation context; the bean factory can also be
used to resolve '@beanName' expressions.

INT-2570 url-expression Reference Docs

Add note to the reference guide.

INT-2570 Polishing

PR Review Comments

INT-2570 Polishing

PR Review comments.
This commit is contained in:
Gary Russell
2012-05-24 14:53:41 -04:00
committed by Oleg Zhurakousky
parent 44e3ae276d
commit 51562d1783
12 changed files with 344 additions and 92 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -18,18 +18,21 @@ package org.springframework.integration.http.config;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Gary Russell
* @since 2.0.2
*/
abstract class HttpAdapterParsingUtils {
@@ -63,4 +66,26 @@ abstract class HttpAdapterParsingUtils {
}
}
static void configureUrlConstructorArg(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String urlAttribute = element.getAttribute("url");
String urlExpressionAttribute = element.getAttribute("url-expression");
boolean hasUrlAttribute = StringUtils.hasText(urlAttribute);
boolean hasUrlExpressionAttribute = StringUtils.hasText(urlExpressionAttribute);
if (!(hasUrlAttribute ^ hasUrlExpressionAttribute)) {
parserContext.getReaderContext().error("Adapter must have exactly one of 'url' or 'url-expression'", element);
}
RootBeanDefinition expressionDef;
if (hasUrlAttribute) {
expressionDef = new RootBeanDefinition(LiteralExpression.class);
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(urlAttribute);
}
else {
expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(urlExpressionAttribute);
}
builder.addConstructorArgValue(expressionDef);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -16,20 +16,20 @@
package org.springframework.integration.http.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the 'outbound-channel-adapter' element of the http namespace.
*
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.0
*/
public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@@ -39,9 +39,9 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler");
builder.addPropertyValue("expectReply", false);
builder.addConstructorArgValue(element.getAttribute("url"));
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "http-method");
String restTemplate = element.getAttribute("rest-template");
if (StringUtils.hasText(restTemplate)) {
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
@@ -52,7 +52,7 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
}
}
String headerMapper = element.getAttribute("header-mapper");
String mappedRequestHeaders = element.getAttribute("mapped-request-headers");
if (StringUtils.hasText(headerMapper)) {
@@ -66,7 +66,7 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
else if (StringUtils.hasText(mappedRequestHeaders)) {
BeanDefinitionBuilder headerMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.http.support.DefaultHttpHeaderMapper");
IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames");
IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames");
builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition());
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -16,19 +16,19 @@
package org.springframework.integration.http.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the 'outbound-gateway' element of the http namespace.
*
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -41,9 +41,9 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler");
builder.addConstructorArgValue(element.getAttribute("url"));
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "http-method");
String restTemplate = element.getAttribute("rest-template");
if (StringUtils.hasText(restTemplate)) {
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
@@ -54,7 +54,7 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
}
}
String headerMapper = element.getAttribute("header-mapper");
String mappedRequestHeaders = element.getAttribute("mapped-request-headers");
String mappedResponseHeaders = element.getAttribute("mapped-response-headers");

View File

@@ -32,6 +32,7 @@ import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.http.HttpEntity;
@@ -67,7 +68,7 @@ import org.springframework.web.client.RestTemplate;
* When there is a response body, the {@link HttpStatus} enum instance will instead be
* copied to the MessageHeaders of the reply. In both cases, the response headers will
* be mapped to the reply Message's headers by this handler's {@link HeaderMapper} instance.
*
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
@@ -75,7 +76,7 @@ import org.springframework.web.client.RestTemplate;
*/
public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMessageHandler {
private final String uri;
private final Expression uriExpression;
private volatile HttpMethod httpMethod = HttpMethod.POST;
@@ -84,7 +85,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
private volatile Class<?> expectedResponseType;
private volatile boolean extractPayload = true;
private volatile boolean extractPayloadExplicitlySet = false;
private volatile String charset = "UTF-8";
@@ -113,16 +114,39 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
this(uri, null);
}
/**
* Create a handler that will send requests to the provided URI Expression.
*/
public HttpRequestExecutingMessageHandler(Expression uriExpression) {
this(uriExpression, null);
}
/**
* Create a handler that will send requests to the provided URI using a provided RestTemplate
* @param uri
* @param restTemplate
*/
public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) {
this(new LiteralExpression(uri), restTemplate);
/*
* We'd prefer to do this assertion first, but the compiler doesn't allow it. However,
* it's safe because the literal expression simply wraps the String variable, even
* when null.
*/
Assert.hasText(uri, "URI is required");
}
/**
* Create a handler that will send requests to the provided URI using a provided RestTemplate
* @param uriExpression A SpEL Expression that can be resolved against the message object and
* {@link BeanFactory}.
* @param 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.uri = uri;
this.uriExpression = uriExpression;
StandardEvaluationContext sec = new StandardEvaluationContext();
sec.addPropertyAccessor(new MapAccessor());
this.evaluationContext = sec;
@@ -205,7 +229,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
this.restTemplate.setRequestFactory(requestFactory);
}
/**
* Set the Map of URI variable expressions to evaluate against the outbound message
* when replacing the variable placeholders in a URI template.
@@ -240,7 +264,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
}
if (!this.shouldIncludeRequestBody() && this.extractPayloadExplicitlySet){
if (logger.isWarnEnabled()){
logger.warn("The 'extractPayload' attribute has no meaning in the context of this handler since the provided HTTP Method is '" +
logger.warn("The 'extractPayload' attribute has no meaning in the context of this handler since the provided HTTP Method is '" +
this.httpMethod + "', and no request body will be sent for that method.");
}
}
@@ -248,6 +272,8 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
String uri = this.uriExpression.getValue(this.evaluationContext, requestMessage, String.class);
Assert.notNull(uri, "URI Expression evaluation cannot result in null");
try {
Map<String, Object> uriVariables = new HashMap<String, Object>();
for (Map.Entry<String, Expression> entry : this.uriVariableExpressions.entrySet()) {
@@ -255,7 +281,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
uriVariables.put(entry.getKey(), value);
}
HttpEntity<?> httpRequest = this.generateHttpRequest(requestMessage);
ResponseEntity<?> httpResponse = this.restTemplate.exchange(this.uri, this.httpMethod, httpRequest, this.expectedResponseType, uriVariables);
ResponseEntity<?> httpResponse = this.restTemplate.exchange(uri, this.httpMethod, httpRequest, this.expectedResponseType, uriVariables);
if (this.expectReply) {
HttpHeaders httpHeaders = httpResponse.getHeaders();
Map<String, Object> headers = this.headerMapper.toHeaders(httpHeaders);
@@ -281,7 +307,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
throw e;
}
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI [" + this.uri + "]", e);
throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI [" + uri + "]", e);
}
}
@@ -367,7 +393,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
if (this.isFormData((Map<Object, ?>) content)) {
if (this.isMultipart((Map<String, ?>)content)) {
contentType = MediaType.MULTIPART_FORM_DATA;
}
}
else {
contentType = MediaType.APPLICATION_FORM_URLENCODED;
}
@@ -393,7 +419,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
Object value = simpleMap.get(key);
if (value instanceof Object[]) {
Object[] valueArray = (Object[]) value;
value = Arrays.asList(valueArray);
value = Arrays.asList(valueArray);
}
if (value instanceof Collection) {
multipartValueMap.put(key, new ArrayList<Object>((Collection<?>) value));
@@ -406,7 +432,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
}
/**
* If all keys are Strings, and some values are not Strings we'll consider
* If all keys are Strings, and some values are not Strings we'll consider
* the Map to be multipart/form-data
*/
private boolean isMultipart(Map<String, ?> map) {
@@ -414,7 +440,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
Object value = map.get(key);
if (value != null) {
if (value.getClass().isArray()) {
value = CollectionUtils.arrayToList(value);
value = CollectionUtils.arrayToList(value);
}
if (value instanceof Collection) {
Collection<?> cValues = (Collection<?>) value;
@@ -423,7 +449,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
return true;
}
}
}
}
else if (!(value instanceof String)) {
return true;
}
@@ -439,7 +465,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
for (Object key : map.keySet()) {
if (!(key instanceof String)) {
return false;
}
}
}
return true;
}

View File

@@ -333,11 +333,21 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="url" type="xsd:string" use="required">
<xsd:attribute name="url" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
URL to which the requests should be sent. It may include {placeholders}.
URL to which the requests should be sent. It may include {placeholders} for
evaluation against uri-variables.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="url-expression" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
SpEL Expression resolving to a URL to which the requests should be sent. The resolved
value may include {placeholders} for further evaluation against uri-variables.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -463,13 +473,23 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
<xsd:sequence>
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="url" type="xsd:string" use="required">
<xsd:attribute name="url" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
URL to be used as a fallback for any request Message does not contain the request URL Message header.
URL to which the requests should be sent. It may include {placeholders} for
evaluation against uri-variables.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="url-expression" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
SpEL Expression resolving to a URL to which the requests should be sent. The resolved
value may include {placeholders} for further evaluation against uri-variables.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="http-method" default="POST">
<xsd:annotation>
<xsd:documentation>

View File

@@ -34,6 +34,16 @@
<uri-variable name="foo" expression="headers.bar"/>
</outbound-channel-adapter>
<outbound-channel-adapter id="withUrlAndTemplate"
url="http://localhost/test1" channel="requests"
rest-template="customRestTemplate"/>
<outbound-channel-adapter id="withUrlExpression" url-expression="'http://localhost/test1'" channel="requests"/>
<outbound-channel-adapter id="withUrlExpressionAndTemplate"
url-expression="'http://localhost/test1'" channel="requests"
rest-template="customRestTemplate"/>
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
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/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">
<si:channel id="requests"/>
<!-- Can't have both url and url-expression -->
<outbound-channel-adapter id="minimalConfig" url="http://localhost/test1" url-expression="'foo'"
error-handler="testErrorHandler" rest-template="restTemplate" channel="requests"/>
<beans:bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>
<beans:bean id="testErrorHandler"
class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>
</beans:beans>

View File

@@ -18,7 +18,9 @@ package org.springframework.integration.http.config;
import static junit.framework.Assert.assertNotSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
@@ -33,6 +35,7 @@ import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
@@ -58,13 +61,22 @@ public class HttpOutboundChannelAdapterParserTests {
@Autowired @Qualifier("fullConfig")
private AbstractEndpoint fullConfig;
@Autowired @Qualifier("restTemplateConfig")
private AbstractEndpoint restTemplateConfig;
@Autowired @Qualifier("customRestTemplate")
private RestTemplate customRestTemplate;
@Autowired @Qualifier("withUrlAndTemplate")
private AbstractEndpoint withUrlAndTemplate;
@Autowired @Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpression;
@Autowired @Qualifier("withUrlExpressionAndTemplate")
private AbstractEndpoint withUrlExpressionAndTemplate;
@Autowired
private ApplicationContext applicationContext;
@@ -72,7 +84,7 @@ public class HttpOutboundChannelAdapterParserTests {
@Test
public void minimalConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.minimalConfig);
RestTemplate restTemplate =
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.minimalConfig, "handler.restTemplate", RestTemplate.class);
assertNotSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
@@ -128,10 +140,10 @@ public class HttpOutboundChannelAdapterParserTests {
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader1"));
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2"));
}
@Test
public void restTemplateConfig() {
RestTemplate restTemplate =
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.restTemplateConfig, "handler.restTemplate", RestTemplate.class);
assertEquals(customRestTemplate, restTemplate);
}
@@ -141,6 +153,77 @@ public class HttpOutboundChannelAdapterParserTests {
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", this.getClass());
}
@Test
public void withUrlAndTemplate() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlAndTemplate);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate", RestTemplate.class);
assertSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(false, handlerAccessor.getPropertyValue("expectReply"));
assertEquals(this.applicationContext.getBean("requests"), endpointAccessor.getPropertyValue("inputChannel"));
assertNull(handlerAccessor.getPropertyValue("outputChannel"));
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
assertEquals("http://localhost/test1", handlerAccessor.getPropertyValue("uri"));
assertEquals(HttpMethod.POST, handlerAccessor.getPropertyValue("httpMethod"));
assertEquals("UTF-8", handlerAccessor.getPropertyValue("charset"));
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
}
@Test
public void withUrlExpression() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpression);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlExpression, "handler.restTemplate", RestTemplate.class);
assertNotSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(false, handlerAccessor.getPropertyValue("expectReply"));
assertEquals(this.applicationContext.getBean("requests"), endpointAccessor.getPropertyValue("inputChannel"));
assertNull(handlerAccessor.getPropertyValue("outputChannel"));
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
SpelExpression expression = (SpelExpression) handlerAccessor.getPropertyValue("uriExpression");
assertNotNull(expression);
assertEquals("'http://localhost/test1'", expression.getExpressionString());
assertEquals(HttpMethod.POST, handlerAccessor.getPropertyValue("httpMethod"));
assertEquals("UTF-8", handlerAccessor.getPropertyValue("charset"));
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
}
@Test
public void withUrlExpressionAndTemplate() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpressionAndTemplate);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlExpressionAndTemplate, "handler.restTemplate", RestTemplate.class);
assertSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(false, handlerAccessor.getPropertyValue("expectReply"));
assertEquals(this.applicationContext.getBean("requests"), endpointAccessor.getPropertyValue("inputChannel"));
assertNull(handlerAccessor.getPropertyValue("outputChannel"));
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
SpelExpression expression = (SpelExpression) handlerAccessor.getPropertyValue("uriExpression");
assertNotNull(expression);
assertEquals("'http://localhost/test1'", expression.getExpressionString());
assertEquals(HttpMethod.POST, handlerAccessor.getPropertyValue("httpMethod"));
assertEquals("UTF-8", handlerAccessor.getPropertyValue("charset"));
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
}
@Test(expected=BeanDefinitionParsingException.class)
public void failWithUrlAndExpression() {
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-url-fail-context.xml", this.getClass());
}
public static class StubErrorHandler implements ResponseErrorHandler {

View File

@@ -40,6 +40,8 @@
<uri-variable name="foo" expression="headers.bar"/>
</outbound-gateway>
<outbound-gateway id="withUrlExpression" url-expression="'http://localhost/test1'" request-channel="requests"/>
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$StubErrorHandler"/>

View File

@@ -26,12 +26,12 @@ import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
@@ -57,6 +57,9 @@ public class HttpOutboundGatewayParserTests {
@Autowired @Qualifier("fullConfig")
private AbstractEndpoint fullConfigEndpoint;
@Autowired @Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpressionEndpoint;
@Autowired
private ApplicationContext applicationContext;
@@ -129,6 +132,29 @@ public class HttpOutboundGatewayParserTests {
assertEquals(true, handlerAccessor.getPropertyValue("transferCookies"));
}
@Test
public void withUrlExpression() {
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor(
this.withUrlExpressionEndpoint).getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.withUrlExpressionEndpoint).getPropertyValue("inputChannel");
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
Object replyChannel = handlerAccessor.getPropertyValue("outputChannel");
assertNull(replyChannel);
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
SpelExpression expression = (SpelExpression) handlerAccessor.getPropertyValue("uriExpression");
assertNotNull(expression);
assertEquals("'http://localhost/test1'", expression.getExpressionString());
assertEquals(HttpMethod.POST, handlerAccessor.getPropertyValue("httpMethod"));
assertEquals("UTF-8", handlerAccessor.getPropertyValue("charset"));
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
assertEquals(false, handlerAccessor.getPropertyValue("transferCookies"));
}
public static class StubErrorHandler implements ResponseErrorHandler {

View File

@@ -34,6 +34,7 @@ import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -51,9 +52,10 @@ import org.springframework.web.client.RestTemplate;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
*/
public class HttpRequestExecutingMessageHandlerTests {
@Test
public void simpleStringKeyStringValueFormData() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -85,7 +87,7 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("3", map.get("c").iterator().next());
assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType());
}
@Test
public void simpleStringKeyObjectValueFormData() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -116,7 +118,7 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("Mohnton", map.get("c").get(0).toString());
assertEquals(MediaType.MULTIPART_FORM_DATA, request.getHeaders().getContentType());
}
@Test
public void simpleObjectKeyObjectValueFormData() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -173,13 +175,13 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(3, aValue.size());
assertEquals("1", aValue.get(0));
assertEquals("2", aValue.get(1));
assertEquals("3", aValue.get(2));
List<?> bValue = map.get("b");
assertEquals(1, bValue.size());
assertEquals("4", bValue.get(0));
@@ -187,13 +189,13 @@ public class HttpRequestExecutingMessageHandlerTests {
List<?> cValue = map.get("c");
assertEquals(1, cValue.size());
assertEquals("5", cValue.get(0));
List<?> dValue = map.get("d");
assertEquals(1, dValue.size());
assertEquals("6", dValue.get(0));
assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType());
}
@Test
public void stringKeyPrimitiveArrayValueMixedFormData() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -218,7 +220,7 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(1, aValue.size());
Object value = aValue.get(0);
@@ -227,7 +229,7 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals(1, y[0]);
assertEquals(2, y[1]);
assertEquals(3, y[2]);
List<?> bValue = map.get("b");
assertEquals(1, bValue.size());
assertEquals("4", bValue.get(0));
@@ -235,7 +237,7 @@ public class HttpRequestExecutingMessageHandlerTests {
List<?> cValue = map.get("c");
assertEquals(1, cValue.size());
assertEquals("5", cValue.get(0));
List<?> dValue = map.get("d");
assertEquals(1, dValue.size());
assertEquals("6", dValue.get(0));
@@ -263,13 +265,13 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(3, aValue.size());
assertNull(aValue.get(0));
assertEquals(4, aValue.get(1));
assertNull(aValue.get(2));
List<?> bValue = map.get("b");
assertEquals(1, bValue.size());
assertEquals("4", bValue.get(0));
@@ -278,8 +280,8 @@ public class HttpRequestExecutingMessageHandlerTests {
}
/**
* This test and the one below might look identical, but they are not.
* This test injected "5" into the list as String resulting in
* the Content-TYpe being application/x-www-form-urlencoded
* This test injected "5" into the list as String resulting in
* the Content-TYpe being application/x-www-form-urlencoded
* @throws Exception
*/
@Test
@@ -308,13 +310,13 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(3, aValue.size());
assertNull(aValue.get(0));
assertEquals("5", aValue.get(1));
assertNull(aValue.get(2));
List<?> bValue = map.get("b");
assertEquals(1, bValue.size());
assertEquals("4", bValue.get(0));
@@ -323,7 +325,7 @@ public class HttpRequestExecutingMessageHandlerTests {
}
/**
* This test and the one above might look identical, but they are not.
* This test injected 5 into the list as int resulting in
* This test injected 5 into the list as int resulting in
* Content-type being multipart/form-data
* @throws Exception
*/
@@ -353,13 +355,13 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(3, aValue.size());
assertNull(aValue.get(0));
assertEquals(5, aValue.get(1));
assertNull(aValue.get(2));
List<?> bValue = map.get("b");
assertEquals(1, bValue.size());
assertEquals("4", bValue.get(0));
@@ -393,23 +395,23 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(2, aValue.size());
assertEquals("1", aValue.get(0));
assertEquals("2", aValue.get(1));
List<?> bValue = map.get("b");
assertEquals(0, bValue.size());
List<?> cValue = map.get("c");
assertEquals(1, cValue.size());
assertEquals("3", cValue.get(0));
assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType());
}
@Test
public void stringKeyObjectCollectionValueFormData() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
@@ -436,20 +438,20 @@ public class HttpRequestExecutingMessageHandlerTests {
Object body = request.getBody();
assertTrue(body instanceof MultiValueMap<?, ?>);
MultiValueMap<?, ?> map = (MultiValueMap <?, ?>) body;
List<?> aValue = map.get("a");
assertEquals(2, aValue.size());
assertEquals("Philadelphia", aValue.get(0).toString());
assertEquals("Ambler", aValue.get(1).toString());
List<?> bValue = map.get("b");
assertEquals(0, bValue.size());
List<?> cValue = map.get("c");
assertEquals(1, cValue.size());
assertEquals("Mohnton", cValue.get(0).toString());
assertEquals(MediaType.MULTIPART_FORM_DATA, request.getHeaders().getContentType());
}
@@ -486,7 +488,7 @@ public class HttpRequestExecutingMessageHandlerTests {
assertNull(map.get("c").get(0));
assertEquals(MediaType.APPLICATION_FORM_URLENCODED, request.getHeaders().getContentType());
}
@SuppressWarnings("cast")
@Test
public void contentAsByteArray() throws Exception {
@@ -494,7 +496,7 @@ public class HttpRequestExecutingMessageHandlerTests {
MockRestTemplate template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.POST);
byte[] bytes = "Hello World".getBytes();
Message<?> message = MessageBuilder.withPayload(bytes).build();
Exception exception = null;
@@ -511,14 +513,14 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("Hello World", new String((byte[])bytes));
assertEquals(MediaType.APPLICATION_OCTET_STREAM, request.getHeaders().getContentType());
}
@Test
public void contentAsXmlSource() throws Exception {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
MockRestTemplate template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.POST);
Message<?> message = MessageBuilder.withPayload(mock(Source.class)).build();
Exception exception = null;
try {
@@ -533,28 +535,28 @@ public class HttpRequestExecutingMessageHandlerTests {
assertTrue(body instanceof Source);
assertEquals(MediaType.TEXT_XML, request.getHeaders().getContentType());
}
@Test // no asertions just a warn message in a log
public void testWarnMessageForNonPostPutAndExtractPayload() throws Exception {
// should see a warn message
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
MockRestTemplate template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.GET);
handler.setExtractPayload(true);
handler.afterPropertiesSet();
// should not see a warn message since 'setExtractPayload' is not set explicitly
handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.GET);
handler.afterPropertiesSet();
// should not see a warn message since HTTP method is not GET
handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
@@ -562,7 +564,7 @@ public class HttpRequestExecutingMessageHandlerTests {
handler.setExtractPayload(true);
handler.afterPropertiesSet();
}
@Test
public void contentTypeIsNotSetForGetRequest() throws Exception {
//GET
@@ -570,7 +572,7 @@ public class HttpRequestExecutingMessageHandlerTests {
MockRestTemplate template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.GET);
Message<?> message = MessageBuilder.withPayload(mock(Source.class)).build();
Exception exception = null;
try {
@@ -582,18 +584,18 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("intentional", exception.getCause().getMessage());
HttpEntity<?> request = template.lastRequestEntity.get();
assertNull(request.getHeaders().getContentType());
/* TODO: reconsider the inclusion of content-type for various HttpMethods (only ignoring for GET as of 2.0.5)
* uncomment code below accordingly (see INT-1951)
*/
*/
/*
//HEAD
handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.HEAD);
message = MessageBuilder.withPayload(mock(Source.class)).build();
exception = null;
try {
@@ -605,13 +607,13 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("intentional", exception.getCause().getMessage());
request = template.lastRequestEntity.get();
assertNull(request.getHeaders().getContentType());
//DELETE
handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.DELETE);
message = MessageBuilder.withPayload(mock(Source.class)).build();
exception = null;
try {
@@ -623,13 +625,13 @@ public class HttpRequestExecutingMessageHandlerTests {
assertEquals("intentional", exception.getCause().getMessage());
request = template.lastRequestEntity.get();
assertNull(request.getHeaders().getContentType());
//TRACE
handler = new HttpRequestExecutingMessageHandler("http://www.springsource.org/spring-integration");
template = new MockRestTemplate();
new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template);
handler.setHttpMethod(HttpMethod.TRACE);
message = MessageBuilder.withPayload(mock(Source.class)).build();
exception = null;
try {
@@ -652,11 +654,27 @@ public class HttpRequestExecutingMessageHandlerTests {
// It's just enough if it was sent successfully from chain without any failures
}
@Test
public void testUriExpression() {
MockRestTemplate restTemplate = new MockRestTemplate();
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
new SpelExpressionParser().parseExpression("headers['foo']"),
restTemplate);
String theURL = "http://bar/baz";
Message<?> message = MessageBuilder.withPayload("").setHeader("foo", theURL).build();
try {
handler.handleRequestMessage(message);
}
catch (Exception e) {}
assertEquals(theURL, restTemplate.actualUrl.get());
}
public static class City{
private String name;
public City(String name){
this.name = name;
}
@Override
public String toString(){
return name;
}
@@ -665,15 +683,18 @@ public class HttpRequestExecutingMessageHandlerTests {
private static class MockRestTemplate extends RestTemplate {
private final AtomicReference<HttpEntity<?>> lastRequestEntity = new AtomicReference<HttpEntity<?>>();
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);
this.lastRequestEntity.set(requestEntity);
throw new RuntimeException("intentional");
}
}
@SuppressWarnings("unused")
private static class MockRestTemplate2 extends RestTemplate {
@Override
@@ -683,5 +704,4 @@ public class HttpRequestExecutingMessageHandlerTests {
}
}
}