INT-4208: Support AsycRest in HTTP Module
https://jira.spring.io/browse/INT-4208 https://jira.spring.io/browse/INT-4076 Add `AsyncRestTemplate` support in `HttpRequestExecutingMessageHandler` and corresponding config/dsl support * Polishing Docs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -35,16 +35,21 @@ import org.springframework.util.xml.DomUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
* @since 2.0.2
|
||||
*/
|
||||
abstract class HttpAdapterParsingUtils {
|
||||
|
||||
static final String[] REST_TEMPLATE_REFERENCE_ATTRIBUTES = {
|
||||
static final String[] SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES = {
|
||||
"request-factory", "error-handler", "message-converters"
|
||||
};
|
||||
|
||||
static final String[] ASYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES = {
|
||||
"async-request-factory", "error-handler", "message-converters"
|
||||
};
|
||||
|
||||
static void verifyNoRestTemplateAttributes(Element element, ParserContext parserContext) {
|
||||
for (String attributeName : REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
for (String attributeName : SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
if (element.hasAttribute(attributeName)) {
|
||||
parserContext.getReaderContext().error("When providing a 'rest-template' reference, the '"
|
||||
+ attributeName + "' attribute is not allowed.",
|
||||
@@ -53,6 +58,16 @@ abstract class HttpAdapterParsingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyNoAsyncRestTemplateAttributes(Element element, ParserContext parserContext) {
|
||||
for (String attributeName : ASYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
if (element.hasAttribute(attributeName)) {
|
||||
parserContext.getReaderContext().error("When providing an 'async-rest-template' reference, the '"
|
||||
+ attributeName + "' attribute is not allowed.",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void configureUriVariableExpressions(BeanDefinitionBuilder builder, ParserContext parserContext, Element element) {
|
||||
String uriVariablesExpression = element.getAttribute("uri-variables-expression");
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 1.0.2
|
||||
*/
|
||||
@@ -33,6 +34,8 @@ public class HttpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
registerBeanDefinitionParser("inbound-gateway", new HttpInboundEndpointParser(true));
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new HttpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-gateway", new HttpOutboundGatewayParser());
|
||||
registerBeanDefinitionParser("outbound-async-channel-adapter", new HttpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-async-gateway", new HttpOutboundGatewayParser());
|
||||
registerBeanDefinitionParser("graph-controller", new IntegrationGraphControllerParser());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -23,6 +23,7 @@ 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.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -33,28 +34,51 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
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");
|
||||
if (StringUtils.hasText(restTemplate)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(restTemplate);
|
||||
BeanDefinitionBuilder builder;
|
||||
boolean async = element.getLocalName().contains("async");
|
||||
if (async) {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(AsyncHttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
|
||||
builder.addPropertyValue("expectReply", false);
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
if (async) {
|
||||
String asyncTemplateRef = element.getAttribute("async-rest-template");
|
||||
|
||||
if (StringUtils.hasText(asyncTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoAsyncRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(asyncTemplateRef);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.ASYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
String restTemplateRef = element.getAttribute("rest-template");
|
||||
|
||||
if (StringUtils.hasText(restTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(restTemplateRef);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -22,6 +22,7 @@ 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.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -32,6 +33,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*/
|
||||
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@@ -42,22 +44,42 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
String restTemplate = element.getAttribute("rest-template");
|
||||
if (StringUtils.hasText(restTemplate)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(restTemplate);
|
||||
BeanDefinitionBuilder builder;
|
||||
boolean async = element.getLocalName().contains("async");
|
||||
if (async) {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(AsyncHttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
if (async) {
|
||||
String asyncTemplateRef = element.getAttribute("async-rest-template");
|
||||
if (StringUtils.hasText(asyncTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoAsyncRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(asyncTemplateRef);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.ASYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
String restTemplateRef = element.getAttribute("rest-template");
|
||||
|
||||
if (StringUtils.hasText(restTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.addConstructorArgReference(restTemplateRef);
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.dsl;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
|
||||
/**
|
||||
* The {@link BaseHttpMessageHandlerSpec} implementation for the {@link AsyncHttpRequestExecutingMessageHandler}.
|
||||
* @author Shiliang Li
|
||||
* @since 5.0
|
||||
* @see AsyncHttpRequestExecutingMessageHandler
|
||||
*/
|
||||
public class AsyncHttpMessageHandlerSpec
|
||||
extends BaseHttpMessageHandlerSpec<AsyncHttpMessageHandlerSpec, AsyncHttpRequestExecutingMessageHandler> {
|
||||
|
||||
private final AsyncRestTemplate asyncRestTemplate;
|
||||
|
||||
AsyncHttpMessageHandlerSpec(URI uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
this(new ValueExpression<>(uri), asyncRestTemplate);
|
||||
}
|
||||
|
||||
AsyncHttpMessageHandlerSpec(String uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
this(new LiteralExpression(uri), asyncRestTemplate);
|
||||
}
|
||||
|
||||
AsyncHttpMessageHandlerSpec(Expression uriExpression, AsyncRestTemplate asyncRestTemplate) {
|
||||
super(new AsyncHttpRequestExecutingMessageHandler(uriExpression, asyncRestTemplate));
|
||||
this.asyncRestTemplate = asyncRestTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link AsyncClientHttpRequestFactory} for the underlying {@link AsyncRestTemplate}.
|
||||
* @param asyncRequestFactory The request factory.
|
||||
* @return the spec
|
||||
*/
|
||||
public AsyncHttpMessageHandlerSpec asyncRequestFactory(AsyncClientHttpRequestFactory asyncRequestFactory) {
|
||||
Assert.isNull(this.asyncRestTemplate,
|
||||
"the 'requestFactory' must be specified on the provided 'restTemplate': " + this.asyncRestTemplate);
|
||||
this.target.setAsyncRequestFactory(asyncRequestFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRestTemplateSet() {
|
||||
return this.asyncRestTemplate != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.dsl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.integration.dsl.ComponentsRegistration;
|
||||
import org.springframework.integration.dsl.MessageHandlerSpec;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.http.outbound.AbstractHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
|
||||
import org.springframework.integration.mapping.HeaderMapper;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* The base {@link MessageHandlerSpec} for {@link AbstractHttpRequestExecutingMessageHandler}s.
|
||||
*
|
||||
* @param <S> the target {@link BaseHttpMessageHandlerSpec} implementation type.
|
||||
* @param <E> the target {@link AbstractHttpRequestExecutingMessageHandler} implementation type.
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class BaseHttpMessageHandlerSpec<S extends BaseHttpMessageHandlerSpec<S, E>, E extends AbstractHttpRequestExecutingMessageHandler>
|
||||
extends MessageHandlerSpec<S, E>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
|
||||
|
||||
private HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.outboundMapper();
|
||||
|
||||
private boolean headerMapperExplicitlySet;
|
||||
|
||||
public BaseHttpMessageHandlerSpec(E handler) {
|
||||
this.target = handler;
|
||||
this.target.setUriVariableExpressions(this.uriVariableExpressions);
|
||||
this.target.setHeaderMapper(this.headerMapper);
|
||||
}
|
||||
|
||||
S expectReply(boolean expectReply) {
|
||||
this.target.setExpectReply(expectReply);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the real URI should be encoded after <code>uriVariables</code>
|
||||
* expanding and before send request via underlying implementation. The default value is <code>true</code>.
|
||||
* @param encodeUri true if the URI should be encoded.
|
||||
* @return the spec
|
||||
*/
|
||||
public S encodeUri(boolean encodeUri) {
|
||||
this.target.setEncodeUri(encodeUri);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodExpression The method expression.
|
||||
* @return the spec
|
||||
*/
|
||||
public S httpMethodExpression(Expression httpMethodExpression) {
|
||||
this.target.setHttpMethodExpression(httpMethodExpression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodFunction The HTTP method {@link Function}.
|
||||
* @param <P> the payload type.
|
||||
* @return the spec
|
||||
*/
|
||||
public <P> S httpMethodFunction(Function<Message<P>, ?> httpMethodFunction) {
|
||||
return httpMethodExpression(new FunctionExpression<>(httpMethodFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link HttpMethod} for requests.
|
||||
* The default method is {@code POST}.
|
||||
* @param httpMethod the {@link HttpMethod} to use.
|
||||
* @return the spec
|
||||
*/
|
||||
public S httpMethod(HttpMethod httpMethod) {
|
||||
this.target.setHttpMethod(httpMethod);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the outbound message's payload should be extracted
|
||||
* when preparing the request body.
|
||||
* Otherwise the Message instance itself is serialized.
|
||||
* The default value is {@code true}.
|
||||
* @param extractPayload true if the payload should be extracted.
|
||||
* @return the spec
|
||||
*/
|
||||
public S extractPayload(boolean extractPayload) {
|
||||
this.target.setExtractPayload(extractPayload);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the charset name to use for converting String-typed payloads to bytes.
|
||||
* The default is {@code UTF-8}.
|
||||
* @param charset The charset.
|
||||
* @return the spec
|
||||
*/
|
||||
public S charset(String charset) {
|
||||
this.target.setCharset(charset);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the expected response type for the REST request.
|
||||
* @param expectedResponseType The expected type.
|
||||
* @return the spec
|
||||
*/
|
||||
public S expectedResponseType(Class<?> expectedResponseType) {
|
||||
this.target.setExpectedResponseType(expectedResponseType);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link ParameterizedTypeReference} for the expected response type for the REST request.
|
||||
* @param expectedResponseType The {@link ParameterizedTypeReference} for expected type.
|
||||
* @return the spec
|
||||
*/
|
||||
public S expectedResponseType(ParameterizedTypeReference<?> expectedResponseType) {
|
||||
return expectedResponseTypeExpression(new ValueExpression<ParameterizedTypeReference<?>>(expectedResponseType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL {@link Expression} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeExpression The expected response type expression.
|
||||
* @return the spec
|
||||
*/
|
||||
public S expectedResponseTypeExpression(Expression expectedResponseTypeExpression) {
|
||||
this.target.setExpectedResponseTypeExpression(expectedResponseTypeExpression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeFunction The expected response type {@link Function}.
|
||||
* @param <P> the payload type.
|
||||
* @return the spec
|
||||
*/
|
||||
public <P> S expectedResponseTypeFunction(
|
||||
Function<Message<P>, ?> expectedResponseTypeFunction) {
|
||||
return expectedResponseTypeExpression(new FunctionExpression<>(expectedResponseTypeFunction));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link HeaderMapper} to use when mapping between HTTP headers and {@code MessageHeaders}.
|
||||
* @param headerMapper The header mapper.
|
||||
* @return the spec
|
||||
*/
|
||||
public S headerMapper(HeaderMapper<HttpHeaders> headerMapper) {
|
||||
this.headerMapper = headerMapper;
|
||||
this.target.setHeaderMapper(this.headerMapper);
|
||||
this.headerMapperExplicitlySet = true;
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the pattern array for request headers to map.
|
||||
* @param patterns the patterns for request headers to map.
|
||||
* @return the spec
|
||||
* @see DefaultHttpHeaderMapper#setOutboundHeaderNames(String[])
|
||||
*/
|
||||
public S mappedRequestHeaders(String... patterns) {
|
||||
Assert.isTrue(!this.headerMapperExplicitlySet,
|
||||
"The 'mappedRequestHeaders' must be specified on the provided 'headerMapper': " + this.headerMapper);
|
||||
((DefaultHttpHeaderMapper) this.headerMapper).setOutboundHeaderNames(patterns);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the pattern array for response headers to map.
|
||||
* @param patterns the patterns for response headers to map.
|
||||
* @return the current Spec.
|
||||
* @see DefaultHttpHeaderMapper#setInboundHeaderNames(String[])
|
||||
*/
|
||||
public S mappedResponseHeaders(String... patterns) {
|
||||
Assert.isTrue(!this.headerMapperExplicitlySet,
|
||||
"The 'mappedResponseHeaders' must be specified on the provided 'headerMapper': " + this.headerMapper);
|
||||
((DefaultHttpHeaderMapper) this.headerMapper).setInboundHeaderNames(patterns);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
* @param uriVariableExpressions The URI variable expressions.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public S uriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
|
||||
this.uriVariableExpressions.clear();
|
||||
this.uriVariableExpressions.putAll(uriVariableExpressions);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param value the expression to evaluate value for te uri template variable.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public S uriVariable(String variable, Expression value) {
|
||||
this.uriVariableExpressions.put(variable, value);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param value the expression to evaluate value for te uri template variable.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public S uriVariable(String variable, String value) {
|
||||
return uriVariable(variable, PARSER.parseExpression(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to evaluate a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param valueFunction the {@link Function} to evaluate a value for the uri template variable.
|
||||
* @param <P> the payload type.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public <P> S uriVariable(String variable, Function<Message<P>, ?> valueFunction) {
|
||||
return uriVariable(variable, new FunctionExpression<>(valueFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesExpression to use.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public S uriVariablesExpression(String uriVariablesExpression) {
|
||||
return uriVariablesExpression(PARSER.parseExpression(uriVariablesExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesExpression to use.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public S uriVariablesExpression(Expression uriVariablesExpression) {
|
||||
this.target.setUriVariablesExpression(uriVariablesExpression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesFunction the {@link Function} to use.
|
||||
* @param <P> the payload type.
|
||||
* @return the current Spec.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public <P> S uriVariablesFunction(Function<Message<P>, Map<String, ?>> uriVariablesFunction) {
|
||||
return uriVariablesExpression(new FunctionExpression<>(uriVariablesFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if you wish {@code Set-Cookie} header in response to be
|
||||
* transferred as {@code Cookie} header in subsequent interaction for a message.
|
||||
* @param transferCookies the transferCookies to set.
|
||||
* @return the current Spec.
|
||||
*/
|
||||
public S transferCookies(boolean transferCookies) {
|
||||
this.target.setTransferCookies(transferCookies);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return Collections.singletonList(this.headerMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ResponseErrorHandler} for the underlying {@link RestTemplate}.
|
||||
* @param errorHandler The error handler.
|
||||
* @return the spec
|
||||
*/
|
||||
public S errorHandler(ResponseErrorHandler errorHandler) {
|
||||
Assert.isTrue(this.isRestTemplateSet(),
|
||||
"the 'errorHandler' must be specified on the provided 'restTemplate'");
|
||||
this.target.setErrorHandler(errorHandler);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of {@link HttpMessageConverter}s to be used by the underlying {@link RestTemplate}.
|
||||
* Converters configured via this method will override the default converters.
|
||||
* @param messageConverters The message converters.
|
||||
* @return the spec
|
||||
*/
|
||||
public S messageConverters(HttpMessageConverter<?>... messageConverters) {
|
||||
Assert.isTrue(!isRestTemplateSet(), "the 'messageConverters' must be specified on the provided restTemplate");
|
||||
this.target.setMessageConverters(Arrays.asList(messageConverters));
|
||||
return _this();
|
||||
}
|
||||
|
||||
protected abstract boolean isRestTemplateSet();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 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.
|
||||
@@ -27,12 +27,14 @@ import org.springframework.integration.http.inbound.HttpRequestHandlingMessaging
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* The HTTP components Factory.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@@ -125,6 +127,93 @@ public final class Http {
|
||||
return new HttpMessageHandlerSpec(uriExpression, restTemplate).expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(URI uri) {
|
||||
return outboundAsyncChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(String uri) {
|
||||
return outboundAsyncChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter based on provided {@code Function}
|
||||
* to evaluate target {@code uri} against request message.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundAsyncChannelAdapter(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter based on provided SpEL {@link Expression}
|
||||
* to evaluate target {@code uri} against request message.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(Expression uriExpression) {
|
||||
return outboundAsyncChannelAdapter(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@link URI} and {@link AsyncRestTemplate}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(URI uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uri, asyncRestTemplate).expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code uri} and {@link AsyncRestTemplate}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(String uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uri, asyncRestTemplate).expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link AsyncRestTemplate} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(Function<Message<P>, ?> uriFunction,
|
||||
AsyncRestTemplate asyncRestTemplate) {
|
||||
return outboundAsyncChannelAdapter(new FunctionExpression<>(uriFunction), asyncRestTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link AsyncRestTemplate} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncChannelAdapter(Expression uriExpression, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uriExpression, asyncRestTemplate).expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link HttpMessageHandlerSpec} builder for request-reply gateway based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
@@ -212,6 +301,93 @@ public final class Http {
|
||||
return new HttpMessageHandlerSpec(uriExpression, restTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(URI uri) {
|
||||
return outboundAsyncGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(String uri) {
|
||||
return outboundAsyncGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> AsyncHttpMessageHandlerSpec outboundAsyncGateway(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundAsyncGateway(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri} against request message.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(Expression uriExpression) {
|
||||
return outboundAsyncGateway(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@link URI} and {@link RestTemplate}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(URI uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uri, asyncRestTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code uri} and {@link RestTemplate}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(String uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uri, asyncRestTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link RestTemplate} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> AsyncHttpMessageHandlerSpec outboundAsyncGateway(Function<Message<P>, ?> uriFunction,
|
||||
AsyncRestTemplate asyncRestTemplate) {
|
||||
return outboundAsyncGateway(new FunctionExpression<>(uriFunction), asyncRestTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AsyncHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link AsyncRestTemplate} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param asyncRestTemplate {@link AsyncRestTemplate} to use.
|
||||
* @return the AsyncHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static AsyncHttpMessageHandlerSpec outboundAsyncGateway(Expression uriExpression, AsyncRestTemplate asyncRestTemplate) {
|
||||
return new AsyncHttpMessageHandlerSpec(uriExpression, asyncRestTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link HttpControllerEndpointSpec} builder for one-way adapter
|
||||
* based on the provided MVC {@code viewName} and {@code path} array for mapping.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 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.
|
||||
@@ -17,53 +17,30 @@
|
||||
package org.springframework.integration.http.dsl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.integration.dsl.ComponentsRegistration;
|
||||
import org.springframework.integration.dsl.MessageHandlerSpec;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
|
||||
import org.springframework.integration.mapping.HeaderMapper;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* The {@link MessageHandlerSpec} implementation for the {@link HttpRequestExecutingMessageHandler}.
|
||||
* The {@link BaseHttpMessageHandlerSpec} implementation for the {@link HttpRequestExecutingMessageHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see HttpRequestExecutingMessageHandler
|
||||
*/
|
||||
public class HttpMessageHandlerSpec
|
||||
extends MessageHandlerSpec<HttpMessageHandlerSpec, HttpRequestExecutingMessageHandler>
|
||||
implements ComponentsRegistration {
|
||||
extends BaseHttpMessageHandlerSpec<HttpMessageHandlerSpec, HttpRequestExecutingMessageHandler> {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
|
||||
|
||||
private HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.outboundMapper();
|
||||
|
||||
private boolean headerMapperExplicitlySet;
|
||||
|
||||
HttpMessageHandlerSpec(URI uri, RestTemplate restTemplate) {
|
||||
this(new ValueExpression<>(uri), restTemplate);
|
||||
}
|
||||
@@ -73,152 +50,10 @@ public class HttpMessageHandlerSpec
|
||||
}
|
||||
|
||||
HttpMessageHandlerSpec(Expression uriExpression, RestTemplate restTemplate) {
|
||||
this.target = new HttpRequestExecutingMessageHandler(uriExpression, restTemplate);
|
||||
this.target.setUriVariableExpressions(this.uriVariableExpressions);
|
||||
this.target.setHeaderMapper(this.headerMapper);
|
||||
super(new HttpRequestExecutingMessageHandler(uriExpression, restTemplate));
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
HttpMessageHandlerSpec expectReply(boolean expectReply) {
|
||||
this.target.setExpectReply(expectReply);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>.
|
||||
* @param encodeUri true if the URI should be encoded.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec encodeUri(boolean encodeUri) {
|
||||
this.target.setEncodeUri(encodeUri);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodExpression The method expression.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec httpMethodExpression(Expression httpMethodExpression) {
|
||||
this.target.setHttpMethodExpression(httpMethodExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodFunction The HTTP method {@link Function}.
|
||||
* @param <P> the payload type.
|
||||
* @return the spec
|
||||
*/
|
||||
public <P> HttpMessageHandlerSpec httpMethodFunction(Function<Message<P>, ?> httpMethodFunction) {
|
||||
return httpMethodExpression(new FunctionExpression<>(httpMethodFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link HttpMethod} for requests.
|
||||
* The default method is {@code POST}.
|
||||
* @param httpMethod the {@link HttpMethod} to use.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec httpMethod(HttpMethod httpMethod) {
|
||||
this.target.setHttpMethod(httpMethod);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the outbound message's payload should be extracted
|
||||
* when preparing the request body.
|
||||
* Otherwise the Message instance itself is serialized.
|
||||
* The default value is {@code true}.
|
||||
* @param extractPayload true if the payload should be extracted.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec extractPayload(boolean extractPayload) {
|
||||
this.target.setExtractPayload(extractPayload);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the charset name to use for converting String-typed payloads to bytes.
|
||||
* The default is {@code UTF-8}.
|
||||
* @param charset The charset.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec charset(String charset) {
|
||||
this.target.setCharset(charset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the expected response type for the REST request.
|
||||
* @param expectedResponseType The expected type.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec expectedResponseType(Class<?> expectedResponseType) {
|
||||
this.target.setExpectedResponseType(expectedResponseType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link ParameterizedTypeReference} for the expected response type for the REST request.
|
||||
* @param expectedResponseType The {@link ParameterizedTypeReference} for expected type.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec expectedResponseType(ParameterizedTypeReference<?> expectedResponseType) {
|
||||
return expectedResponseTypeExpression(new ValueExpression<ParameterizedTypeReference<?>>(expectedResponseType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL {@link Expression} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeExpression The expected response type expression.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec expectedResponseTypeExpression(Expression expectedResponseTypeExpression) {
|
||||
this.target.setExpectedResponseTypeExpression(expectedResponseTypeExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeFunction The expected response type {@link Function}.
|
||||
* @param <P> the payload type.
|
||||
* @return the spec
|
||||
*/
|
||||
public <P> HttpMessageHandlerSpec expectedResponseTypeFunction(
|
||||
Function<Message<P>, ?> expectedResponseTypeFunction) {
|
||||
return expectedResponseTypeExpression(new FunctionExpression<>(expectedResponseTypeFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ResponseErrorHandler} for the underlying {@link RestTemplate}.
|
||||
* @param errorHandler The error handler.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec errorHandler(ResponseErrorHandler errorHandler) {
|
||||
Assert.isNull(this.restTemplate,
|
||||
"the 'errorHandler' must be specified on the provided 'restTemplate': " + this.restTemplate);
|
||||
this.target.setErrorHandler(errorHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of {@link HttpMessageConverter}s to be used by the underlying {@link RestTemplate}.
|
||||
* Converters configured via this method will override the default converters.
|
||||
* @param messageConverters The message converters.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec messageConverters(HttpMessageConverter<?>... messageConverters) {
|
||||
Assert.isNull(this.restTemplate,
|
||||
"the 'messageConverters' must be specified on the provided 'restTemplate': " + this.restTemplate);
|
||||
this.target.setMessageConverters(Arrays.asList(messageConverters));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ClientHttpRequestFactory} for the underlying {@link RestTemplate}.
|
||||
* @param requestFactory The request factory.
|
||||
@@ -231,138 +66,8 @@ public class HttpMessageHandlerSpec
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link HeaderMapper} to use when mapping between HTTP headers and {@code MessageHeaders}.
|
||||
* @param headerMapper The header mapper.
|
||||
* @return the spec
|
||||
*/
|
||||
public HttpMessageHandlerSpec headerMapper(HeaderMapper<HttpHeaders> headerMapper) {
|
||||
this.headerMapper = headerMapper;
|
||||
this.target.setHeaderMapper(this.headerMapper);
|
||||
this.headerMapperExplicitlySet = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the pattern array for request headers to map.
|
||||
* @param patterns the patterns for request headers to map.
|
||||
* @return the spec
|
||||
* @see DefaultHttpHeaderMapper#setOutboundHeaderNames(String[])
|
||||
*/
|
||||
public HttpMessageHandlerSpec mappedRequestHeaders(String... patterns) {
|
||||
Assert.isTrue(!this.headerMapperExplicitlySet,
|
||||
"The 'mappedRequestHeaders' must be specified on the provided 'headerMapper': " + this.headerMapper);
|
||||
((DefaultHttpHeaderMapper) this.headerMapper).setOutboundHeaderNames(patterns);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the pattern array for response headers to map.
|
||||
* @param patterns the patterns for response headers to map.
|
||||
* @return the current Spec.
|
||||
* @see DefaultHttpHeaderMapper#setInboundHeaderNames(String[])
|
||||
*/
|
||||
public HttpMessageHandlerSpec mappedResponseHeaders(String... patterns) {
|
||||
Assert.isTrue(!this.headerMapperExplicitlySet,
|
||||
"The 'mappedResponseHeaders' must be specified on the provided 'headerMapper': " + this.headerMapper);
|
||||
((DefaultHttpHeaderMapper) this.headerMapper).setInboundHeaderNames(patterns);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
* @param uriVariableExpressions The URI variable expressions.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public HttpMessageHandlerSpec uriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
|
||||
this.uriVariableExpressions.clear();
|
||||
this.uriVariableExpressions.putAll(uriVariableExpressions);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param value the expression to evaluate value for te uri template variable.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public HttpMessageHandlerSpec uriVariable(String variable, Expression value) {
|
||||
this.uriVariableExpressions.put(variable, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param value the expression to evaluate value for te uri template variable.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public HttpMessageHandlerSpec uriVariable(String variable, String value) {
|
||||
return uriVariable(variable, PARSER.parseExpression(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to evaluate a value for the uri template variable.
|
||||
* @param variable the uri template variable.
|
||||
* @param valueFunction the {@link Function} to evaluate a value for the uri template variable.
|
||||
* @param <P> the payload type.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariableExpressions(Map)
|
||||
*/
|
||||
public <P> HttpMessageHandlerSpec uriVariable(String variable, Function<Message<P>, ?> valueFunction) {
|
||||
return uriVariable(variable, new FunctionExpression<>(valueFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesExpression to use.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public HttpMessageHandlerSpec uriVariablesExpression(String uriVariablesExpression) {
|
||||
return uriVariablesExpression(PARSER.parseExpression(uriVariablesExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a SpEL expression to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesExpression to use.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public HttpMessageHandlerSpec uriVariablesExpression(Expression uriVariablesExpression) {
|
||||
this.target.setUriVariablesExpression(uriVariablesExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link Function} to evaluate a {@link Map} of URI variables at runtime against request message.
|
||||
* @param uriVariablesFunction the {@link Function} to use.
|
||||
* @param <P> the payload type.
|
||||
* @return the current Spec.
|
||||
* @see HttpRequestExecutingMessageHandler#setUriVariablesExpression(Expression)
|
||||
*/
|
||||
public <P> HttpMessageHandlerSpec uriVariablesFunction(Function<Message<P>, Map<String, ?>> uriVariablesFunction) {
|
||||
return uriVariablesExpression(new FunctionExpression<>(uriVariablesFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if you wish {@code Set-Cookie} header in response to be
|
||||
* transferred as {@code Cookie} header in subsequent interaction for a message.
|
||||
* @param transferCookies the transferCookies to set.
|
||||
* @return the current Spec.
|
||||
*/
|
||||
public HttpMessageHandlerSpec transferCookies(boolean transferCookies) {
|
||||
this.target.setTransferCookies(transferCookies);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return Collections.singletonList(this.headerMapper);
|
||||
protected boolean isRestTemplateSet() {
|
||||
return this.restTemplate != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.integration.expression.ExpressionEvalMap;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
|
||||
import org.springframework.integration.mapping.HeaderMapper;
|
||||
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Base class for http outbound adapter/gateway.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Wallace Wadge
|
||||
* @author Shiliang Li
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractHttpRequestExecutingMessageHandler extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
|
||||
|
||||
private volatile StandardEvaluationContext evaluationContext;
|
||||
|
||||
private final Expression uriExpression;
|
||||
|
||||
private volatile boolean encodeUri = true;
|
||||
|
||||
private volatile Expression httpMethodExpression = new ValueExpression<>(HttpMethod.POST);
|
||||
|
||||
private volatile boolean expectReply = true;
|
||||
|
||||
private volatile Expression expectedResponseTypeExpression;
|
||||
|
||||
private volatile boolean extractPayload = true;
|
||||
|
||||
private volatile boolean extractPayloadExplicitlySet = false;
|
||||
|
||||
private volatile Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
private volatile boolean transferCookies = false;
|
||||
|
||||
private volatile HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.outboundMapper();
|
||||
|
||||
private volatile Expression uriVariablesExpression;
|
||||
|
||||
public AbstractHttpRequestExecutingMessageHandler(Expression uriExpression) {
|
||||
Assert.notNull(uriExpression, "URI Expression is required");
|
||||
this.uriExpression = uriExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>.
|
||||
*
|
||||
* @param encodeUri true if the URI should be encoded.
|
||||
*
|
||||
* @see UriComponentsBuilder
|
||||
*/
|
||||
public void setEncodeUri(boolean encodeUri) {
|
||||
this.encodeUri = encodeUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodExpression The method expression.
|
||||
*/
|
||||
public void setHttpMethodExpression(Expression httpMethodExpression) {
|
||||
Assert.notNull(httpMethodExpression, "'httpMethodExpression' must not be null");
|
||||
this.httpMethodExpression = httpMethodExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link HttpMethod} for requests.
|
||||
* The default method is {@code POST}.
|
||||
* @param httpMethod The method.
|
||||
*/
|
||||
public void setHttpMethod(HttpMethod httpMethod) {
|
||||
Assert.notNull(httpMethod, "'httpMethod' must not be null");
|
||||
this.httpMethodExpression = new ValueExpression<HttpMethod>(httpMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the outbound message's payload should be extracted
|
||||
* when preparing the request body.
|
||||
* Otherwise the Message instance itself is serialized.
|
||||
* The default value is {@code true}.
|
||||
* @param extractPayload true if the payload should be extracted.
|
||||
*/
|
||||
public void setExtractPayload(boolean extractPayload) {
|
||||
this.extractPayload = extractPayload;
|
||||
this.extractPayloadExplicitlySet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the charset name to use for converting String-typed payloads to bytes.
|
||||
* The default is {@code UTF-8}.
|
||||
* @param charset The charset.
|
||||
*/
|
||||
public void setCharset(String charset) {
|
||||
Assert.isTrue(Charset.isSupported(charset), "unsupported charset '" + charset + "'");
|
||||
this.charset = Charset.forName(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether a reply Message is expected.
|
||||
* @see AbstractHttpRequestExecutingMessageHandler#setExpectReply(boolean)
|
||||
*/
|
||||
public boolean getExpectReply() {
|
||||
return this.expectReply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether a reply Message is expected. If not, this handler will simply return null for a
|
||||
* successful response or throw an Exception for a non-successful response. The default is true.
|
||||
*
|
||||
* @param expectReply true if a reply is expected.
|
||||
*/
|
||||
public void setExpectReply(boolean expectReply) {
|
||||
this.expectReply = expectReply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the expected response type for the REST request
|
||||
* otherwise the default response type is {@link ResponseEntity} and will
|
||||
* be returned as a payload of the reply Message.
|
||||
* To take advantage of the HttpMessageConverters
|
||||
* registered on this adapter, provide a different type).
|
||||
*
|
||||
* @param expectedResponseType The expected type.
|
||||
*
|
||||
* Also see {@link #setExpectedResponseTypeExpression(Expression)}
|
||||
*/
|
||||
public void setExpectedResponseType(Class<?> expectedResponseType) {
|
||||
Assert.notNull(expectedResponseType, "'expectedResponseType' must not be null");
|
||||
this.expectedResponseTypeExpression = new ValueExpression<Class<?>>(expectedResponseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link Expression} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeExpression The expected response type expression.
|
||||
* Also see {@link #setExpectedResponseType}
|
||||
*/
|
||||
public void setExpectedResponseTypeExpression(Expression expectedResponseTypeExpression) {
|
||||
this.expectedResponseTypeExpression = expectedResponseTypeExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ResponseErrorHandler} for the underlying implementation.
|
||||
*
|
||||
* @param errorHandler The error handler.
|
||||
*/
|
||||
public abstract void setErrorHandler(ResponseErrorHandler errorHandler);
|
||||
|
||||
/**
|
||||
* Set a list of {@link HttpMessageConverter}s to be used by the underlying implementation.
|
||||
* Converters configured via this method will override the default converters.
|
||||
*
|
||||
* @param messageConverters The message converters.
|
||||
*/
|
||||
public abstract void setMessageConverters(List<HttpMessageConverter<?>> messageConverters);
|
||||
|
||||
/**
|
||||
* Set the {@link HeaderMapper} to use when mapping between HTTP headers and MessageHeaders.
|
||||
* @param headerMapper The header mapper.
|
||||
*/
|
||||
public void setHeaderMapper(HeaderMapper<HttpHeaders> headerMapper) {
|
||||
Assert.notNull(headerMapper, "headerMapper must not be null");
|
||||
this.headerMapper = headerMapper;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
*
|
||||
* @param uriVariableExpressions The URI variable expressions.
|
||||
*/
|
||||
public void setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
|
||||
synchronized (this.uriVariableExpressions) {
|
||||
this.uriVariableExpressions.clear();
|
||||
this.uriVariableExpressions.putAll(uriVariableExpressions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Expression} to evaluate against the outbound message; the expression
|
||||
* must evaluate to a Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
*
|
||||
* @param uriVariablesExpression The URI variables expression.
|
||||
*/
|
||||
public void setUriVariablesExpression(Expression uriVariablesExpression) {
|
||||
this.uriVariablesExpression = uriVariablesExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you wish 'Set-Cookie' headers in responses to be
|
||||
* transferred as 'Cookie' headers in subsequent interactions for
|
||||
* a message.
|
||||
*
|
||||
* @param transferCookies the transferCookies to set.
|
||||
*/
|
||||
public void setTransferCookies(boolean transferCookies) {
|
||||
this.transferCookies = transferCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Object uri = this.uriExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(uri instanceof String || uri instanceof URI,
|
||||
"'uriExpression' evaluation must result in a 'String' or 'URI' instance, not: "
|
||||
+ (uri == null ? "null" : uri.getClass()));
|
||||
URI realUri = null;
|
||||
try {
|
||||
HttpMethod httpMethod = this.determineHttpMethod(requestMessage);
|
||||
|
||||
if (!this.shouldIncludeRequestBody(httpMethod) && this.extractPayloadExplicitlySet) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("The 'extractPayload' attribute has no relevance for the current request since the HTTP Method is '" +
|
||||
httpMethod + "', and no request body will be sent for that method.");
|
||||
}
|
||||
}
|
||||
|
||||
Object expectedResponseType = this.determineExpectedResponseType(requestMessage);
|
||||
|
||||
HttpEntity<?> httpRequest = this.generateHttpRequest(requestMessage, httpMethod);
|
||||
Map<String, ?> uriVariables = this.determineUriVariables(requestMessage);
|
||||
UriComponentsBuilder uriComponentsBuilder = uri instanceof String
|
||||
? UriComponentsBuilder.fromUriString((String) uri)
|
||||
: UriComponentsBuilder.fromUri((URI) uri);
|
||||
UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables);
|
||||
realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString());
|
||||
|
||||
return this.exchange(realUri, httpMethod, httpRequest, expectedResponseType);
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI ["
|
||||
+ (realUri == null ? uri : realUri) + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected Object exchange(URI realUri, HttpMethod httpMethod, HttpEntity<?> httpRequest, Object expectedResponseType);
|
||||
|
||||
protected Object getReply(ResponseEntity<?> httpResponse) {
|
||||
if (this.expectReply) {
|
||||
HttpHeaders httpHeaders = httpResponse.getHeaders();
|
||||
Map<String, Object> headers = this.headerMapper.toHeaders(httpHeaders);
|
||||
if (this.transferCookies) {
|
||||
this.doConvertSetCookie(headers);
|
||||
}
|
||||
AbstractIntegrationMessageBuilder<?> replyBuilder = null;
|
||||
if (httpResponse.hasBody()) {
|
||||
Object responseBody = httpResponse.getBody();
|
||||
replyBuilder = (responseBody instanceof Message<?>) ?
|
||||
this.getMessageBuilderFactory().fromMessage((Message<?>) responseBody) : this.getMessageBuilderFactory().withPayload(responseBody);
|
||||
|
||||
}
|
||||
else {
|
||||
replyBuilder = this.getMessageBuilderFactory().withPayload(httpResponse);
|
||||
}
|
||||
replyBuilder.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, httpResponse.getStatusCode());
|
||||
return replyBuilder.copyHeaders(headers);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Set-Cookie to Cookie
|
||||
*/
|
||||
private void doConvertSetCookie(Map<String, Object> headers) {
|
||||
String keyName = null;
|
||||
for (String key : headers.keySet()) {
|
||||
if (key.equalsIgnoreCase(DefaultHttpHeaderMapper.SET_COOKIE)) {
|
||||
keyName = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyName != null) {
|
||||
Object cookies = headers.remove(keyName);
|
||||
headers.put(DefaultHttpHeaderMapper.COOKIE, cookies);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Converted Set-Cookie header to Cookie for: "
|
||||
+ cookies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpEntity<?> generateHttpRequest(Message<?> message, HttpMethod httpMethod) throws Exception {
|
||||
Assert.notNull(message, "message must not be null");
|
||||
return (this.extractPayload) ? this.createHttpEntityFromPayload(message, httpMethod)
|
||||
: this.createHttpEntityFromMessage(message, httpMethod);
|
||||
}
|
||||
|
||||
private HttpEntity<?> createHttpEntityFromPayload(Message<?> message, HttpMethod httpMethod) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof HttpEntity<?>) {
|
||||
// payload is already an HttpEntity, just return it as-is
|
||||
return (HttpEntity<?>) payload;
|
||||
}
|
||||
HttpHeaders httpHeaders = this.mapHeaders(message);
|
||||
if (!shouldIncludeRequestBody(httpMethod)) {
|
||||
return new HttpEntity<Object>(httpHeaders);
|
||||
}
|
||||
// otherwise, we are creating a request with a body and need to deal with the content-type header as well
|
||||
if (httpHeaders.getContentType() == null) {
|
||||
MediaType contentType = (payload instanceof String)
|
||||
? resolveContentType((String) payload, this.charset)
|
||||
: resolveContentType(payload);
|
||||
httpHeaders.setContentType(contentType);
|
||||
}
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.equals(httpHeaders.getContentType()) ||
|
||||
MediaType.MULTIPART_FORM_DATA.equals(httpHeaders.getContentType())) {
|
||||
if (!(payload instanceof MultiValueMap)) {
|
||||
payload = this.convertToMultiValueMap((Map<?, ?>) payload);
|
||||
}
|
||||
}
|
||||
return new HttpEntity<Object>(payload, httpHeaders);
|
||||
}
|
||||
|
||||
private HttpEntity<?> createHttpEntityFromMessage(Message<?> message, HttpMethod httpMethod) {
|
||||
HttpHeaders httpHeaders = mapHeaders(message);
|
||||
if (shouldIncludeRequestBody(httpMethod)) {
|
||||
httpHeaders.setContentType(new MediaType("application", "x-java-serialized-object"));
|
||||
return new HttpEntity<Object>(message, httpHeaders);
|
||||
}
|
||||
return new HttpEntity<Object>(httpHeaders);
|
||||
}
|
||||
|
||||
protected HttpHeaders mapHeaders(Message<?> message) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
this.headerMapper.fromHeaders(message.getHeaders(), httpHeaders);
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MediaType resolveContentType(Object content) {
|
||||
MediaType contentType = null;
|
||||
if (content instanceof byte[]) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
else if (content instanceof Source) {
|
||||
contentType = MediaType.TEXT_XML;
|
||||
}
|
||||
else if (content instanceof Map) {
|
||||
// We need to check separately for MULTIPART as well as URLENCODED simply because
|
||||
// MultiValueMap<Object, Object> is actually valid content for serialization
|
||||
if (this.isFormData((Map<Object, ?>) content)) {
|
||||
if (this.isMultipart((Map<String, ?>) content)) {
|
||||
contentType = MediaType.MULTIPART_FORM_DATA;
|
||||
}
|
||||
else {
|
||||
contentType = MediaType.APPLICATION_FORM_URLENCODED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contentType == null) {
|
||||
contentType = new MediaType("application", "x-java-serialized-object");
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeRequestBody(HttpMethod httpMethod) {
|
||||
return !HttpMethod.GET.equals(httpMethod);
|
||||
}
|
||||
|
||||
private MediaType resolveContentType(String content, Charset charset) {
|
||||
return new MediaType("text", "plain", charset);
|
||||
}
|
||||
|
||||
private MultiValueMap<Object, Object> convertToMultiValueMap(Map<?, ?> simpleMap) {
|
||||
LinkedMultiValueMap<Object, Object> multipartValueMap = new LinkedMultiValueMap<Object, Object>();
|
||||
for (Entry<?, ?> entry : simpleMap.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Object[]) {
|
||||
value = Arrays.asList((Object[]) value);
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
multipartValueMap.put(key, new ArrayList<Object>((Collection<?>) value));
|
||||
}
|
||||
else {
|
||||
multipartValueMap.add(key, value);
|
||||
}
|
||||
}
|
||||
return multipartValueMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
for (Object value : map.values()) {
|
||||
if (value != null) {
|
||||
if (value.getClass().isArray()) {
|
||||
value = CollectionUtils.arrayToList(value);
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> cValues = (Collection<?>) value;
|
||||
for (Object cValue : cValues) {
|
||||
if (cValue != null && !(cValue instanceof String)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(value instanceof String)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If all keys and values are Strings, we'll consider the Map to be form data.
|
||||
*/
|
||||
private boolean isFormData(Map<Object, ?> map) {
|
||||
for (Object key : map.keySet()) {
|
||||
if (!(key instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private HttpMethod determineHttpMethod(Message<?> requestMessage) {
|
||||
Object httpMethod = this.httpMethodExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(httpMethod != null && (httpMethod instanceof String || httpMethod instanceof HttpMethod),
|
||||
"'httpMethodExpression' evaluation must result in an 'HttpMethod' enum or its String representation, " +
|
||||
"not: " + (httpMethod == null ? "null" : httpMethod.getClass()));
|
||||
if (httpMethod instanceof HttpMethod) {
|
||||
return (HttpMethod) httpMethod;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return HttpMethod.valueOf((String) httpMethod);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("The 'httpMethodExpression' returned an invalid HTTP Method value: "
|
||||
+ httpMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object determineExpectedResponseType(Message<?> requestMessage) throws Exception {
|
||||
Object expectedResponseType = null;
|
||||
if (this.expectedResponseTypeExpression != null) {
|
||||
expectedResponseType = this.expectedResponseTypeExpression.getValue(this.evaluationContext, requestMessage);
|
||||
}
|
||||
if (expectedResponseType != null) {
|
||||
Assert.state(expectedResponseType instanceof Class<?>
|
||||
|| expectedResponseType instanceof String
|
||||
|| expectedResponseType instanceof ParameterizedTypeReference,
|
||||
"'expectedResponseType' can be an instance of 'Class<?>', 'String' or 'ParameterizedTypeReference<?>'; "
|
||||
+ "evaluation resulted in a" + expectedResponseType.getClass() + ".");
|
||||
if (expectedResponseType instanceof String && StringUtils.hasText((String) expectedResponseType)) {
|
||||
expectedResponseType = ClassUtils.forName((String) expectedResponseType,
|
||||
getApplicationContext().getClassLoader());
|
||||
}
|
||||
}
|
||||
return expectedResponseType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, ?> determineUriVariables(Message<?> requestMessage) {
|
||||
Map<String, ?> expressions;
|
||||
|
||||
if (this.uriVariablesExpression != null) {
|
||||
Object expressionsObject = this.uriVariablesExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(expressionsObject instanceof Map,
|
||||
"The 'uriVariablesExpression' evaluation must result in a 'Map'.");
|
||||
expressions = (Map<String, ?>) expressionsObject;
|
||||
}
|
||||
else {
|
||||
expressions = this.uriVariableExpressions;
|
||||
}
|
||||
|
||||
return ExpressionEvalMap.from(expressions)
|
||||
.usingEvaluationContext(this.evaluationContext)
|
||||
.withRoot(requestMessage)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
|
||||
/**
|
||||
* A {@link MessageHandler} implementation that executes HTTP requests by delegating
|
||||
* to an {@link AsyncRestTemplate} instance.
|
||||
* @see HttpRequestExecutingMessageHandler
|
||||
* @author Shiliang Li
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AsyncHttpRequestExecutingMessageHandler extends AbstractHttpRequestExecutingMessageHandler {
|
||||
|
||||
private final AsyncRestTemplate asyncRestTemplate;
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
*
|
||||
* @param uri The URI.
|
||||
*/
|
||||
public AsyncHttpRequestExecutingMessageHandler(URI uri) {
|
||||
this(new ValueExpression<>(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
*
|
||||
* @param uri The URI.
|
||||
*/
|
||||
public AsyncHttpRequestExecutingMessageHandler(String uri) {
|
||||
this(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI Expression.
|
||||
*
|
||||
* @param uriExpression The URI expression.
|
||||
*/
|
||||
public AsyncHttpRequestExecutingMessageHandler(Expression uriExpression) {
|
||||
this(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI using a provided AsyncRestTemplate
|
||||
* @param uri The URI.
|
||||
* @param asyncRestTemplate The rest template.
|
||||
*/
|
||||
public AsyncHttpRequestExecutingMessageHandler(String uri, AsyncRestTemplate asyncRestTemplate) {
|
||||
this(new LiteralExpression(uri), asyncRestTemplate);
|
||||
/*
|
||||
* 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 AsyncRestTemplate
|
||||
* @param uriExpression A SpEL Expression that can be resolved against the message object and
|
||||
* {@link BeanFactory}.
|
||||
* @param asyncRestTemplate The rest template.
|
||||
*/
|
||||
public AsyncHttpRequestExecutingMessageHandler(Expression uriExpression, AsyncRestTemplate asyncRestTemplate) {
|
||||
super(uriExpression);
|
||||
this.asyncRestTemplate = (asyncRestTemplate == null ? new AsyncRestTemplate() : asyncRestTemplate);
|
||||
this.setAsync(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (this.getExpectReply() ? "http:outbound-async-gateway" : "http:outbound-async-channel-adapter");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ResponseErrorHandler} for the underlying {@link AsyncRestTemplate}.
|
||||
*
|
||||
* @param errorHandler The error handler.
|
||||
*
|
||||
* @see AsyncRestTemplate#setErrorHandler(ResponseErrorHandler)
|
||||
*/
|
||||
@Override
|
||||
public void setErrorHandler(ResponseErrorHandler errorHandler) {
|
||||
this.asyncRestTemplate.setErrorHandler(errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of {@link HttpMessageConverter}s to be used by the underlying {@link AsyncRestTemplate}.
|
||||
* Converters configured via this method will override the default converters.
|
||||
*
|
||||
* @param messageConverters The message converters.
|
||||
*
|
||||
* @see AsyncRestTemplate#setMessageConverters(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.asyncRestTemplate.setMessageConverters(messageConverters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link AsyncClientHttpRequestFactory} for the underlying {@link AsyncRestTemplate}.
|
||||
*
|
||||
* @param asyncRequestFactory The request factory.
|
||||
*
|
||||
* @see AsyncRestTemplate#setAsyncRequestFactory(AsyncClientHttpRequestFactory)
|
||||
*/
|
||||
public void setAsyncRequestFactory(AsyncClientHttpRequestFactory asyncRequestFactory) {
|
||||
this.asyncRestTemplate.setAsyncRequestFactory(asyncRequestFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object exchange(URI uri, HttpMethod httpMethod, HttpEntity<?> httpRequest, Object expectedResponseType) {
|
||||
SettableListenableFuture<Object> replyMessageFuture = new SettableListenableFuture<>();
|
||||
ListenableFuture<? extends ResponseEntity<?>> responseFuture;
|
||||
if (expectedResponseType instanceof ParameterizedTypeReference<?>) {
|
||||
responseFuture = this.asyncRestTemplate.exchange(uri, httpMethod, httpRequest, (ParameterizedTypeReference<?>) expectedResponseType);
|
||||
}
|
||||
else {
|
||||
responseFuture = this.asyncRestTemplate.exchange(uri, httpMethod, httpRequest, (Class<?>) expectedResponseType);
|
||||
}
|
||||
|
||||
responseFuture.addCallback(
|
||||
result -> replyMessageFuture.set(getReply(result)),
|
||||
replyMessageFuture::setException);
|
||||
|
||||
return replyMessageFuture;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -17,51 +17,24 @@
|
||||
package org.springframework.integration.http.outbound;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.integration.expression.ExpressionEvalMap;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
|
||||
import org.springframework.integration.mapping.HeaderMapper;
|
||||
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
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
|
||||
@@ -79,40 +52,12 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Wallace Wadge
|
||||
* @author Shiliang Li
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
|
||||
|
||||
public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecutingMessageHandler {
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private volatile StandardEvaluationContext evaluationContext;
|
||||
|
||||
private final Expression uriExpression;
|
||||
|
||||
private volatile boolean encodeUri = true;
|
||||
|
||||
private volatile Expression httpMethodExpression = new ValueExpression<>(HttpMethod.POST);
|
||||
|
||||
private volatile boolean expectReply = true;
|
||||
|
||||
private volatile Expression expectedResponseTypeExpression;
|
||||
|
||||
private volatile boolean extractPayload = true;
|
||||
|
||||
private volatile boolean extractPayloadExplicitlySet = false;
|
||||
|
||||
private volatile Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
private volatile boolean transferCookies = false;
|
||||
|
||||
private volatile HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.outboundMapper();
|
||||
|
||||
private volatile Expression uriVariablesExpression;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
*
|
||||
@@ -162,99 +107,13 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
* @param restTemplate The rest template.
|
||||
*/
|
||||
public HttpRequestExecutingMessageHandler(Expression uriExpression, RestTemplate restTemplate) {
|
||||
Assert.notNull(uriExpression, "URI Expression is required");
|
||||
super(uriExpression);
|
||||
this.restTemplate = (restTemplate == null ? new RestTemplate() : restTemplate);
|
||||
this.uriExpression = uriExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>.
|
||||
*
|
||||
* @param encodeUri true if the URI should be encoded.
|
||||
*
|
||||
* @see UriComponentsBuilder
|
||||
*/
|
||||
public void setEncodeUri(boolean encodeUri) {
|
||||
this.encodeUri = encodeUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime.
|
||||
* @param httpMethodExpression The method expression.
|
||||
*/
|
||||
public void setHttpMethodExpression(Expression httpMethodExpression) {
|
||||
Assert.notNull(httpMethodExpression, "'httpMethodExpression' must not be null");
|
||||
this.httpMethodExpression = httpMethodExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link HttpMethod} for requests.
|
||||
* The default method is {@code POST}.
|
||||
* @param httpMethod The method.
|
||||
*/
|
||||
public void setHttpMethod(HttpMethod httpMethod) {
|
||||
Assert.notNull(httpMethod, "'httpMethod' must not be null");
|
||||
this.httpMethodExpression = new ValueExpression<HttpMethod>(httpMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the outbound message's payload should be extracted
|
||||
* when preparing the request body.
|
||||
* Otherwise the Message instance itself is serialized.
|
||||
* The default value is {@code true}.
|
||||
* @param extractPayload true if the payload should be extracted.
|
||||
*/
|
||||
public void setExtractPayload(boolean extractPayload) {
|
||||
this.extractPayload = extractPayload;
|
||||
this.extractPayloadExplicitlySet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the charset name to use for converting String-typed payloads to bytes.
|
||||
* The default is {@code UTF-8}.
|
||||
* @param charset The charset.
|
||||
*/
|
||||
public void setCharset(String charset) {
|
||||
Assert.isTrue(Charset.isSupported(charset), "unsupported charset '" + charset + "'");
|
||||
this.charset = Charset.forName(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether a reply Message is expected. If not, this handler will simply return null for a
|
||||
* successful response or throw an Exception for a non-successful response. The default is true.
|
||||
*
|
||||
* @param expectReply true if a reply is expected.
|
||||
*/
|
||||
public void setExpectReply(boolean expectReply) {
|
||||
this.expectReply = expectReply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the expected response type for the REST request
|
||||
* otherwise the default response type is {@link ResponseEntity} and will
|
||||
* be returned as a payload of the reply Message.
|
||||
* To take advantage of the HttpMessageConverters
|
||||
* registered on this adapter, provide a different type).
|
||||
*
|
||||
* @param expectedResponseType The expected type.
|
||||
*
|
||||
* Also see {@link #setExpectedResponseTypeExpression(Expression)}
|
||||
*/
|
||||
public void setExpectedResponseType(Class<?> expectedResponseType) {
|
||||
Assert.notNull(expectedResponseType, "'expectedResponseType' must not be null");
|
||||
this.expectedResponseTypeExpression = new ValueExpression<Class<?>>(expectedResponseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link Expression} to determine the type for the expected response
|
||||
* The returned value of the expression could be an instance of {@link Class} or
|
||||
* {@link String} representing a fully qualified class name.
|
||||
* @param expectedResponseTypeExpression The expected response type expression.
|
||||
* Also see {@link #setExpectedResponseType}
|
||||
*/
|
||||
public void setExpectedResponseTypeExpression(Expression expectedResponseTypeExpression) {
|
||||
this.expectedResponseTypeExpression = expectedResponseTypeExpression;
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (this.getExpectReply() ? "http:outbound-gateway" : "http:outbound-channel-adapter");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,6 +123,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
*
|
||||
* @see RestTemplate#setErrorHandler(ResponseErrorHandler)
|
||||
*/
|
||||
@Override
|
||||
public void setErrorHandler(ResponseErrorHandler errorHandler) {
|
||||
this.restTemplate.setErrorHandler(errorHandler);
|
||||
}
|
||||
@@ -276,19 +136,11 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
*
|
||||
* @see RestTemplate#setMessageConverters(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.restTemplate.setMessageConverters(messageConverters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link HeaderMapper} to use when mapping between HTTP headers and MessageHeaders.
|
||||
* @param headerMapper The header mapper.
|
||||
*/
|
||||
public void setHeaderMapper(HeaderMapper<HttpHeaders> headerMapper) {
|
||||
Assert.notNull(headerMapper, "headerMapper must not be null");
|
||||
this.headerMapper = headerMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ClientHttpRequestFactory} for the underlying {@link RestTemplate}.
|
||||
*
|
||||
@@ -300,330 +152,15 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
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.
|
||||
*
|
||||
* @param uriVariableExpressions The URI variable expressions.
|
||||
*/
|
||||
public void setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
|
||||
synchronized (this.uriVariableExpressions) {
|
||||
this.uriVariableExpressions.clear();
|
||||
this.uriVariableExpressions.putAll(uriVariableExpressions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Expression} to evaluate against the outbound message; the expression
|
||||
* must evaluate to a Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
*
|
||||
* @param uriVariablesExpression The URI variables expression.
|
||||
*/
|
||||
public void setUriVariablesExpression(Expression uriVariablesExpression) {
|
||||
this.uriVariablesExpression = uriVariablesExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you wish 'Set-Cookie' headers in responses to be
|
||||
* transferred as 'Cookie' headers in subsequent interactions for
|
||||
* a message.
|
||||
*
|
||||
* @param transferCookies the transferCookies to set.
|
||||
*/
|
||||
public void setTransferCookies(boolean transferCookies) {
|
||||
this.transferCookies = transferCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (this.expectReply ? "http:outbound-gateway" : "http:outbound-channel-adapter");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Object uri = this.uriExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(uri instanceof String || uri instanceof URI,
|
||||
"'uriExpression' evaluation must result in a 'String' or 'URI' instance, not: "
|
||||
+ (uri == null ? "null" : uri.getClass()));
|
||||
URI realUri = null;
|
||||
try {
|
||||
HttpMethod httpMethod = this.determineHttpMethod(requestMessage);
|
||||
|
||||
if (!this.shouldIncludeRequestBody(httpMethod) && this.extractPayloadExplicitlySet) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("The 'extractPayload' attribute has no relevance for the current request since the HTTP Method is '" +
|
||||
httpMethod + "', and no request body will be sent for that method.");
|
||||
}
|
||||
}
|
||||
|
||||
Object expectedResponseType = this.determineExpectedResponseType(requestMessage);
|
||||
|
||||
HttpEntity<?> httpRequest = this.generateHttpRequest(requestMessage, httpMethod);
|
||||
Map<String, ?> uriVariables = this.determineUriVariables(requestMessage);
|
||||
UriComponentsBuilder uriComponentsBuilder = uri instanceof String
|
||||
? UriComponentsBuilder.fromUriString((String) uri)
|
||||
: UriComponentsBuilder.fromUri((URI) uri);
|
||||
UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables);
|
||||
realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString());
|
||||
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);
|
||||
if (this.transferCookies) {
|
||||
this.doConvertSetCookie(headers);
|
||||
}
|
||||
AbstractIntegrationMessageBuilder<?> replyBuilder = null;
|
||||
if (httpResponse.hasBody()) {
|
||||
Object responseBody = httpResponse.getBody();
|
||||
replyBuilder = (responseBody instanceof Message<?>) ?
|
||||
this.getMessageBuilderFactory().fromMessage((Message<?>) responseBody) : this.getMessageBuilderFactory().withPayload(responseBody);
|
||||
|
||||
}
|
||||
else {
|
||||
replyBuilder = this.getMessageBuilderFactory().withPayload(httpResponse);
|
||||
}
|
||||
replyBuilder.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, httpResponse.getStatusCode());
|
||||
return replyBuilder.copyHeaders(headers).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI ["
|
||||
+ (realUri == null ? uri : realUri) + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Set-Cookie to Cookie
|
||||
*/
|
||||
private void doConvertSetCookie(Map<String, Object> headers) {
|
||||
String keyName = null;
|
||||
for (String key : headers.keySet()) {
|
||||
if (key.equalsIgnoreCase(DefaultHttpHeaderMapper.SET_COOKIE)) {
|
||||
keyName = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyName != null) {
|
||||
Object cookies = headers.remove(keyName);
|
||||
headers.put(DefaultHttpHeaderMapper.COOKIE, cookies);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Converted Set-Cookie header to Cookie for: "
|
||||
+ cookies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpEntity<?> generateHttpRequest(Message<?> message, HttpMethod httpMethod) throws Exception {
|
||||
Assert.notNull(message, "message must not be null");
|
||||
return (this.extractPayload) ? this.createHttpEntityFromPayload(message, httpMethod)
|
||||
: this.createHttpEntityFromMessage(message, httpMethod);
|
||||
}
|
||||
|
||||
private HttpEntity<?> createHttpEntityFromPayload(Message<?> message, HttpMethod httpMethod) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof HttpEntity<?>) {
|
||||
// payload is already an HttpEntity, just return it as-is
|
||||
return (HttpEntity<?>) payload;
|
||||
}
|
||||
HttpHeaders httpHeaders = this.mapHeaders(message);
|
||||
if (!shouldIncludeRequestBody(httpMethod)) {
|
||||
return new HttpEntity<Object>(httpHeaders);
|
||||
}
|
||||
// otherwise, we are creating a request with a body and need to deal with the content-type header as well
|
||||
if (httpHeaders.getContentType() == null) {
|
||||
MediaType contentType = (payload instanceof String)
|
||||
? resolveContentType((String) payload, this.charset)
|
||||
: resolveContentType(payload);
|
||||
httpHeaders.setContentType(contentType);
|
||||
}
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.equals(httpHeaders.getContentType()) ||
|
||||
MediaType.MULTIPART_FORM_DATA.equals(httpHeaders.getContentType())) {
|
||||
if (!(payload instanceof MultiValueMap)) {
|
||||
payload = this.convertToMultiValueMap((Map<?, ?>) payload);
|
||||
}
|
||||
}
|
||||
return new HttpEntity<Object>(payload, httpHeaders);
|
||||
}
|
||||
|
||||
private HttpEntity<?> createHttpEntityFromMessage(Message<?> message, HttpMethod httpMethod) {
|
||||
HttpHeaders httpHeaders = mapHeaders(message);
|
||||
if (shouldIncludeRequestBody(httpMethod)) {
|
||||
httpHeaders.setContentType(new MediaType("application", "x-java-serialized-object"));
|
||||
return new HttpEntity<Object>(message, httpHeaders);
|
||||
}
|
||||
return new HttpEntity<Object>(httpHeaders);
|
||||
}
|
||||
|
||||
protected HttpHeaders mapHeaders(Message<?> message) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
this.headerMapper.fromHeaders(message.getHeaders(), httpHeaders);
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MediaType resolveContentType(Object content) {
|
||||
MediaType contentType = null;
|
||||
if (content instanceof byte[]) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
else if (content instanceof Source) {
|
||||
contentType = MediaType.TEXT_XML;
|
||||
}
|
||||
else if (content instanceof Map) {
|
||||
// We need to check separately for MULTIPART as well as URLENCODED simply because
|
||||
// MultiValueMap<Object, Object> is actually valid content for serialization
|
||||
if (this.isFormData((Map<Object, ?>) content)) {
|
||||
if (this.isMultipart((Map<String, ?>) content)) {
|
||||
contentType = MediaType.MULTIPART_FORM_DATA;
|
||||
}
|
||||
else {
|
||||
contentType = MediaType.APPLICATION_FORM_URLENCODED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contentType == null) {
|
||||
contentType = new MediaType("application", "x-java-serialized-object");
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeRequestBody(HttpMethod httpMethod) {
|
||||
return !HttpMethod.GET.equals(httpMethod);
|
||||
}
|
||||
|
||||
private MediaType resolveContentType(String content, Charset charset) {
|
||||
return new MediaType("text", "plain", charset);
|
||||
}
|
||||
|
||||
private MultiValueMap<Object, Object> convertToMultiValueMap(Map<?, ?> simpleMap) {
|
||||
LinkedMultiValueMap<Object, Object> multipartValueMap = new LinkedMultiValueMap<Object, Object>();
|
||||
for (Entry<?, ?> entry : simpleMap.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Object[]) {
|
||||
value = Arrays.asList((Object[]) value);
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
multipartValueMap.put(key, new ArrayList<Object>((Collection<?>) value));
|
||||
}
|
||||
else {
|
||||
multipartValueMap.add(key, value);
|
||||
}
|
||||
}
|
||||
return multipartValueMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
for (Object value : map.values()) {
|
||||
if (value != null) {
|
||||
if (value.getClass().isArray()) {
|
||||
value = CollectionUtils.arrayToList(value);
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> cValues = (Collection<?>) value;
|
||||
for (Object cValue : cValues) {
|
||||
if (cValue != null && !(cValue instanceof String)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(value instanceof String)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If all keys and values are Strings, we'll consider the Map to be form data.
|
||||
*/
|
||||
private boolean isFormData(Map<Object, ?> map) {
|
||||
for (Object key : map.keySet()) {
|
||||
if (!(key instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private HttpMethod determineHttpMethod(Message<?> requestMessage) {
|
||||
Object httpMethod = this.httpMethodExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(httpMethod != null && (httpMethod instanceof String || httpMethod instanceof HttpMethod),
|
||||
"'httpMethodExpression' evaluation must result in an 'HttpMethod' enum or its String representation, " +
|
||||
"not: " + (httpMethod == null ? "null" : httpMethod.getClass()));
|
||||
if (httpMethod instanceof HttpMethod) {
|
||||
return (HttpMethod) httpMethod;
|
||||
protected Object exchange(URI uri, HttpMethod httpMethod, HttpEntity<?> httpRequest, Object expectedResponseType) {
|
||||
ResponseEntity<?> httpResponse;
|
||||
if (expectedResponseType instanceof ParameterizedTypeReference<?>) {
|
||||
httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, (ParameterizedTypeReference<?>) expectedResponseType);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return HttpMethod.valueOf((String) httpMethod);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("The 'httpMethodExpression' returned an invalid HTTP Method value: "
|
||||
+ httpMethod);
|
||||
}
|
||||
httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, (Class<?>) expectedResponseType);
|
||||
}
|
||||
return getReply(httpResponse);
|
||||
}
|
||||
|
||||
private Object determineExpectedResponseType(Message<?> requestMessage) throws Exception {
|
||||
Object expectedResponseType = null;
|
||||
if (this.expectedResponseTypeExpression != null) {
|
||||
expectedResponseType = this.expectedResponseTypeExpression.getValue(this.evaluationContext, requestMessage);
|
||||
}
|
||||
if (expectedResponseType != null) {
|
||||
Assert.state(expectedResponseType instanceof Class<?>
|
||||
|| expectedResponseType instanceof String
|
||||
|| expectedResponseType instanceof ParameterizedTypeReference,
|
||||
"'expectedResponseType' can be an instance of 'Class<?>', 'String' or 'ParameterizedTypeReference<?>'; "
|
||||
+ "evaluation resulted in a" + expectedResponseType.getClass() + ".");
|
||||
if (expectedResponseType instanceof String && StringUtils.hasText((String) expectedResponseType)) {
|
||||
expectedResponseType = ClassUtils.forName((String) expectedResponseType,
|
||||
getApplicationContext().getClassLoader());
|
||||
}
|
||||
}
|
||||
return expectedResponseType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, ?> determineUriVariables(Message<?> requestMessage) {
|
||||
Map<String, ?> expressions;
|
||||
|
||||
if (this.uriVariablesExpression != null) {
|
||||
Object expressionsObject = this.uriVariablesExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(expressionsObject instanceof Map,
|
||||
"The 'uriVariablesExpression' evaluation must result in a 'Map'.");
|
||||
expressions = (Map<String, ?>) expressionsObject;
|
||||
}
|
||||
else {
|
||||
expressions = this.uriVariableExpressions;
|
||||
}
|
||||
|
||||
return ExpressionEvalMap.from(expressions)
|
||||
.usingEvaluationContext(this.evaluationContext)
|
||||
.withRoot(requestMessage)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -387,6 +387,44 @@
|
||||
</xsd:choice>
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
|
||||
<xsd:attributeGroup ref="syncHttpOutboundCommonAttributes"/>
|
||||
<xsd:attribute name="extract-payload" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether the outbound message's payload should be extracted
|
||||
when preparing the request body. Otherwise the Message instance itself
|
||||
will be serialized.
|
||||
The default value is 'true'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="outbound-async-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler'
|
||||
with 'expectReply = false' that sends HTTP requests based on incoming messages.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:choice minOccurs="0" maxOccurs="3">
|
||||
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify an expression for URI variable placeholder within 'url'.
|
||||
This element is mutually exclusive with 'uri-variables-expression' attribute.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:choice>
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
|
||||
<xsd:attributeGroup ref="asyncHttpOutboundCommonAttributes"/>
|
||||
<xsd:attribute name="extract-payload" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -488,6 +526,101 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
|
||||
<xsd:attributeGroup ref="syncHttpOutboundCommonAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="outbound-async-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler'
|
||||
that sends HTTP requests based on incoming messages and expects HTTP responses.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="gatewayType">
|
||||
<xsd:choice minOccurs="0" maxOccurs="3">
|
||||
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify an expression for URI variable placeholder within 'url'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="transactional" type="integration:transactionalType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="request-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The receiving Message Channel of this endpoint.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mapped-response-headers" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Comma-separated list of names of HttpHeaders to be mapped from the HTTP response into the MessageHeaders.
|
||||
This can only be provided if the 'header-mapper' reference is not being set directly. The values in
|
||||
this list can also be simple patterns to be matched against the header names (e.g. "foo*" or "*foo").
|
||||
The String "HTTP_RESPONSE_HEADERS" will match against any of the standard HTTP Response headers.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="extract-request-payload" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies whether the outbound message's payload should be extracted
|
||||
when preparing the request body. Otherwise the Message instance itself
|
||||
will be serialized.
|
||||
The default value is 'true'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="transfer-cookies" type="xsd:string" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
When set to "true", if a response contains a 'Set-Cookie' header, it will be mapped to a 'Cookie' header. This enables simple
|
||||
cookie handling where subsequent HTTP interactions in the same message flow can use a cookie
|
||||
supplied by the server. Default is "false".
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="reply-timeout" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Allows you to specify how long this gateway will wait for
|
||||
the reply message to be sent successfully to the reply channel
|
||||
before throwing an exception. This attribute only applies when the
|
||||
channel might block, for example when using a bounded queue channel that
|
||||
is currently full.
|
||||
|
||||
Also, keep in mind that when sending to a DirectChannel, the
|
||||
invocation will occur in the sender's thread. Therefore,
|
||||
the failing of the send operation may be caused by other
|
||||
components further downstream.
|
||||
|
||||
The "reply-timeout" attribute maps to the "sendTimeout" property of the
|
||||
underlying 'MessagingTemplate' instance (org.springframework.integration.core.MessagingTemplate).
|
||||
|
||||
The attribute will default, if not specified, to '-1', meaning that
|
||||
by default, the Gateway will wait indefinitely. The value is
|
||||
specified in milliseconds.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
|
||||
<xsd:attributeGroup ref="asyncHttpOutboundCommonAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -726,6 +859,60 @@
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:attributeGroup name="asyncHttpOutboundCommonAttributes">
|
||||
<xsd:attribute name="async-rest-template" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.client.AsyncRestTemplate" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The reference to org.springframework.web.client.AsyncRestTemplate bean to send the HTTP Request.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="async-request-factory" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Reference to a AsyncClientHttpRequestFactory to be used by the underlying RestTemplate.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.http.client.AsyncClientHttpRequestFactory" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="syncHttpOutboundCommonAttributes">
|
||||
<xsd:attribute name="rest-template" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.client.RestTemplate" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The reference to org.springframework.web.client.RestTemplate bean to send the HTTP Request.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-factory" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Reference to a ClientHttpRequestFactory to be used by the underlying RestTemplate.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.http.client.ClientHttpRequestFactory" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="httpOutboundCommonAttributes">
|
||||
<xsd:attribute name="url" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
@@ -773,18 +960,6 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="rest-template" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.client.RestTemplate" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The reference to org.springframework.web.client.RestTemplate bean to send the HTTP Request.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="charset" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -849,18 +1024,6 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-factory" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Reference to a ClientHttpRequestFactory to be used by the underlying RestTemplate.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.http.client.ClientHttpRequestFactory" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="error-handler" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -16,8 +16,14 @@
|
||||
|
||||
<outbound-channel-adapter id="restTemplateConfig" url="http://localhost/test1" channel="requests" rest-template="customRestTemplate"/>
|
||||
|
||||
<outbound-async-channel-adapter id="asyncMinimalConfig" url="http://localhost/test1" channel="requests" />
|
||||
|
||||
<outbound-async-channel-adapter id="asyncRestTemplateConfig" url="http://localhost/test1" channel="requests" async-rest-template="asyncRestTemplate" />
|
||||
|
||||
<beans:bean id="customRestTemplate" class="org.springframework.web.client.RestTemplate"/>
|
||||
|
||||
<beans:bean id="asyncRestTemplate" class="org.springframework.web.client.AsyncRestTemplate"/>
|
||||
|
||||
<outbound-channel-adapter id="fullConfig"
|
||||
url="http://localhost/test2/{foo}"
|
||||
http-method="GET"
|
||||
@@ -94,6 +100,8 @@
|
||||
|
||||
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
|
||||
|
||||
<beans:bean id="testAsyncRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
|
||||
|
||||
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>
|
||||
|
||||
<util:list id="converterList">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -41,12 +41,14 @@ 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.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -56,6 +58,7 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@@ -65,6 +68,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Biju Kunjummen
|
||||
* @author Shiliang Li
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@@ -80,9 +84,18 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
@Autowired @Qualifier("restTemplateConfig")
|
||||
private AbstractEndpoint restTemplateConfig;
|
||||
|
||||
@Autowired @Qualifier("asyncMinimalConfig")
|
||||
private AbstractEndpoint asyncMinimalConfig;
|
||||
|
||||
@Autowired @Qualifier("asyncRestTemplateConfig")
|
||||
private AbstractEndpoint asyncRestTemplateConfig;
|
||||
|
||||
@Autowired @Qualifier("customRestTemplate")
|
||||
private RestTemplate customRestTemplate;
|
||||
|
||||
@Autowired @Qualifier("asyncRestTemplate")
|
||||
private AsyncRestTemplate asyncRestTemplate;
|
||||
|
||||
@Autowired @Qualifier("withUrlAndTemplate")
|
||||
private AbstractEndpoint withUrlAndTemplate;
|
||||
|
||||
@@ -165,6 +178,28 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncMinimalConfig() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.asyncMinimalConfig);
|
||||
AsyncRestTemplate asyncRestTemplate =
|
||||
TestUtils.getPropertyValue(this.asyncMinimalConfig, "handler.asyncRestTemplate", AsyncRestTemplate.class);
|
||||
assertNotSame(this.asyncRestTemplate, asyncRestTemplate);
|
||||
AsyncHttpRequestExecutingMessageHandler handler = (AsyncHttpRequestExecutingMessageHandler) 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("asyncRestTemplate"));
|
||||
AsyncClientHttpRequestFactory asyncRequestFactory = (AsyncClientHttpRequestFactory)
|
||||
templateAccessor.getPropertyValue("asyncRequestFactory");
|
||||
assertTrue(asyncRequestFactory instanceof SimpleClientHttpRequestFactory);
|
||||
Expression uriExpression = (Expression) handlerAccessor.getPropertyValue("uriExpression");
|
||||
assertEquals("http://localhost/test1", uriExpression.getValue());
|
||||
assertEquals(HttpMethod.POST.name(), TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString());
|
||||
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void restTemplateConfig() {
|
||||
RestTemplate restTemplate =
|
||||
@@ -172,6 +207,14 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
assertEquals(customRestTemplate, restTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncRestTemplateConfig() {
|
||||
AsyncRestTemplate asyncRestTemplate = TestUtils.getPropertyValue(
|
||||
this.asyncRestTemplateConfig,
|
||||
"handler.asyncRestTemplate", AsyncRestTemplate.class);
|
||||
assertSame(this.asyncRestTemplate, asyncRestTemplate);
|
||||
}
|
||||
|
||||
@Test(expected = BeanDefinitionParsingException.class)
|
||||
public void failWithRestTemplateAndRestAttributes() {
|
||||
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", this.getClass())
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
<beans:bean id="asyncRestTemplate" class="org.springframework.web.client.AsyncRestTemplate"/>
|
||||
|
||||
<outbound-gateway id="minimalConfig" url="http://localhost/test1" request-channel="requests"/>
|
||||
|
||||
<si:channel id="replies">
|
||||
@@ -40,6 +42,27 @@
|
||||
<uri-variable name="foo" expression="headers.bar"/>
|
||||
</outbound-gateway>
|
||||
|
||||
<outbound-async-gateway id="asyncMinimalConfig" url="http://localhost/test1" request-channel="requests" async-rest-template="asyncRestTemplate"/>
|
||||
|
||||
<outbound-async-gateway id="asyncFullConfig"
|
||||
url="http://localhost/test2"
|
||||
http-method="PUT"
|
||||
request-channel="requests"
|
||||
async-request-factory="testRequestFactory"
|
||||
reply-timeout="1234"
|
||||
message-converters="converterList"
|
||||
extract-request-payload="false"
|
||||
expected-response-type="java.lang.String"
|
||||
mapped-request-headers="requestHeader1, requestHeader2"
|
||||
mapped-response-headers="responseHeader"
|
||||
error-handler="testErrorHandler"
|
||||
reply-channel="replies"
|
||||
charset="UTF-8"
|
||||
order="77"
|
||||
auto-startup="false"
|
||||
transfer-cookies="true">
|
||||
<uri-variable name="foo" expression="headers.bar"/>
|
||||
</outbound-async-gateway>
|
||||
|
||||
<util:map id="uriVariables">
|
||||
<beans:entry key="foo1" value="bar1"/>
|
||||
|
||||
@@ -41,12 +41,14 @@ 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.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -56,6 +58,7 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
|
||||
/**
|
||||
@@ -75,6 +78,12 @@ public class HttpOutboundGatewayParserTests {
|
||||
@Autowired @Qualifier("fullConfig")
|
||||
private AbstractEndpoint fullConfigEndpoint;
|
||||
|
||||
@Autowired @Qualifier("asyncMinimalConfig")
|
||||
private AbstractEndpoint asyncMinimalConfigEndpoint;
|
||||
|
||||
@Autowired @Qualifier("asyncFullConfig")
|
||||
private AbstractEndpoint asyncFullConfigEndpoint;
|
||||
|
||||
@Autowired @Qualifier("withUrlExpression")
|
||||
private AbstractEndpoint withUrlExpressionEndpoint;
|
||||
|
||||
@@ -84,6 +93,9 @@ public class HttpOutboundGatewayParserTests {
|
||||
@Autowired @Qualifier("withPoller1")
|
||||
private AbstractEndpoint withPoller1;
|
||||
|
||||
@Autowired @Qualifier("asyncRestTemplate")
|
||||
private AsyncRestTemplate asyncRestTemplate;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -160,6 +172,78 @@ public class HttpOutboundGatewayParserTests {
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncMinimalConfig() {
|
||||
AsyncHttpRequestExecutingMessageHandler handler = (AsyncHttpRequestExecutingMessageHandler) new DirectFieldAccessor(
|
||||
this.asyncMinimalConfigEndpoint).getPropertyValue("handler");
|
||||
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
|
||||
this.minimalConfigEndpoint).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("asyncRestTemplate"));
|
||||
AsyncClientHttpRequestFactory requestFactory = (AsyncClientHttpRequestFactory)
|
||||
templateAccessor.getPropertyValue("asyncRequestFactory");
|
||||
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
|
||||
Expression uriExpression = (Expression) handlerAccessor.getPropertyValue("uriExpression");
|
||||
assertEquals("http://localhost/test1", uriExpression.getValue());
|
||||
assertEquals(HttpMethod.POST.name(), TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString());
|
||||
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
|
||||
assertEquals(false, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void asyncFullConfig() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.asyncFullConfigEndpoint);
|
||||
AsyncHttpRequestExecutingMessageHandler handler = (AsyncHttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
|
||||
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
|
||||
this.asyncFullConfigEndpoint).getPropertyValue("inputChannel");
|
||||
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(77, handlerAccessor.getPropertyValue("order"));
|
||||
assertEquals(Boolean.FALSE, endpointAccessor.getPropertyValue("autoStartup"));
|
||||
Object replyChannel = handlerAccessor.getPropertyValue("outputChannel");
|
||||
assertNotNull(replyChannel);
|
||||
assertEquals(this.applicationContext.getBean("replies"), replyChannel);
|
||||
DirectFieldAccessor asyncTemplateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("asyncRestTemplate"));
|
||||
DirectFieldAccessor syncTemplateAccessor = new DirectFieldAccessor(asyncTemplateAccessor.getPropertyValue("syncTemplate"));
|
||||
AsyncClientHttpRequestFactory requestFactory = (AsyncClientHttpRequestFactory)
|
||||
asyncTemplateAccessor.getPropertyValue("asyncRequestFactory");
|
||||
assertTrue(requestFactory instanceof SimpleClientHttpRequestFactory);
|
||||
Object converterListBean = this.applicationContext.getBean("converterList");
|
||||
assertEquals(converterListBean, syncTemplateAccessor.getPropertyValue("messageConverters"));
|
||||
|
||||
assertEquals(String.class.getName(), TestUtils.getPropertyValue(handler, "expectedResponseTypeExpression", Expression.class).getValue());
|
||||
Expression uriExpression = (Expression) handlerAccessor.getPropertyValue("uriExpression");
|
||||
assertEquals("http://localhost/test2", uriExpression.getValue());
|
||||
assertEquals(HttpMethod.PUT.name(), TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString());
|
||||
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
assertEquals(false, handlerAccessor.getPropertyValue("extractPayload"));
|
||||
Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory");
|
||||
assertEquals(requestFactoryBean, requestFactory);
|
||||
Object errorHandlerBean = this.applicationContext.getBean("testErrorHandler");
|
||||
assertEquals(errorHandlerBean, syncTemplateAccessor.getPropertyValue("errorHandler"));
|
||||
Object sendTimeout = new DirectFieldAccessor(
|
||||
handlerAccessor.getPropertyValue("messagingTemplate")).getPropertyValue("sendTimeout");
|
||||
assertEquals(new Long("1234"), sendTimeout);
|
||||
Map<String, Expression> uriVariableExpressions =
|
||||
(Map<String, Expression>) handlerAccessor.getPropertyValue("uriVariableExpressions");
|
||||
assertEquals(1, uriVariableExpressions.size());
|
||||
assertEquals("headers.bar", uriVariableExpressions.get("foo").getExpressionString());
|
||||
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("headerMapper"));
|
||||
String[] mappedRequestHeaders = (String[]) mapperAccessor.getPropertyValue("outboundHeaderNames");
|
||||
String[] mappedResponseHeaders = (String[]) mapperAccessor.getPropertyValue("inboundHeaderNames");
|
||||
assertEquals(2, mappedRequestHeaders.length);
|
||||
assertEquals(1, mappedResponseHeaders.length);
|
||||
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader1"));
|
||||
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2"));
|
||||
assertEquals("responseHeader", mappedResponseHeaders[0]);
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withUrlExpression() {
|
||||
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor(
|
||||
|
||||
@@ -18,12 +18,16 @@ package org.springframework.integration.http.dsl;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -32,10 +36,13 @@ import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
|
||||
import org.springframework.integration.security.channel.SecuredChannel;
|
||||
@@ -51,9 +58,11 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
@@ -61,6 +70,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@@ -75,6 +85,9 @@ public class HttpDslTests {
|
||||
@Autowired
|
||||
private HttpRequestExecutingMessageHandler serviceInternalGatewayHandler;
|
||||
|
||||
@Autowired
|
||||
private AsyncHttpRequestExecutingMessageHandler serviceInternalAsyncGatewayHandler;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
@@ -101,6 +114,27 @@ public class HttpDslTests {
|
||||
.string("FOO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpAsyncProxyFlow() throws Exception {
|
||||
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
|
||||
String destinationUri = "http://www.springsource.org/spring-integration";
|
||||
MockRestServiceServer
|
||||
.createServer(asyncRestTemplate)
|
||||
.expect(requestTo(Matchers.startsWith(destinationUri)))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withSuccess("FOO", MediaType.TEXT_PLAIN));
|
||||
new DirectFieldAccessor(this.serviceInternalAsyncGatewayHandler)
|
||||
.setPropertyValue("asyncRestTemplate", asyncRestTemplate);
|
||||
|
||||
this.mockMvc.perform(
|
||||
get("/service2")
|
||||
.with(httpBasic("guest", "guest"))
|
||||
.param("name", "foo"))
|
||||
.andExpect(
|
||||
content()
|
||||
.string("FOO"));
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@@ -159,6 +193,21 @@ public class HttpDslTests {
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow httpAsyncProxyFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Http.inboundGateway("/service2")
|
||||
.requestMapping(r -> r.params("name")))
|
||||
.handle(Http.<MultiValueMap<String, String>>outboundAsyncGateway(m ->
|
||||
UriComponentsBuilder.fromUriString("http://www.springsource.org/spring-integration")
|
||||
.queryParams(m.getPayload())
|
||||
.build()
|
||||
.toUri())
|
||||
.expectedResponseType(String.class),
|
||||
e -> e.id("serviceInternalAsyncGateway"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDecisionManager accessDecisionManager() {
|
||||
return new AffirmativeBased(Collections.singletonList(new RoleVoter()));
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.http.HttpHeaders;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
|
||||
/**
|
||||
* @author Shiliang Li
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AsyncHttpRequestExecutingMessageHandlerTests {
|
||||
|
||||
@Test
|
||||
public void testAsyncReturn() {
|
||||
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
|
||||
String destinationUri = "http://www.springsource.org/spring-integration";
|
||||
MockRestServiceServer
|
||||
.createServer(asyncRestTemplate)
|
||||
.expect(requestTo(destinationUri))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withSuccess());
|
||||
|
||||
AsyncHttpRequestExecutingMessageHandler asyncHandler = new AsyncHttpRequestExecutingMessageHandler(
|
||||
destinationUri,
|
||||
asyncRestTemplate);
|
||||
QueueChannel ackChannel = MessageChannels.queue().get();
|
||||
asyncHandler.setOutputChannel(ackChannel);
|
||||
asyncHandler.handleMessage(MessageBuilder.withPayload("hello, world").build());
|
||||
Message<?> ack = ackChannel.receive(1000);
|
||||
assertNotNull(ack);
|
||||
assertNotNull(ack.getHeaders());
|
||||
assertEquals(ack.getHeaders().get(HttpHeaders.STATUS_CODE), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
=== Introduction
|
||||
|
||||
The HTTP support allows for the execution of HTTP requests and the processing of inbound HTTP requests.
|
||||
Because interaction over HTTP is always synchronous, even if all that is returned is a 200 status code, the HTTP support consists of two gateway implementations: `HttpInboundEndpoint` and `HttpRequestExecutingMessageHandler`.
|
||||
the HTTP support consists of the following gateway implementations: `HttpInboundEndpoint`, `HttpRequestExecutingMessageHandler` and `AsyncHttpRequestExecutingMessageHandler`
|
||||
|
||||
[[http-inbound]]
|
||||
=== Http Inbound Components
|
||||
@@ -125,6 +125,7 @@ The key that is used for that map entry by default is 'reply', but this can be o
|
||||
|
||||
[[http-outbound]]
|
||||
=== Http Outbound Components
|
||||
==== HttpRequestExecutingMessageHandler
|
||||
|
||||
To configure the `HttpRequestExecutingMessageHandler` write a bean definition like this:
|
||||
|
||||
@@ -164,6 +165,7 @@ When set to true (default is false), a _Set-Cookie_ header received from the ser
|
||||
This header will then be used on subsequent sends.
|
||||
This enables simple stateful interactions, such as...
|
||||
|
||||
|
||||
`...->logonGateway->...->doWorkGateway->...->logoffGateway->...`
|
||||
|
||||
If _transfer-cookies_ is false, any _Set-Cookie_ header received will remain as _Set-Cookie_ in the reply message, and will be dropped on subsequent sends.
|
||||
@@ -190,6 +192,37 @@ The `expected-response-type` must be compatible with the (configured or default)
|
||||
Of course, this can be an abstract class, or even an interface (such as `java.io.Serializable` when using java serialization and `Content-Type: application/x-java-serialized-object`).
|
||||
=====
|
||||
|
||||
==== AsyncHttpRequestExecutingMessageHandler
|
||||
|
||||
The `AsyncHttpRequestExecutingMessageHandler` implementation is very similar to `HttpRequestExecutingMessageHandler` instead of delegating to a `AsyncRestTemplate`.
|
||||
To configure it, write a bean like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="httpAsyncOutbound"
|
||||
class="org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler">
|
||||
<constructor-arg value="http://localhost:8080/example" />
|
||||
<property name="outputChannel" ref="responseChannel" />
|
||||
</bean>
|
||||
----
|
||||
|
||||
You can configure the `AsyncClientHttpRequestFactory` instance to use:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="httpOutbound"
|
||||
class="org.springframework.integration.http.outbound.AsyncHttpRequestExecutingMessageHandler">
|
||||
<constructor-arg value="http://localhost:8080/example" />
|
||||
<property name="outputChannel" ref="responseChannel" />
|
||||
<property name="asyncRequestFactory" ref="customRequestFactory" />
|
||||
</bean>
|
||||
----
|
||||
|
||||
By default the HTTP request will be generated using an instance of `SimpleClientHttpRequestFactory`.
|
||||
Use of the Apache Commons HTTP Async Client is also supported through the provided `HttpComponentsAsyncClientHttpRequestFactory` which can be injected as shown above.
|
||||
|
||||
For other settings like cookie, converters, etc, please see <<HttpRequestExecutingMessageHandler>> above.
|
||||
|
||||
[[http-namespace]]
|
||||
=== HTTP Namespace Support
|
||||
|
||||
@@ -512,6 +545,34 @@ The configuration looks very similar to the gateway:
|
||||
auto-startup="false"/>
|
||||
----
|
||||
|
||||
If you want to execute the http request in an asynchronous way, you can use the `outbound-async-gateway` or `outbound-async-channel-adapter`.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-http:outbound-async-gateway id="asyncExample1"
|
||||
request-channel="requests"
|
||||
url="http://localhost/test"
|
||||
http-method-expression="headers.httpMethod"
|
||||
extract-request-payload="false"
|
||||
expected-response-type-expression="payload"
|
||||
charset="UTF-8"
|
||||
async-request-factory="requestFactory"
|
||||
reply-timeout="1234"
|
||||
reply-channel="replies"/>
|
||||
|
||||
<int-http:outbound-async-channel-adapter id="asyncExample2"
|
||||
url="http://localhost/example"
|
||||
http-method="GET"
|
||||
channel="requests"
|
||||
charset="UTF-8"
|
||||
extract-payload="false"
|
||||
expected-response-type="java.lang.String"
|
||||
async-request-factory="someRequestFactory"
|
||||
order="3"
|
||||
auto-startup="false"/>
|
||||
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
To specify the URL; you can use either the 'url' attribute or the 'url-expression' attribute.
|
||||
|
||||
@@ -14,6 +14,11 @@ development process.
|
||||
The new `MongoDbOutboundGateway` allows you to make queries to the database on demand by sending a message to its request channel.
|
||||
See <<mongodb-outbound-gateway>> for more information.
|
||||
|
||||
==== HTTP Async Outbound Gateway and Channel Adapter
|
||||
|
||||
The new `AsyncHttpRequestExecutingMessageHandler` adds support for `AsyncRestTemplate` for outbound channel adapter and gateway.
|
||||
See <<AsyncHttpRequestExecutingMessageHandler>> for more information.
|
||||
|
||||
[[x5.0-general]]
|
||||
=== General Changes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user