INT-4208: Add WebFlux-Based HttpMessageHandler

JIRA: https://jira.spring.io/browse/INT-4208

Since `AsyncRestTemplate` is deprecated in Spring 5.0, it doesn't make
sense to promote that feature via our new `AsyncHttpRequestExecutingMessageHandler`
component

* Rework (and rename) `AsyncHttpRequestExecutingMessageHandler` to
`ReactiveHttpRequestExecutingMessageHandler` and make it based on the
WebFlux `WebClient`
* Fix Java DSL (`ReactiveHttpMessageHandlerSpec`) and all tests according a new
logic in the `ReactiveHttpRequestExecutingMessageHandler`
* Fix XML namespace support to use new `ReactiveHttpRequestExecutingMessageHandler`
and don't expose unused options like `converters` and `request-factory`
* Fix `What's New` and `http.adoc`
* To remain with `async` mode for the `ReactiveHttpRequestExecutingMessageHandler`
behavior support fix `AbstractMessageProducingHandler` to adapt reply `Mono`
to the `SettableListenableFuture`
* Introduce new `reactive` behavior for the `AbstractMessageProducingHandler`
when `outputChannel` is `ReactiveSubscribableChannel` and perform
`.subscribeTo(Publisher)` for `Publisher` reply
* Upgrade to Spring AMQP `2.0 M3`
* Downgrade to Spring Security `4.2.2`

Address PR comments

Add async error handling to the one-way case

Minor polishing and checkstyle fix
This commit is contained in:
Artem Bilan
2017-04-04 15:27:29 -04:00
committed by Gary Russell
parent d954e6a66f
commit 19c5402079
25 changed files with 939 additions and 813 deletions

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
@@ -14,29 +14,32 @@
<outbound-channel-adapter id="minimalConfig" url="http://localhost/test1" channel="requests"/>
<outbound-channel-adapter id="restTemplateConfig" url="http://localhost/test1" channel="requests" rest-template="customRestTemplate"/>
<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-reactive-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"/>
<outbound-async-channel-adapter id="asyncRestTemplateConfig" url="http://localhost/test1" channel="requests" async-rest-template="asyncRestTemplate" />
<outbound-reactive-channel-adapter id="reactiveWebClientConfig" url="http://localhost/test1" channel="requests"
web-client="webClient"/>
<beans:bean id="customRestTemplate" class="org.springframework.web.client.RestTemplate"/>
<beans:bean id="asyncRestTemplate" class="org.springframework.web.client.AsyncRestTemplate"/>
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
factory-method="create"/>
<outbound-channel-adapter id="fullConfig"
url="http://localhost/test2/{foo}"
http-method="GET"
channel="requests"
charset="UTF-8"
message-converters="converterList"
extract-payload="false"
expected-response-type="java.lang.Boolean"
mapped-request-headers="requestHeader1, requestHeader2"
request-factory="testRequestFactory"
error-handler="testErrorHandler"
order="77"
auto-startup="false">
url="http://localhost/test2/{foo}"
http-method="GET"
channel="requests"
charset="UTF-8"
message-converters="converterList"
extract-payload="false"
expected-response-type="java.lang.Boolean"
mapped-request-headers="requestHeader1, requestHeader2"
request-factory="testRequestFactory"
error-handler="testErrorHandler"
order="77"
auto-startup="false">
<uri-variable name="foo" expression="headers.bar"/>
</outbound-channel-adapter>
@@ -47,21 +50,22 @@
</util:map>
<outbound-channel-adapter id="withUrlAndTemplate"
url="http://localhost/test1" channel="requests"
uri-variables-expression="@uriVariables"
rest-template="customRestTemplate"/>
url="http://localhost/test1" channel="requests"
uri-variables-expression="@uriVariables"
rest-template="customRestTemplate"/>
<outbound-channel-adapter id="withUrlExpression" url-expression="'http://localhost/test1'" channel="requests"/>
<outbound-channel-adapter id="withAdvice" url-expression="'http://localhost/test1'" channel="requests">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$FooAdvice" />
<beans:bean
class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$FooAdvice"/>
</request-handler-advice-chain>
</outbound-channel-adapter>
<outbound-channel-adapter id="withUrlExpressionAndTemplate"
url-expression="'http://localhost/test1'" channel="requests"
rest-template="customRestTemplate"/>
url-expression="'http://localhost/test1'" channel="requests"
rest-template="customRestTemplate"/>
<si:channel id="queueChannel">
<si:queue capacity="10"/>
@@ -102,7 +106,8 @@
<beans:bean id="testAsyncRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>
<beans:bean id="testErrorHandler"
class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>
<util:list id="converterList">
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>

