INT-4315: Add WebFlux module

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

Move Reactive components outside of HTTP module to the new WebFlux one,
including XSD, tests and documentation
Make an appropriate polishing for the `http.adoc` with cross-link
to the `webflux.adoc`

Exclude transitive `spring-webmvc` for the `spring-integration-webflux`
This commit is contained in:
Artem Bilan
2017-08-11 10:37:08 -04:00
committed by Gary Russell
parent 4fd32d2bd4
commit 84d60f4ab4
52 changed files with 1657 additions and 1003 deletions

View File

@@ -5,7 +5,8 @@
=== Introduction
The HTTP support allows for the execution of HTTP requests and the processing of inbound HTTP requests.
the HTTP support consists of the following gateway implementations: `HttpInboundEndpoint`, `HttpRequestExecutingMessageHandler` and `ReactiveHttpRequestExecutingMessageHandler`
The HTTP support consists of the following gateway implementations: `HttpInboundEndpoint`, `HttpRequestExecutingMessageHandler`.
Also see <<webflux>>.
[[http-inbound]]
=== Http Inbound Components
@@ -122,59 +123,6 @@ This also shows how to customize the HTTP methods accepted by the gateway, which
The reply message will be available in the Model map.
The key that is used for that map entry by default is 'reply', but this can be overridden by setting the 'replyKey' property on the endpoint's configuration.
=== WebFlux Server Side support
Starting with _version 5.0_, the `ReactiveHttpInboundEndpoint`, http://docs.spring.io/spring/docs/5.0.0.RC3/spring-framework-reference/web.html#web-reactive[WebFlux] `WebHandler`, implementation is provided.
This component is similar to the MVC-based `HttpRequestHandlingEndpointSupport` with which it shares some common options via the newly extracted `BaseHttpInboundEndpoint`.
Instead of MVC, it is used in the Spring WebFlux Reactive environment.
A simple sample for explanation:
[source,java]
----
@Configuration
@EnableWebFlux
@EnableIntegration
public class ReactiveHttpConfiguration {
@Bean
public ReactiveHttpInboundEndpoint simpleInboundEndpoint() {
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setPathPatterns("/test");
endpoint.setRequestMapping(requestMapping);
endpoint.setRequestChannelName("serviceChannel");
return endpoint;
}
@ServiceActivator(inputChannel = "serviceChannel")
String service() {
return "It works!";
}
}
----
As can be seen, the configuration is similar to the `HttpRequestHandlingEndpointSupport` mentioned above, except that we use `@EnableWebFlux` to add the WebFlux infrastructure to our integration application.
Also, the `ReactiveHttpInboundEndpoint` performs `sendAndReceive` operation to the downstream flow using back-pressure, on demand based capabilities, provided by the reactive HTTP server implementation.
NOTE: The reply part is non-blocking as well and based on the internal `FutureReplyChannel` which is flat-mapped to a reply `Mono` for on demand resolution.
The `ReactiveHttpInboundEndpoint` can be configured with a custom `ServerCodecConfigurer`, `RequestedContentTypeResolver` and even a `ReactiveAdapterRegistry`.
The latter provides a mechanism where we can return a reply as any reactive type - Reactor `Flux`, RxJava `Observable`, `Flowable` etc.
This way, we can simply implement https://en.wikipedia.org/wiki/Server-sent_events[Server Sent Events] scenarios with Spring Integration components:
[source,java]
----
@Bean
public IntegrationFlow sseFlow() {
return IntegrationFlows
.from(Http.inboundReactiveGateway("/sse")
.requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
.handle((p, h) -> Flux.just("foo", "bar", "baz"))
.get();
}
----
[[http-outbound]]
=== Http Outbound Components
==== HttpRequestExecutingMessageHandler
@@ -244,43 +192,6 @@ 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`).
=====
==== ReactiveHttpRequestExecutingMessageHandler
The `ReactiveHttpRequestExecutingMessageHandler` (starting with _version 5.0_) implementation is very similar to `HttpRequestExecutingMessageHandler`, using a `WebClient` from the Spring Framework WebFlux module.
To configure it, define a bean like this:
[source,xml]
----
<bean id="httpReactiveOutbound"
class="org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler">
<constructor-arg value="http://localhost:8080/example" />
<property name="outputChannel" ref="responseChannel" />
</bean>
----
You can configure a `WebClient` instance to use:
[source,xml]
----
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
factory-method="create"/>
<bean id="httpReactiveOutbound"
class="org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler">
<constructor-arg value="http://localhost:8080/example" />
<constructor-arg re="webClient" />
<property name="outputChannel" ref="responseChannel" />
</bean>
----
The `WebClient` `exchange()` operation returns a `Mono<ClientResponse>` which is mapped to the `AbstractIntegrationMessageBuilder` reactive support (using `Mono.map()`) as the output from the `ReactiveHttpRequestExecutingMessageHandler`.
Together with the `ReactiveChannel` as an `outputChannel`, the `Mono<ClientResponse>` evaluation is deferred until a downstream subscription is made.
Otherwise, it is treated as an `async` mode and the `Mono` response is adapted to an `SettableListenableFuture` for an asynchronous reply from the `ReactiveHttpRequestExecutingMessageHandler`.
See http://docs.spring.io/spring/docs/5.0.0.M5/spring-framework-reference/html/web-reactive.html#web-reactive-client[WebFlux documentation] and https://projectreactor.io/[Project Reactor] for more information.
For other settings like cookie, uri variables, etc, please see `HttpRequestExecutingMessageHandler` above.
[[http-namespace]]
=== HTTP Namespace Support
@@ -327,6 +238,7 @@ To process requests that do expect a response, use an _inbound-gateway_:
reply-channel="responses"/>
----
[[http-request-mapping]]
==== Request Mapping Support
NOTE: _Spring Integration 3.0_ is improving the REST support by introducing the http://static.springsource.org/spring-integration/api/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.html[IntegrationRequestMappingHandlerMapping].
@@ -570,7 +482,7 @@ Alternatively, you may wish to consider using JSON instead which is enabled by s
=====
Beginning with Spring Integration 2.2 you can also determine the HTTP Method dynamically using SpEL and the _http-method-expression_ attribute.
Note that this attribute is obviously murually exclusive with _http-method_ You can also use `expected-response-type-expression` attribute instead of `expected-response-type` and provide any valid SpEL expression that determines the type of the response.
Note that this attribute is obviously mutually exclusive with _http-method_ You can also use `expected-response-type-expression` attribute instead of `expected-response-type` and provide any valid SpEL expression that determines the type of the response.
[source,xml]
----
<int-http:outbound-gateway id="example"
@@ -603,32 +515,6 @@ The configuration looks very similar to the gateway:
auto-startup="false"/>
----
If you want to execute the http request in a reactive, non-blocking way, you can use the `outbound-reactive-gateway` or `outbound-reactive-channel-adapter`.
[source,xml]
----
<int-http:outbound-reactive-gateway id="reactiveExample1"
request-channel="requests"
url="http://localhost/test"
http-method-expression="headers.httpMethod"
extract-request-payload="false"
expected-response-type-expression="payload"
charset="UTF-8"
reply-timeout="1234"
reply-channel="replies"/>
<int-http:outbound-reactive-channel-adapter id="reactiveExample2"
url="http://localhost/example"
http-method="GET"
channel="requests"
charset="UTF-8"
extract-payload="false"
expected-response-type="java.lang.String"
order="3"
auto-startup="false"/>
----
[NOTE]
=====
To specify the URL; you can use either the 'url' attribute or the 'url-expression' attribute.
@@ -739,26 +625,6 @@ public RequestMapping mapping() {
requestMapping.setMethods(HttpMethod.POST);
return requestMapping;
}
@Bean
public ReactiveHttpInboundEndpoint jsonInboundEndpoint() {
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setPathPatterns("/persons");
endpoint.setRequestMapping(requestMapping);
endpoint.setRequestChannel(fluxResultChannel());
return endpoint;
}
@Bean
public MessageChannel fluxResultChannel() {
return new FluxMessageChannel();
}
@ServiceActivator(inputChannel = "fluxResultChannel")
Flux<Person> getPersons() {
return Flux.just(new Person("Jane"), new Person("Jason"), new Person("John"));
}
----
.Inbound Gateway Using the Java DSL
@@ -772,17 +638,6 @@ public IntegrationFlow inbound() {
.channel("httpRequest")
.get();
}
@Bean
public IntegrationFlow httpReactiveInboundChannelAdapterFlow() {
return IntegrationFlows
.from(Http.inboundReactiveChannelAdapter("/reactivePost")
.requestMapping(m -> m.methods(HttpMethod.POST))
.requestPayloadType(ResolvableType.forClassWithGenerics(Flux.class, String.class))
.statusCodeFunction(m -> HttpStatus.ACCEPTED))
.channel(c -> c.queue("storeChannel"))
.get();
}
----
.Outbound Gateway Using Java Configuration
@@ -797,16 +652,6 @@ public HttpRequestExecutingMessageHandler outbound() {
handler.setExpectedResponseType(String.class);
return handler;
}
@ServiceActivator(inputChannel = "reactiveHttpOutRequest")
@Bean
public ReactiveHttpRequestExecutingMessageHandler reactiveOutbound(WebClient client) {
ReactiveHttpRequestExecutingMessageHandler handler =
new ReactiveHttpRequestExecutingMessageHandler("http://localhost:8080/foo", client);
handler.setHttpMethod(HttpMethod.POST);
handler.setExpectedResponseType(String.class);
return handler;
}
----
.Outbound Gateway Using the Java DSL
@@ -820,18 +665,6 @@ public IntegrationFlow outbound() {
.expectedResponseType(String.class))
.get();
}
@Bean
public IntegrationFlow outboundReactive() {
return f -> f
.handle(Http.<MultiValueMap<String, String>>outboundReactiveGateway(m ->
UriComponentsBuilder.fromUriString("http://localhost:8080/foo")
.queryParams(m.getPayload())
.build()
.toUri())
.httpMethod(HttpMethod.GET)
.expectedResponseType(String.class));
}
----
[[http-timeout]]

View File

@@ -98,6 +98,8 @@ include::./ip.adoc[]
include::./twitter.adoc[]
include::./webflux.adoc[]
include::./web-sockets.adoc[]
include::./ws.adoc[]

View File

@@ -0,0 +1,240 @@
[[webflux]]
== WebFlux Support
[[webflux-intro]]
=== Introduction
The WebFlux Spring Integration module (`spring-integration-webflux`) allows for the execution of HTTP requests and the processing of inbound HTTP requests in Reactive manner.
The WebFlux support consists of the following gateway implementations: `WebFluxInboundEndpoint`, `WebFluxRequestExecutingMessageHandler`.
The implementation is fully based on the Spring http://docs.spring.io/spring/docs/5.0.0.RC3/spring-framework-reference/web.html#web-reactive[WebFlux] and https://projectreactor.io/[Project Reactor] foundations.
Also see <<http>> for more information since many options are shared between reactive and regular HTTP components.
[[webflux-inbound]]
=== WebFlux Inbound Components
Starting with _version 5.0_, the `WebFluxInboundEndpoint`, `WebHandler`, implementation is provided.
This component is similar to the MVC-based `HttpRequestHandlingEndpointSupport` with which it shares some common options via the newly extracted `BaseHttpInboundEndpoint`.
Instead of MVC, it is used in the Spring WebFlux Reactive environment.
A simple sample for explanation:
[source,java]
----
@Configuration
@EnableWebFlux
@EnableIntegration
public class ReactiveHttpConfiguration {
@Bean
public WebFluxInboundEndpoint simpleInboundEndpoint() {
WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setPathPatterns("/test");
endpoint.setRequestMapping(requestMapping);
endpoint.setRequestChannelName("serviceChannel");
return endpoint;
}
@ServiceActivator(inputChannel = "serviceChannel")
String service() {
return "It works!";
}
}
----
As can be seen, the configuration is similar to the `HttpRequestHandlingEndpointSupport` mentioned above, except that we use `@EnableWebFlux` to add the WebFlux infrastructure to our integration application.
Also, the `WebFluxInboundEndpoint` performs `sendAndReceive` operation to the downstream flow using back-pressure, on demand based capabilities, provided by the reactive HTTP server implementation.
NOTE: The reply part is non-blocking as well and based on the internal `FutureReplyChannel` which is flat-mapped to a reply `Mono` for on demand resolution.
The `WebFluxInboundEndpoint` can be configured with a custom `ServerCodecConfigurer`, `RequestedContentTypeResolver` and even a `ReactiveAdapterRegistry`.
The latter provides a mechanism where we can return a reply as any reactive type - Reactor `Flux`, RxJava `Observable`, `Flowable` etc.
This way, we can simply implement https://en.wikipedia.org/wiki/Server-sent_events[Server Sent Events] scenarios with Spring Integration components:
[source,java]
----
@Bean
public IntegrationFlow sseFlow() {
return IntegrationFlows
.from(WebFlux.inboundGateway("/sse")
.requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
.handle((p, h) -> Flux.just("foo", "bar", "baz"))
.get();
}
----
Also see <<http-request-mapping>> and <<http-cors>> for more possible configuration options.
[[webflux-outbound]]
=== WebFlux Outbound Components
The `WebFluxRequestExecutingMessageHandler` (starting with _version 5.0_) implementation is very similar to `HttpRequestExecutingMessageHandler`, using a `WebClient` from the Spring Framework WebFlux module.
To configure it, define a bean like this:
[source,xml]
----
<bean id="httpReactiveOutbound"
class="org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler">
<constructor-arg value="http://localhost:8080/example" />
<property name="outputChannel" ref="responseChannel" />
</bean>
----
You can configure a `WebClient` instance to use:
[source,xml]
----
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
factory-method="create"/>
<bean id="httpReactiveOutbound"
class="org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler">
<constructor-arg value="http://localhost:8080/example" />
<constructor-arg re="webClient" />
<property name="outputChannel" ref="responseChannel" />
</bean>
----
The `WebClient` `exchange()` operation returns a `Mono<ClientResponse>` which is mapped to the `AbstractIntegrationMessageBuilder` reactive support (using `Mono.map()`) as the output from the `WebFluxRequestExecutingMessageHandler`.
Together with the `ReactiveChannel` as an `outputChannel`, the `Mono<ClientResponse>` evaluation is deferred until a downstream subscription is made.
Otherwise, it is treated as an `async` mode and the `Mono` response is adapted to an `SettableListenableFuture` for an asynchronous reply from the `WebFluxRequestExecutingMessageHandler`.
Also see <<http-outbound>> for more possible configuration options.
[[webflux-namespace]]
=== WebFlux Namespace Support
==== Introduction
Spring Integration provides a _webflux_ namespace and the corresponding schema definition.
To include it in your configuration, simply provide the following namespace declaration in your application context configuration file:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-webflux="http://www.springframework.org/schema/integration/webflux"
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
http://www.springframework.org/schema/integration/webflux
http://www.springframework.org/schema/integration/webflux/spring-integration-webflux.xsd">
...
</beans>
----
==== Inbound
==== Outbound
If you want to execute the http request in a reactive, non-blocking way, you can use the `outbound-gateway` or `outbound-channel-adapter`.
[source,xml]
----
<int-webflux:outbound-gateway id="reactiveExample1"
request-channel="requests"
url="http://localhost/test"
http-method-expression="headers.httpMethod"
extract-request-payload="false"
expected-response-type-expression="payload"
charset="UTF-8"
reply-timeout="1234"
reply-channel="replies"/>
<int-webflux:outbound-channel-adapter id="reactiveExample2"
url="http://localhost/example"
http-method="GET"
channel="requests"
charset="UTF-8"
extract-payload="false"
expected-response-type="java.lang.String"
order="3"
auto-startup="false"/>
----
[[webflux-java-config]]
=== Configuring WebFlux Endpoints with Java
.Inbound Gateway Using Java Configuration
[source, java]
----
@Bean
public WebFluxInboundEndpoint jsonInboundEndpoint() {
WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setPathPatterns("/persons");
endpoint.setRequestMapping(requestMapping);
endpoint.setRequestChannel(fluxResultChannel());
return endpoint;
}
@Bean
public MessageChannel fluxResultChannel() {
return new FluxMessageChannel();
}
@ServiceActivator(inputChannel = "fluxResultChannel")
Flux<Person> getPersons() {
return Flux.just(new Person("Jane"), new Person("Jason"), new Person("John"));
}
----
.Inbound Gateway Using the Java DSL
[source, java]
----
@Bean
public IntegrationFlow inboundChannelAdapterFlow() {
return IntegrationFlows
.from(WebFlux.inboundChannelAdapter("/reactivePost")
.requestMapping(m -> m.methods(HttpMethod.POST))
.requestPayloadType(ResolvableType.forClassWithGenerics(Flux.class, String.class))
.statusCodeFunction(m -> HttpStatus.ACCEPTED))
.channel(c -> c.queue("storeChannel"))
.get();
}
----
.Outbound Gateway Using Java Configuration
[source, java]
----
@ServiceActivator(inputChannel = "reactiveHttpOutRequest")
@Bean
public WebFluxRequestExecutingMessageHandler reactiveOutbound(WebClient client) {
WebFluxRequestExecutingMessageHandler handler =
new WebFluxRequestExecutingMessageHandler("http://localhost:8080/foo", client);
handler.setHttpMethod(HttpMethod.POST);
handler.setExpectedResponseType(String.class);
return handler;
}
----
.Outbound Gateway Using the Java DSL
[source, java]
----
@Bean
public IntegrationFlow outboundReactive() {
return f -> f
.handle(WebFlux.<MultiValueMap<String, String>>outboundGateway(m ->
UriComponentsBuilder.fromUriString("http://localhost:8080/foo")
.queryParams(m.getPayload())
.build()
.toUri())
.httpMethod(HttpMethod.GET)
.expectedResponseType(String.class));
}
----
[[webflux-header-mapping]]
=== WebFlux Header Mappings
Since WebFlux components are fully based on the HTTP protocol there is no difference in the HTTP headers mapping.
See <<http-header-mapping>> for more possible options and components to use for mapping headers.

View File

@@ -29,11 +29,11 @@ The new `MongoDbOutboundGateway` allows you to make queries to the database on d
See <<mongodb-outbound-gateway>> for more information.
==== HTTP Reactive Inbound and Outbound Gateways and Channel Adapters
==== WebFlux Gateways and Channel Adapters
The new `ReactiveHttpInboundEndpoint` and `ReactiveHttpRequestExecutingMessageHandler` add support for Spring WebFlux Framework gateways and channel adapters.
The new WebFlux support module has been introduced for Spring WebFlux Framework gateways and channel adapters.
See <<http>> for more information.
See <<webflux>> for more information.
==== Content Type Conversion