View File

@@ -41,14 +41,12 @@ 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;
@@ -58,9 +56,9 @@ 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;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Mark Fisher
@@ -75,40 +73,51 @@ import org.springframework.web.client.RestTemplate;
@DirtiesContext
public class HttpOutboundChannelAdapterParserTests {
@Autowired @Qualifier("minimalConfig")
@Autowired
@Qualifier("minimalConfig")
private AbstractEndpoint minimalConfig;
@Autowired @Qualifier("fullConfig")
@Autowired
@Qualifier("fullConfig")
private AbstractEndpoint fullConfig;
@Autowired @Qualifier("restTemplateConfig")
@Autowired
@Qualifier("restTemplateConfig")
private AbstractEndpoint restTemplateConfig;
@Autowired @Qualifier("asyncMinimalConfig")
private AbstractEndpoint asyncMinimalConfig;
@Autowired
@Qualifier("reactiveMinimalConfig")
private AbstractEndpoint reactiveMinimalConfig;
@Autowired @Qualifier("asyncRestTemplateConfig")
private AbstractEndpoint asyncRestTemplateConfig;
@Autowired
@Qualifier("reactiveWebClientConfig")
private AbstractEndpoint reactiveWebClientConfig;
@Autowired @Qualifier("customRestTemplate")
@Autowired
@Qualifier("customRestTemplate")
private RestTemplate customRestTemplate;
@Autowired @Qualifier("asyncRestTemplate")
private AsyncRestTemplate asyncRestTemplate;
@Autowired
private WebClient webClient;
@Autowired @Qualifier("withUrlAndTemplate")
@Autowired
@Qualifier("withUrlAndTemplate")
private AbstractEndpoint withUrlAndTemplate;
@Autowired @Qualifier("withUrlExpression")
@Autowired
@Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpression;
@Autowired @Qualifier("withAdvice")
@Autowired
@Qualifier("withAdvice")
private AbstractEndpoint withAdvice;
@Autowired @Qualifier("withUrlExpressionAndTemplate")
@Autowired
@Qualifier("withUrlExpressionAndTemplate")
private AbstractEndpoint withUrlExpressionAndTemplate;
@Autowired @Qualifier("withPoller1")
@Autowired
@Qualifier("withPoller1")
private AbstractEndpoint withPoller1;
@Autowired
@@ -120,7 +129,7 @@ public class HttpOutboundChannelAdapterParserTests {
public void minimalConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.minimalConfig);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.minimalConfig, "handler.restTemplate", RestTemplate.class);
TestUtils.getPropertyValue(this.minimalConfig, "handler.restTemplate", RestTemplate.class);
assertNotSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
@@ -179,23 +188,20 @@ public class HttpOutboundChannelAdapterParserTests {
}
@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");
public void reactiveMinimalConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.reactiveMinimalConfig);
WebClient webClient =
TestUtils.getPropertyValue(this.reactiveMinimalConfig, "handler.webClient", WebClient.class);
assertNotSame(this.webClient, webClient);
Object handler = 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(HttpMethod.POST.name(),
TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString());
assertEquals(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
}
@@ -203,16 +209,13 @@ public class HttpOutboundChannelAdapterParserTests {
@Test
public void restTemplateConfig() {
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.restTemplateConfig, "handler.restTemplate", RestTemplate.class);
TestUtils.getPropertyValue(this.restTemplateConfig, "handler.restTemplate", RestTemplate.class);
assertEquals(customRestTemplate, restTemplate);
}
@Test
public void asyncRestTemplateConfig() {
AsyncRestTemplate asyncRestTemplate = TestUtils.getPropertyValue(
this.asyncRestTemplateConfig,
"handler.asyncRestTemplate", AsyncRestTemplate.class);
assertSame(this.asyncRestTemplate, asyncRestTemplate);
public void reactiveWebClientConfig() {
assertSame(this.webClient, TestUtils.getPropertyValue(this.reactiveWebClientConfig, "handler.webClient"));
}
@Test(expected = BeanDefinitionParsingException.class)
@@ -225,7 +228,7 @@ public class HttpOutboundChannelAdapterParserTests {
public void withUrlAndTemplate() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlAndTemplate);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate", RestTemplate.class);
TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate", RestTemplate.class);
assertSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
@@ -255,7 +258,7 @@ public class HttpOutboundChannelAdapterParserTests {
public void withUrlExpression() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpression);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlExpression, "handler.restTemplate", RestTemplate.class);
TestUtils.getPropertyValue(this.withUrlExpression, "handler.restTemplate", RestTemplate.class);
assertNotSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
@@ -285,7 +288,7 @@ public class HttpOutboundChannelAdapterParserTests {
public void withUrlExpressionAndTemplate() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpressionAndTemplate);
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlExpressionAndTemplate, "handler.restTemplate", RestTemplate.class);
TestUtils.getPropertyValue(this.withUrlExpressionAndTemplate, "handler.restTemplate", RestTemplate.class);
assertSame(customRestTemplate, restTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
@@ -325,6 +328,7 @@ public class HttpOutboundChannelAdapterParserTests {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@@ -336,4 +340,5 @@ public class HttpOutboundChannelAdapterParserTests {
}
}
}

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
@@ -14,7 +14,8 @@
<si:channel id="requests"/>
<beans:bean id="asyncRestTemplate" class="org.springframework.web.client.AsyncRestTemplate"/>
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
factory-method="create"/>
<outbound-gateway id="minimalConfig" url="http://localhost/test1" request-channel="requests"/>
@@ -23,46 +24,44 @@
</si:channel>
<outbound-gateway id="fullConfig"
url="http://localhost/test2"
http-method="PUT"
request-channel="requests"
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">
url="http://localhost/test2"
http-method="PUT"
request-channel="requests"
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-gateway>
<outbound-async-gateway id="asyncMinimalConfig" url="http://localhost/test1" request-channel="requests" async-rest-template="asyncRestTemplate"/>
<outbound-reactive-gateway id="reactiveMinimalConfig" url="http://localhost/test1" request-channel="requests"
web-client="webClient"/>
<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">
<outbound-reactive-gateway id="reactiveFullConfig"
url="http://localhost/test2"
http-method="PUT"
request-channel="requests"
reply-timeout="1234"
extract-request-payload="false"
expected-response-type="java.lang.String"
mapped-request-headers="requestHeader1, requestHeader2"
mapped-response-headers="responseHeader"
reply-channel="replies"
charset="UTF-8"
order="77"
auto-startup="false"
transfer-cookies="true">
<uri-variable name="foo" expression="headers.bar"/>
</outbound-async-gateway>
</outbound-reactive-gateway>
<util:map id="uriVariables">
<beans:entry key="foo1" value="bar1"/>
@@ -74,7 +73,7 @@
<outbound-gateway id="withAdvice" url-expression="'http://localhost/test1'" request-channel="requests">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$FooAdvice" />
<beans:bean class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$FooAdvice"/>
</request-handler-advice-chain>
</outbound-gateway>
@@ -116,7 +115,8 @@
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$StubErrorHandler"/>
<beans:bean id="testErrorHandler"
class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$StubErrorHandler"/>
<util:list id="converterList">
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>

View File

@@ -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.
@@ -19,6 +19,7 @@ package org.springframework.integration.http.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -41,14 +42,12 @@ 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;
@@ -58,8 +57,8 @@ 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.reactive.function.client.WebClient;
/**
* @author Mark Fisher
@@ -72,29 +71,36 @@ import org.springframework.web.client.ResponseErrorHandler;
@DirtiesContext
public class HttpOutboundGatewayParserTests {
@Autowired @Qualifier("minimalConfig")
@Autowired
@Qualifier("minimalConfig")
private AbstractEndpoint minimalConfigEndpoint;
@Autowired @Qualifier("fullConfig")
@Autowired
@Qualifier("fullConfig")
private AbstractEndpoint fullConfigEndpoint;
@Autowired @Qualifier("asyncMinimalConfig")
private AbstractEndpoint asyncMinimalConfigEndpoint;
@Autowired
@Qualifier("reactiveMinimalConfig")
private AbstractEndpoint reactiveMinimalConfigEndpoint;
@Autowired @Qualifier("asyncFullConfig")
private AbstractEndpoint asyncFullConfigEndpoint;
@Autowired
@Qualifier("reactiveFullConfig")
private AbstractEndpoint reactiveFullConfigEndpoint;
@Autowired @Qualifier("withUrlExpression")
@Autowired
@Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpressionEndpoint;
@Autowired @Qualifier("withAdvice")
@Autowired
@Qualifier("withAdvice")
private AbstractEndpoint withAdvice;
@Autowired @Qualifier("withPoller1")
@Autowired
@Qualifier("withPoller1")
private AbstractEndpoint withPoller1;
@Autowired @Qualifier("asyncRestTemplate")
private AsyncRestTemplate asyncRestTemplate;
@Autowired
private WebClient webClient;
@Autowired
private ApplicationContext applicationContext;
@@ -173,22 +179,19 @@ public class HttpOutboundGatewayParserTests {
}
@Test
public void asyncMinimalConfig() {
AsyncHttpRequestExecutingMessageHandler handler = (AsyncHttpRequestExecutingMessageHandler) new DirectFieldAccessor(
this.asyncMinimalConfigEndpoint).getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.minimalConfigEndpoint).getPropertyValue("inputChannel");
public void reactiveMinimalConfig() {
Object handler = new DirectFieldAccessor(this.reactiveMinimalConfigEndpoint).getPropertyValue("handler");
Object requestChannel = new DirectFieldAccessor(this.reactiveMinimalConfigEndpoint)
.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);
assertSame(this.webClient, handlerAccessor.getPropertyValue("webClient"));
Expression uriExpression = (Expression) handlerAccessor.getPropertyValue("uriExpression");
assertEquals("http://localhost/test1", uriExpression.getValue());
assertEquals(HttpMethod.POST.name(), TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString());
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"));
@@ -196,11 +199,11 @@ public class HttpOutboundGatewayParserTests {
@Test
@SuppressWarnings("unchecked")
public void asyncFullConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.asyncFullConfigEndpoint);
AsyncHttpRequestExecutingMessageHandler handler = (AsyncHttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
public void reactiveFullConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.reactiveFullConfigEndpoint);
Object handler = endpointAccessor.getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.asyncFullConfigEndpoint).getPropertyValue("inputChannel");
this.reactiveFullConfigEndpoint).getPropertyValue("inputChannel");
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(77, handlerAccessor.getPropertyValue("order"));
@@ -208,24 +211,15 @@ public class HttpOutboundGatewayParserTests {
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());
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(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);
@@ -302,7 +296,6 @@ public class HttpOutboundGatewayParserTests {
}
public static class StubErrorHandler implements ResponseErrorHandler {
@Override
@@ -324,4 +317,5 @@ public class HttpOutboundGatewayParserTests {
}
}
}

View File

@@ -18,16 +18,12 @@ 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;
@@ -37,13 +33,15 @@ 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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
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.http.outbound.ReactiveHttpRequestExecutingMessageHandler;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.integration.security.channel.SecuredChannel;
import org.springframework.messaging.MessageChannel;
@@ -58,16 +56,18 @@ 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.reactive.server.HttpHandlerConnector;
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.reactive.function.client.WebClient;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
/**
* @author Artem Bilan
* @author Shiliang Li
@@ -86,7 +86,7 @@ public class HttpDslTests {
private HttpRequestExecutingMessageHandler serviceInternalGatewayHandler;
@Autowired
private AsyncHttpRequestExecutingMessageHandler serviceInternalAsyncGatewayHandler;
private ReactiveHttpRequestExecutingMessageHandler serviceInternalReactiveGatewayHandler;
private MockMvc mockMvc;
@@ -115,16 +115,21 @@ public class HttpDslTests {
}
@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);
public void testHttpReactiveProxyFlow() throws Exception {
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
response.setStatusCode(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return response.writeWith(Mono.just(response.bufferFactory().wrap("FOO".getBytes())))
.then(response::setComplete);
});
WebClient webClient = WebClient.builder()
.clientConnector(httpConnector)
.build();
new DirectFieldAccessor(this.serviceInternalReactiveGatewayHandler)
.setPropertyValue("webClient", webClient);
this.mockMvc.perform(
get("/service2")
@@ -191,17 +196,18 @@ public class HttpDslTests {
}
@Bean
public IntegrationFlow httpAsyncProxyFlow() {
public IntegrationFlow httpReactiveProxyFlow() {
return IntegrationFlows
.from(Http.inboundGateway("/service2")
.requestMapping(r -> r.params("name")))
.handle(Http.<MultiValueMap<String, String>>outboundAsyncGateway(m ->
.handle(Http.<MultiValueMap<String, String>>outboundReactiveGateway(m ->
UriComponentsBuilder.fromUriString("http://www.springsource.org/spring-integration")
.queryParams(m.getPayload())
.build()
.toUri())
.httpMethod(HttpMethod.GET)
.expectedResponseType(String.class),
e -> e.id("serviceInternalAsyncGateway"))
e -> e.id("serviceInternalReactiveGateway"))
.get();
}

View File

@@ -1,63 +0,0 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.time.Duration;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.ReactiveChannel;
import org.springframework.integration.http.HttpHeaders;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.test.web.reactive.server.HttpHandlerConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* @author Shiliang Li
* @author Artem Bilan
*
* @since 5.0
*/
public class ReactiveHttpRequestExecutingMessageHandlerTests {
@Test
public void testReactiveReturn() throws Throwable {
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
response.setStatusCode(HttpStatus.OK);
return Mono.empty()
.then(response::setComplete);
});
WebClient webClient = WebClient.builder()
.clientConnector(httpConnector)
.build();
String destinationUri = "http://www.springsource.org/spring-integration";
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
ReactiveChannel ackChannel = new ReactiveChannel();
reactiveHandler.setOutputChannel(ackChannel);
reactiveHandler.handleMessage(MessageBuilder.withPayload("hello, world").build());
Message<?> ack = Mono.from(ackChannel).block(Duration.ofSeconds(10));
assertNotNull(ack);
assertNotNull(ack.getHeaders());
assertEquals(ack.getHeaders().get(HttpHeaders.STATUS_CODE), HttpStatus.OK);
}
@Test
public void testReactiveErrorOneWay() throws Throwable {
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return Mono.empty()
.then(response::setComplete);
});
WebClient webClient = WebClient.builder()
.clientConnector(httpConnector)
.build();
String destinationUri = "http://www.springsource.org/spring-integration";
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
reactiveHandler.setExpectReply(false);
QueueChannel errorChannel = new QueueChannel();
reactiveHandler.handleMessage(MessageBuilder.withPayload("hello, world")
.setErrorChannel(errorChannel)
.build());
Message<?> errorMessage = errorChannel.receive(10000);
assertNotNull(errorMessage);
assertThat(errorMessage, instanceOf(ErrorMessage.class));
Throwable throwable = (Throwable) errorMessage.getPayload();
assertThat(throwable.getMessage(), containsString("401 Unauthorized"));
}
@Test
public void testReactiveConnectErrorOneWay() throws Throwable {
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
throw new RuntimeException("Intentional connection error");
});
WebClient webClient = WebClient.builder()
.clientConnector(httpConnector)
.build();
String destinationUri = "http://www.springsource.org/spring-integration";
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
reactiveHandler.setExpectReply(false);
QueueChannel errorChannel = new QueueChannel();
reactiveHandler.handleMessage(MessageBuilder.withPayload("hello, world")
.setErrorChannel(errorChannel)
.build());
Message<?> errorMessage = errorChannel.receive(10000);
assertNotNull(errorMessage);
assertThat(errorMessage, instanceOf(ErrorMessage.class));
Throwable throwable = (Throwable) errorMessage.getPayload();
assertThat(throwable.getMessage(), containsString("Intentional connection error"));
}
}