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:
committed by
Gary Russell
parent
4fd32d2bd4
commit
84d60f4ab4
23
build.gradle
23
build.gradle
@@ -396,17 +396,13 @@ project('spring-integration-http') {
|
||||
description = 'Spring Integration HTTP Support'
|
||||
dependencies {
|
||||
compile project(":spring-integration-core")
|
||||
compile "org.springframework:spring-web:$springVersion"
|
||||
compile ("org.springframework:spring-webmvc:$springVersion", optional)
|
||||
compile ("org.springframework:spring-webflux:$springVersion", optional)
|
||||
compile "org.springframework:spring-webmvc:$springVersion"
|
||||
compile ("javax.servlet:javax.servlet-api:$servletApiVersion", provided)
|
||||
compile ("com.rometools:rome:$romeToolsVersion", optional)
|
||||
compile ("io.projectreactor.ipc:reactor-netty:$reactorNettyVersion" , optional)
|
||||
|
||||
testCompile project(":spring-integration-security")
|
||||
testCompile "org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
testCompile "org.springframework.security:spring-security-test:$springSecurityVersion"
|
||||
testCompile "io.projectreactor:reactor-test:$reactorVersion"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +638,6 @@ project('spring-integration-twitter') {
|
||||
exclude group: 'org.springframework', module: 'spring-expression'
|
||||
exclude group: 'org.springframework', module: 'spring-web'
|
||||
exclude group: 'org.springframework', module: 'spring-webmvc'
|
||||
|
||||
}
|
||||
compile("javax.activation:activation:$javaxActivationVersion", optional)
|
||||
testCompile project(":spring-integration-redis")
|
||||
@@ -651,6 +646,22 @@ project('spring-integration-twitter') {
|
||||
}
|
||||
}
|
||||
|
||||
project('spring-integration-webflux') {
|
||||
description = 'Spring Integration HTTP Support'
|
||||
dependencies {
|
||||
compile (project(":spring-integration-http")) {
|
||||
exclude group: 'org.springframework', module: 'spring-webmvc'
|
||||
}
|
||||
compile "org.springframework:spring-webflux:$springVersion"
|
||||
compile ("io.projectreactor.ipc:reactor-netty:$reactorNettyVersion" , optional)
|
||||
|
||||
testCompile "org.springframework:spring-webmvc:$springVersion"
|
||||
testCompile "org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
testCompile "org.springframework.security:spring-security-test:$springSecurityVersion"
|
||||
testCompile "io.projectreactor:reactor-test:$reactorVersion"
|
||||
}
|
||||
}
|
||||
|
||||
project('spring-integration-websocket') {
|
||||
description = 'Spring Integration WebSockets Support'
|
||||
dependencies {
|
||||
|
||||
@@ -23,14 +23,10 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.integration.config.IntegrationConfigurationInitializer;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.http.inbound.IntegrationHandlerResultHandler;
|
||||
import org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping;
|
||||
import org.springframework.integration.http.inbound.ReactiveIntegrationRequestMappingHandlerMapping;
|
||||
import org.springframework.integration.http.support.HttpContextUtils;
|
||||
|
||||
/**
|
||||
@@ -48,7 +44,6 @@ public class HttpIntegrationConfigurationInitializer implements IntegrationConfi
|
||||
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
registerRequestMappingHandlerMappingIfNecessary((BeanDefinitionRegistry) beanFactory);
|
||||
registerReactiveRequestMappingHandlerMappingIfNecessary((BeanDefinitionRegistry) beanFactory);
|
||||
}
|
||||
else {
|
||||
logger.warn("'IntegrationRequestMappingHandlerMapping' isn't registered because 'beanFactory'" +
|
||||
@@ -78,30 +73,4 @@ public class HttpIntegrationConfigurationInitializer implements IntegrationConfi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link ReactiveIntegrationRequestMappingHandlerMapping}
|
||||
* which could also be overridden by the user by simply registering
|
||||
* a {@link ReactiveIntegrationRequestMappingHandlerMapping} {@code <bean>} with 'id'
|
||||
* {@link HttpContextUtils#REACTIVE_HANDLER_MAPPING_BEAN_NAME}.
|
||||
* <p>
|
||||
* In addition, checks if the {@code org.springframework.web.reactive.result.method.RequestMappingInfo}
|
||||
* class is present on the classpath.
|
||||
* When Spring Integration HTTP is used only as an HTTP client, there is no reason to use and register
|
||||
* the HTTP server components.
|
||||
*/
|
||||
private void registerReactiveRequestMappingHandlerMappingIfNecessary(BeanDefinitionRegistry registry) {
|
||||
if (HttpContextUtils.WEB_FLUX_PRESENT &&
|
||||
!registry.containsBeanDefinition(HttpContextUtils.REACTIVE_HANDLER_MAPPING_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder requestMappingBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ReactiveIntegrationRequestMappingHandlerMapping.class);
|
||||
requestMappingBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
requestMappingBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, 0);
|
||||
registry.registerBeanDefinition(HttpContextUtils.REACTIVE_HANDLER_MAPPING_BEAN_NAME,
|
||||
requestMappingBuilder.getBeanDefinition());
|
||||
|
||||
BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
new RootBeanDefinition(IntegrationHandlerResultHandler.class), registry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ public class HttpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
registerBeanDefinitionParser("inbound-gateway", new HttpInboundEndpointParser(true));
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new HttpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-gateway", new HttpOutboundGatewayParser());
|
||||
registerBeanDefinitionParser("outbound-reactive-channel-adapter", new HttpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-reactive-gateway", new HttpOutboundGatewayParser());
|
||||
registerBeanDefinitionParser("graph-controller", new IntegrationGraphControllerParser());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.springframework.integration.http.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -42,41 +42,13 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder;
|
||||
boolean reactive = element.getLocalName().contains("reactive");
|
||||
if (reactive) {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(ReactiveHttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
else {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
BeanDefinitionBuilder builder = getBuilder(element, parserContext);
|
||||
|
||||
builder.addPropertyValue("expectReply", false);
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
if (reactive) {
|
||||
String webClientRef = element.getAttribute("web-client");
|
||||
|
||||
if (StringUtils.hasText(webClientRef)) {
|
||||
builder.addConstructorArgReference(webClientRef);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String headerMapper = element.getAttribute("header-mapper");
|
||||
String mappedRequestHeaders = element.getAttribute("mapped-request-headers");
|
||||
if (StringUtils.hasText(headerMapper)) {
|
||||
@@ -101,4 +73,24 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
|
||||
String restTemplateRef = element.getAttribute("rest-template");
|
||||
|
||||
if (StringUtils.hasText(restTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.getBeanDefinition()
|
||||
.getConstructorArgumentValues()
|
||||
.addIndexedArgumentValue(1, new RuntimeBeanReference(restTemplateRef));
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ package org.springframework.integration.http.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
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.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -44,39 +44,12 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder;
|
||||
boolean reactive = element.getLocalName().contains("reactive");
|
||||
if (reactive) {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(ReactiveHttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
else {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
}
|
||||
BeanDefinitionBuilder builder = getBuilder(element, parserContext);
|
||||
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
if (reactive) {
|
||||
String webClientRef = element.getAttribute("web-client");
|
||||
if (StringUtils.hasText(webClientRef)) {
|
||||
builder.addConstructorArgReference(webClientRef);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String headerMapper = element.getAttribute("header-mapper");
|
||||
String mappedRequestHeaders = element.getAttribute("mapped-request-headers");
|
||||
String mappedResponseHeaders = element.getAttribute("mapped-response-headers");
|
||||
@@ -113,4 +86,24 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
|
||||
|
||||
String restTemplateRef = element.getAttribute("rest-template");
|
||||
|
||||
if (StringUtils.hasText(restTemplateRef)) {
|
||||
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
|
||||
builder.getBeanDefinition()
|
||||
.getConstructorArgumentValues()
|
||||
.addIndexedArgumentValue(1, new RuntimeBeanReference(restTemplateRef));
|
||||
}
|
||||
else {
|
||||
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public abstract class BaseHttpMessageHandlerSpec<S extends BaseHttpMessageHandle
|
||||
this.target.setHeaderMapper(this.headerMapper);
|
||||
}
|
||||
|
||||
S expectReply(boolean expectReply) {
|
||||
protected S expectReply(boolean expectReply) {
|
||||
this.target.setExpectReply(expectReply);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@@ -24,12 +24,10 @@ import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.http.inbound.HttpRequestHandlingController;
|
||||
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
|
||||
import org.springframework.integration.http.inbound.ReactiveHttpInboundEndpoint;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* The HTTP components Factory.
|
||||
@@ -128,98 +126,6 @@ public final class Http {
|
||||
return new HttpMessageHandlerSpec(uriExpression, restTemplate).expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(URI uri) {
|
||||
return outboundReactiveChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(String uri) {
|
||||
return outboundReactiveChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} 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 ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundReactiveChannelAdapter(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} 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 ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(Expression uriExpression) {
|
||||
return outboundReactiveChannelAdapter(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@link URI} and {@link WebClient}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(URI uri, WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uri, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code uri} and {@link WebClient}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(String uri, WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uri, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(Function<Message<P>, ?> uriFunction,
|
||||
WebClient webClient) {
|
||||
return outboundReactiveChannelAdapter(new FunctionExpression<>(uriFunction), webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveChannelAdapter(Expression uriExpression,
|
||||
WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uriExpression, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link HttpMessageHandlerSpec} builder for request-reply gateway based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
@@ -307,96 +213,6 @@ public final class Http {
|
||||
return new HttpMessageHandlerSpec(uriExpression, restTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(URI uri) {
|
||||
return outboundReactiveGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(String uri) {
|
||||
return outboundReactiveGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} 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 ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> ReactiveHttpMessageHandlerSpec outboundReactiveGateway(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundReactiveGateway(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} 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 ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(Expression uriExpression) {
|
||||
return outboundReactiveGateway(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@link URI} and {@link WebClient}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(URI uri, WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uri, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code uri} and {@link WebClient}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(String uri, WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uri, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> ReactiveHttpMessageHandlerSpec outboundReactiveGateway(Function<Message<P>, ?> uriFunction,
|
||||
WebClient webClient) {
|
||||
return outboundReactiveGateway(new FunctionExpression<>(uriFunction), webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the ReactiveHttpMessageHandlerSpec instance
|
||||
*/
|
||||
public static ReactiveHttpMessageHandlerSpec outboundReactiveGateway(Expression uriExpression,
|
||||
WebClient webClient) {
|
||||
return new ReactiveHttpMessageHandlerSpec(uriExpression, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link HttpControllerEndpointSpec} builder for one-way adapter
|
||||
* based on the provided MVC {@code viewName} and {@code path} array for mapping.
|
||||
@@ -468,28 +284,6 @@ public final class Http {
|
||||
return new HttpRequestHandlerEndpointSpec(new HttpRequestHandlingMessagingGateway(), path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpInboundEndpointSpec} builder for one-way reactive adapter
|
||||
* based on the provided {@code path} array for mapping.
|
||||
* @param path the path mapping URIs (e.g. "/myPath.do").
|
||||
* @return the ReactiveHttpInboundEndpointSpec instance
|
||||
*/
|
||||
public static ReactiveHttpInboundEndpointSpec inboundReactiveChannelAdapter(String... path) {
|
||||
ReactiveHttpInboundEndpoint httpInboundChannelAdapter = new ReactiveHttpInboundEndpoint(false);
|
||||
return new ReactiveHttpInboundEndpointSpec(httpInboundChannelAdapter, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ReactiveHttpInboundEndpointSpec} builder for request-reply reactive gateway
|
||||
* based on the provided {@code path} array for mapping.
|
||||
* @param path the path mapping URIs (e.g. "/myPath.do").
|
||||
* @return the ReactiveHttpInboundEndpointSpec instance
|
||||
*/
|
||||
public static ReactiveHttpInboundEndpointSpec inboundReactiveGateway(String... path) {
|
||||
return new ReactiveHttpInboundEndpointSpec(new ReactiveHttpInboundEndpoint(), path);
|
||||
}
|
||||
|
||||
private Http() {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public abstract class HttpInboundEndpointSupportSpec<S extends HttpInboundEndpoi
|
||||
|
||||
private HeaderMapper<HttpHeaders> explicitHeaderMapper;
|
||||
|
||||
HttpInboundEndpointSupportSpec(E gateway, String... path) {
|
||||
protected HttpInboundEndpointSupportSpec(E gateway, String... path) {
|
||||
super(gateway);
|
||||
this.requestMapping.setPathPatterns(path);
|
||||
this.target.setRequestMapping(this.requestMapping);
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
* elements, if there is none registered, yet. However it can be configured as a regular
|
||||
* bean with appropriate configuration for {@link RequestMappingHandlerMapping}.
|
||||
* It is recommended to have only one similar bean in the application context using the 'id'
|
||||
* {@link org.springframework.integration.http.support.HttpContextUtils#REACTIVE_HANDLER_MAPPING_BEAN_NAME}.
|
||||
* {@link org.springframework.integration.http.support.HttpContextUtils#HANDLER_MAPPING_BEAN_NAME}.
|
||||
* <p>
|
||||
* In most cases, Spring MVC offers to configure Request Mapping via
|
||||
* {@code org.springframework.stereotype.Controller} and
|
||||
|
||||
@@ -48,27 +48,12 @@ public final class HttpContextUtils {
|
||||
ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet",
|
||||
HttpContextUtils.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* The {@code boolean} flag to indicate if the
|
||||
* {@code org.springframework.web.reactive.result.method.RequestMappingInfo}
|
||||
* is present in the CLASSPATH to allow to register the Integration server reactive components.
|
||||
*/
|
||||
public static final boolean WEB_FLUX_PRESENT =
|
||||
ClassUtils.isPresent("org.springframework.web.reactive.result.method.RequestMappingInfo",
|
||||
HttpContextUtils.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* The name for the infrastructure
|
||||
* {@link org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping} bean.
|
||||
*/
|
||||
public static final String HANDLER_MAPPING_BEAN_NAME = "integrationRequestMappingHandlerMapping";
|
||||
|
||||
/**
|
||||
* The name for the infrastructure
|
||||
* {@link org.springframework.integration.http.inbound.ReactiveIntegrationRequestMappingHandlerMapping} bean.
|
||||
*/
|
||||
public static final String REACTIVE_HANDLER_MAPPING_BEAN_NAME = "reactiveIntegrationRequestMappingHandlerMapping";
|
||||
|
||||
/**
|
||||
* Represents the environment property for the {@code IntegrationGraphController} request mapping path.
|
||||
*/
|
||||
|
||||
@@ -401,55 +401,6 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="outbound-reactive-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler'
|
||||
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:attribute name="web-client" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.reactive.function.client.WebClient" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A reference to an org.springframework.web.reactive.function.client.WebClient bean
|
||||
which is used to send to send the HTTP Requests reactive manner.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<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-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -544,114 +495,6 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="outbound-reactive-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler'
|
||||
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:attribute name="web-client" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.reactive.function.client.WebClient" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A reference to an org.springframework.web.reactive.function.client.WebClient bean
|
||||
which is used to send to send the HTTP Requests reactive manner.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="graph-controller">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -17,16 +17,8 @@
|
||||
<outbound-channel-adapter id="restTemplateConfig" url="http://localhost/test1" channel="requests"
|
||||
rest-template="customRestTemplate"/>
|
||||
|
||||
<outbound-reactive-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"/>
|
||||
|
||||
<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="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"
|
||||
|
||||
@@ -58,7 +58,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -85,21 +84,10 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
@Qualifier("restTemplateConfig")
|
||||
private AbstractEndpoint restTemplateConfig;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveMinimalConfig")
|
||||
private AbstractEndpoint reactiveMinimalConfig;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveWebClientConfig")
|
||||
private AbstractEndpoint reactiveWebClientConfig;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("customRestTemplate")
|
||||
private RestTemplate customRestTemplate;
|
||||
|
||||
@Autowired
|
||||
private WebClient webClient;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("withUrlAndTemplate")
|
||||
private AbstractEndpoint withUrlAndTemplate;
|
||||
@@ -187,25 +175,6 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
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"));
|
||||
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 =
|
||||
@@ -213,11 +182,6 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
assertEquals(customRestTemplate, restTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveWebClientConfig() {
|
||||
assertSame(this.webClient, TestUtils.getPropertyValue(this.reactiveWebClientConfig, "handler.webClient"));
|
||||
}
|
||||
|
||||
@Test(expected = BeanDefinitionParsingException.class)
|
||||
public void failWithRestTemplateAndRestAttributes() {
|
||||
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", this.getClass())
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<si:channel id="replies">
|
||||
@@ -43,26 +40,6 @@
|
||||
<uri-variable name="foo" expression="headers.bar"/>
|
||||
</outbound-gateway>
|
||||
|
||||
<outbound-reactive-gateway id="reactiveMinimalConfig" url="http://localhost/test1" request-channel="requests"
|
||||
web-client="webClient"/>
|
||||
|
||||
<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-reactive-gateway>
|
||||
|
||||
<util:map id="uriVariables">
|
||||
<beans:entry key="foo1" value="bar1"/>
|
||||
<beans:entry key="foo2" value="bar2"/>
|
||||
|
||||
@@ -19,7 +19,6 @@ 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;
|
||||
@@ -58,7 +57,6 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -79,14 +77,6 @@ public class HttpOutboundGatewayParserTests {
|
||||
@Qualifier("fullConfig")
|
||||
private AbstractEndpoint fullConfigEndpoint;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveMinimalConfig")
|
||||
private AbstractEndpoint reactiveMinimalConfigEndpoint;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveFullConfig")
|
||||
private AbstractEndpoint reactiveFullConfigEndpoint;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("withUrlExpression")
|
||||
private AbstractEndpoint withUrlExpressionEndpoint;
|
||||
@@ -99,9 +89,6 @@ public class HttpOutboundGatewayParserTests {
|
||||
@Qualifier("withPoller1")
|
||||
private AbstractEndpoint withPoller1;
|
||||
|
||||
@Autowired
|
||||
private WebClient webClient;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -178,66 +165,6 @@ public class HttpOutboundGatewayParserTests {
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
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(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
|
||||
assertEquals(false, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void reactiveFullConfig() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.reactiveFullConfigEndpoint);
|
||||
Object handler = endpointAccessor.getPropertyValue("handler");
|
||||
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
|
||||
this.reactiveFullConfigEndpoint).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);
|
||||
|
||||
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 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(
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.integration.http.dsl;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
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.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -30,28 +27,19 @@ import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
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.core.ResolvableType;
|
||||
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.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.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
@@ -63,20 +51,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.reactive.server.HttpHandlerConnector;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
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.RestTemplate;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
@@ -95,23 +74,14 @@ public class HttpDslTests {
|
||||
@Autowired
|
||||
private HttpRequestExecutingMessageHandler serviceInternalGatewayHandler;
|
||||
|
||||
@Autowired
|
||||
private ReactiveHttpRequestExecutingMessageHandler serviceInternalReactiveGatewayHandler;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc =
|
||||
MockMvcBuilders.webAppContextSetup(this.wac)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
|
||||
this.webTestClient =
|
||||
WebTestClient.bindToApplicationContext(this.wac)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -130,71 +100,7 @@ public class HttpDslTests {
|
||||
.string("FOO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
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(Mono.defer(response::setComplete));
|
||||
});
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
.clientConnector(httpConnector)
|
||||
.build();
|
||||
|
||||
new DirectFieldAccessor(this.serviceInternalReactiveGatewayHandler)
|
||||
.setPropertyValue("webClient", webClient);
|
||||
|
||||
this.mockMvc.perform(
|
||||
get("/service2")
|
||||
.with(httpBasic("guest", "guest"))
|
||||
.param("name", "foo"))
|
||||
.andExpect(
|
||||
content()
|
||||
.string("FOO"));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private PollableChannel storeChannel;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testHttpReactivePost() {
|
||||
this.webTestClient.post().uri("/reactivePost")
|
||||
.body(Flux.just("foo", "bar", "baz"), String.class)
|
||||
.exchange()
|
||||
.expectStatus().isAccepted();
|
||||
|
||||
Message<?> store = this.storeChannel.receive(10_000);
|
||||
assertNotNull(store);
|
||||
assertThat(store.getPayload(), instanceOf(Flux.class));
|
||||
|
||||
StepVerifier
|
||||
.create((Publisher<String>) store.getPayload())
|
||||
.expectNext("foo", "bar", "baz")
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSse() {
|
||||
Flux<String> responseBody =
|
||||
this.webTestClient.get().uri("/sse")
|
||||
.exchange()
|
||||
.returnResult(String.class)
|
||||
.getResponseBody();
|
||||
|
||||
StepVerifier
|
||||
.create(responseBody)
|
||||
.expectNext("foo", "bar", "baz")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableWebSecurity
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@@ -247,42 +153,6 @@ public class HttpDslTests {
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow httpReactiveProxyFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Http.inboundGateway("/service2")
|
||||
.requestMapping(r -> r.params("name")))
|
||||
.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("serviceInternalReactiveGateway"))
|
||||
.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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDecisionManager accessDecisionManager() {
|
||||
return new AffirmativeBased(Collections.singletonList(new RoleVoter()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration/ip"
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration/syslog"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.webflux.config;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.integration.config.IntegrationConfigurationInitializer;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.webflux.inbound.IntegrationHandlerResultHandler;
|
||||
import org.springframework.integration.webflux.inbound.WebFluxIntegrationRequestMappingHandlerMapping;
|
||||
import org.springframework.integration.webflux.support.WebFluxContextUtils;
|
||||
|
||||
/**
|
||||
* The WebFlux Integration infrastructure {@code beanFactory} initializer.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class WebFluxIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebFluxIntegrationConfigurationInitializer.class);
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
registerReactiveRequestMappingHandlerMappingIfNecessary((BeanDefinitionRegistry) beanFactory);
|
||||
}
|
||||
else {
|
||||
logger.warn("'IntegrationRequestMappingHandlerMapping' isn't registered because 'beanFactory'" +
|
||||
" isn't an instance of `BeanDefinitionRegistry`.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link WebFluxIntegrationRequestMappingHandlerMapping}
|
||||
* which could also be overridden by the user by simply registering
|
||||
* a {@link WebFluxIntegrationRequestMappingHandlerMapping} {@code <bean>} with 'id'
|
||||
* {@link WebFluxContextUtils#HANDLER_MAPPING_BEAN_NAME}.
|
||||
* <p>
|
||||
* In addition, checks if the {@code org.springframework.web.reactive.result.method.RequestMappingInfo}
|
||||
* class is present on the classpath.
|
||||
* When Spring Integration HTTP is used only as an HTTP client, there is no reason to use and register
|
||||
* the HTTP server components.
|
||||
*/
|
||||
private void registerReactiveRequestMappingHandlerMappingIfNecessary(BeanDefinitionRegistry registry) {
|
||||
if (WebFluxContextUtils.WEB_FLUX_PRESENT &&
|
||||
!registry.containsBeanDefinition(WebFluxContextUtils.HANDLER_MAPPING_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder requestMappingBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(WebFluxIntegrationRequestMappingHandlerMapping.class);
|
||||
requestMappingBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
requestMappingBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, 0);
|
||||
registry.registerBeanDefinition(WebFluxContextUtils.HANDLER_MAPPING_BEAN_NAME,
|
||||
requestMappingBuilder.getBeanDefinition());
|
||||
|
||||
BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
new RootBeanDefinition(IntegrationHandlerResultHandler.class), registry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.webflux.config;
|
||||
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
|
||||
/**
|
||||
* Namespace handler for Spring Integration's <em>webflux</em> namespace.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class WebFluxNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new WebFluxOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-gateway", new WebFluxOutboundGatewayParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.webflux.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.http.config.HttpOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the 'outbound-channel-adapter' element of the webflux namespace.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class WebFluxOutboundChannelAdapterParser extends HttpOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(WebFluxRequestExecutingMessageHandler.class);
|
||||
|
||||
String webClientRef = element.getAttribute("web-client");
|
||||
if (StringUtils.hasText(webClientRef)) {
|
||||
builder.getBeanDefinition()
|
||||
.getConstructorArgumentValues()
|
||||
.addIndexedArgumentValue(1, new RuntimeBeanReference(webClientRef));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.webflux.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.http.config.HttpOutboundGatewayParser;
|
||||
import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the 'outbound-gateway' element of the webflux namespace.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class WebFluxOutboundGatewayParser extends HttpOutboundGatewayParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(WebFluxRequestExecutingMessageHandler.class);
|
||||
|
||||
String webClientRef = element.getAttribute("web-client");
|
||||
if (StringUtils.hasText(webClientRef)) {
|
||||
builder.getBeanDefinition()
|
||||
.getConstructorArgumentValues()
|
||||
.addIndexedArgumentValue(1, new RuntimeBeanReference(webClientRef));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes for configuration - parsers, namespace handlers.
|
||||
*/
|
||||
package org.springframework.integration.webflux.config;
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.webflux.dsl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.webflux.inbound.WebFluxInboundEndpoint;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* The WebFlux components Factory.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class WebFlux {
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(URI uri) {
|
||||
return outboundChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(String uri) {
|
||||
return outboundChannelAdapter(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} 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 WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> WebFluxMessageHandlerSpec outboundChannelAdapter(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundChannelAdapter(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} 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 WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(Expression uriExpression) {
|
||||
return outboundChannelAdapter(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@link URI} and {@link WebClient}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(URI uri, WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uri, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code uri} and {@link WebClient}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(String uri, WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uri, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> WebFluxMessageHandlerSpec outboundChannelAdapter(Function<Message<P>, ?> uriFunction,
|
||||
WebClient webClient) {
|
||||
return outboundChannelAdapter(new FunctionExpression<>(uriFunction), webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for one-way adapter
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundChannelAdapter(Expression uriExpression,
|
||||
WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uriExpression, webClient)
|
||||
.expectReply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@link URI}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(URI uri) {
|
||||
return outboundGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code uri}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(String uri) {
|
||||
return outboundGateway(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} 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 WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> WebFluxMessageHandlerSpec outboundGateway(Function<Message<P>, ?> uriFunction) {
|
||||
return outboundGateway(new FunctionExpression<>(uriFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} 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 WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(Expression uriExpression) {
|
||||
return outboundGateway(uriExpression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@link URI} and {@link WebClient}.
|
||||
* @param uri the {@link URI} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(URI uri, WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uri, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code uri} and {@link WebClient}.
|
||||
* @param uri the {@code uri} to send requests.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(String uri, WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uri, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided {@code Function} to evaluate target {@code uri} against request message
|
||||
* and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriFunction the {@code Function} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @param <P> the expected payload type.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static <P> WebFluxMessageHandlerSpec outboundGateway(Function<Message<P>, ?> uriFunction,
|
||||
WebClient webClient) {
|
||||
return outboundGateway(new FunctionExpression<>(uriFunction), webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxMessageHandlerSpec} builder for request-reply gateway
|
||||
* based on provided SpEL {@link Expression} to evaluate target {@code uri}
|
||||
* against request message and {@link WebClient} for HTTP exchanges.
|
||||
* @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime.
|
||||
* @param webClient {@link WebClient} to use.
|
||||
* @return the WebFluxMessageHandlerSpec instance
|
||||
*/
|
||||
public static WebFluxMessageHandlerSpec outboundGateway(Expression uriExpression,
|
||||
WebClient webClient) {
|
||||
return new WebFluxMessageHandlerSpec(uriExpression, webClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxInboundEndpointSpec} builder for one-way reactive adapter
|
||||
* based on the provided {@code path} array for mapping.
|
||||
* @param path the path mapping URIs (e.g. "/myPath.do").
|
||||
* @return the WebFluxInboundEndpointSpec instance
|
||||
*/
|
||||
public static WebFluxInboundEndpointSpec inboundChannelAdapter(String... path) {
|
||||
WebFluxInboundEndpoint httpInboundChannelAdapter = new WebFluxInboundEndpoint(false);
|
||||
return new WebFluxInboundEndpointSpec(httpInboundChannelAdapter, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link WebFluxInboundEndpointSpec} builder for request-reply reactive gateway
|
||||
* based on the provided {@code path} array for mapping.
|
||||
* @param path the path mapping URIs (e.g. "/myPath.do").
|
||||
* @return the WebFluxInboundEndpointSpec instance
|
||||
*/
|
||||
public static WebFluxInboundEndpointSpec inboundGateway(String... path) {
|
||||
return new WebFluxInboundEndpointSpec(new WebFluxInboundEndpoint(), path);
|
||||
}
|
||||
|
||||
private WebFlux() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,40 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.dsl;
|
||||
package org.springframework.integration.webflux.dsl;
|
||||
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.integration.http.inbound.ReactiveHttpInboundEndpoint;
|
||||
import org.springframework.integration.http.dsl.HttpInboundEndpointSupportSpec;
|
||||
import org.springframework.integration.webflux.inbound.WebFluxInboundEndpoint;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
|
||||
/**
|
||||
* The {@link HttpInboundEndpointSupportSpec} implementation for the {@link ReactiveHttpInboundEndpoint}.
|
||||
* The {@link HttpInboundEndpointSupportSpec} implementation for the {@link WebFluxInboundEndpoint}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ReactiveHttpInboundEndpointSpec
|
||||
extends HttpInboundEndpointSupportSpec<ReactiveHttpInboundEndpointSpec, ReactiveHttpInboundEndpoint> {
|
||||
public class WebFluxInboundEndpointSpec
|
||||
extends HttpInboundEndpointSupportSpec<WebFluxInboundEndpointSpec, WebFluxInboundEndpoint> {
|
||||
|
||||
ReactiveHttpInboundEndpointSpec(ReactiveHttpInboundEndpoint gateway, String... path) {
|
||||
WebFluxInboundEndpointSpec(WebFluxInboundEndpoint gateway, String... path) {
|
||||
super(gateway, path);
|
||||
}
|
||||
|
||||
public ReactiveHttpInboundEndpointSpec codecConfigurer(ServerCodecConfigurer codecConfigurer) {
|
||||
public WebFluxInboundEndpointSpec codecConfigurer(ServerCodecConfigurer codecConfigurer) {
|
||||
this.target.setCodecConfigurer(codecConfigurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReactiveHttpInboundEndpointSpec requestedContentTypeResolver(
|
||||
public WebFluxInboundEndpointSpec requestedContentTypeResolver(
|
||||
RequestedContentTypeResolver requestedContentTypeResolver) {
|
||||
|
||||
this.target.setRequestedContentTypeResolver(requestedContentTypeResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReactiveHttpInboundEndpointSpec reactiveAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {
|
||||
public WebFluxInboundEndpointSpec reactiveAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {
|
||||
this.target.setReactiveAdapterRegistry(adapterRegistry);
|
||||
return this;
|
||||
}
|
||||
@@ -14,41 +14,42 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.dsl;
|
||||
package org.springframework.integration.webflux.dsl;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.http.outbound.ReactiveHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.http.dsl.BaseHttpMessageHandlerSpec;
|
||||
import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* The {@link BaseHttpMessageHandlerSpec} implementation for the {@link ReactiveHttpRequestExecutingMessageHandler}.
|
||||
* The {@link BaseHttpMessageHandlerSpec} implementation for the {@link WebFluxRequestExecutingMessageHandler}.
|
||||
*
|
||||
* @author Shiliang Li
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see ReactiveHttpRequestExecutingMessageHandler
|
||||
* @see WebFluxRequestExecutingMessageHandler
|
||||
*/
|
||||
public class ReactiveHttpMessageHandlerSpec
|
||||
extends BaseHttpMessageHandlerSpec<ReactiveHttpMessageHandlerSpec, ReactiveHttpRequestExecutingMessageHandler> {
|
||||
public class WebFluxMessageHandlerSpec
|
||||
extends BaseHttpMessageHandlerSpec<WebFluxMessageHandlerSpec, WebFluxRequestExecutingMessageHandler> {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
ReactiveHttpMessageHandlerSpec(URI uri, WebClient webClient) {
|
||||
WebFluxMessageHandlerSpec(URI uri, WebClient webClient) {
|
||||
this(new ValueExpression<>(uri), webClient);
|
||||
}
|
||||
|
||||
ReactiveHttpMessageHandlerSpec(String uri, WebClient webClient) {
|
||||
WebFluxMessageHandlerSpec(String uri, WebClient webClient) {
|
||||
this(new LiteralExpression(uri), webClient);
|
||||
}
|
||||
|
||||
ReactiveHttpMessageHandlerSpec(Expression uriExpression, WebClient webClient) {
|
||||
super(new ReactiveHttpRequestExecutingMessageHandler(uriExpression, webClient));
|
||||
WebFluxMessageHandlerSpec(Expression uriExpression, WebClient webClient) {
|
||||
super(new WebFluxRequestExecutingMessageHandler(uriExpression, webClient));
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
@@ -57,4 +58,9 @@ public class ReactiveHttpMessageHandlerSpec
|
||||
return this.webClient != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebFluxMessageHandlerSpec expectReply(boolean expectReply) {
|
||||
return super.expectReply(expectReply);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides WebFlux Components support for Spring Integration Java DSL.
|
||||
*/
|
||||
package org.springframework.integration.webflux.dsl;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.inbound;
|
||||
package org.springframework.integration.webflux.inbound;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
@@ -26,14 +26,14 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* A {@link HandlerResultHandler} implementation to handle the result of the
|
||||
* {@link ReactiveHttpInboundEndpoint} execution. Actually just return the
|
||||
* {@link WebFluxInboundEndpoint} execution. Actually just return the
|
||||
* {@code result.getReturnValue()} which essentially is expected {@code Mono<Void>}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see ReactiveHttpInboundEndpoint
|
||||
* @see WebFluxInboundEndpoint
|
||||
*/
|
||||
public class IntegrationHandlerResultHandler implements HandlerResultHandler, Ordered {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class IntegrationHandlerResultHandler implements HandlerResultHandler, Or
|
||||
public boolean supports(HandlerResult result) {
|
||||
Object handler = result.getHandler();
|
||||
return handler instanceof HandlerMethod
|
||||
&& ReactiveHttpInboundEndpoint.class.isAssignableFrom(((HandlerMethod) handler).getBeanType());
|
||||
&& WebFluxInboundEndpoint.class.isAssignableFrom(((HandlerMethod) handler).getBeanType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.inbound;
|
||||
package org.springframework.integration.webflux.inbound;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -43,6 +43,7 @@ import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.integration.http.inbound.BaseHttpInboundEndpoint;
|
||||
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -70,7 +71,7 @@ import reactor.core.publisher.Mono;
|
||||
* @see org.springframework.web.reactive.result.HandlerResultHandlerSupport
|
||||
* @see org.springframework.web.reactive.config.EnableWebFlux
|
||||
*/
|
||||
public class ReactiveHttpInboundEndpoint extends BaseHttpInboundEndpoint implements WebHandler {
|
||||
public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements WebHandler {
|
||||
|
||||
private static final MediaType MEDIA_TYPE_APPLICATION_ALL = new MediaType("application");
|
||||
|
||||
@@ -80,11 +81,11 @@ public class ReactiveHttpInboundEndpoint extends BaseHttpInboundEndpoint impleme
|
||||
|
||||
private ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry();
|
||||
|
||||
public ReactiveHttpInboundEndpoint() {
|
||||
public WebFluxInboundEndpoint() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public ReactiveHttpInboundEndpoint(boolean expectReply) {
|
||||
public WebFluxInboundEndpoint(boolean expectReply) {
|
||||
super(expectReply);
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ public class ReactiveHttpInboundEndpoint extends BaseHttpInboundEndpoint impleme
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return super.getComponentType().replaceFirst("(http:)", "$1reactive-");
|
||||
return super.getComponentType().replaceFirst("(http:)", "$1webflux-");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -14,14 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.inbound;
|
||||
package org.springframework.integration.webflux.inbound;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.integration.http.inbound.BaseHttpInboundEndpoint;
|
||||
import org.springframework.integration.http.inbound.CrossOrigin;
|
||||
import org.springframework.integration.http.inbound.RequestMapping;
|
||||
import org.springframework.integration.http.support.HttpContextUtils;
|
||||
import org.springframework.integration.webflux.support.WebFluxContextUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -33,18 +37,19 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
|
||||
/**
|
||||
* The {@link org.springframework.web.servlet.HandlerMapping} implementation that
|
||||
* detects and registers {@link org.springframework.web.servlet.mvc.method.RequestMappingInfo}s for
|
||||
* {@link HttpRequestHandlingEndpointSupport} from a Spring Integration HTTP configuration
|
||||
* The {@link org.springframework.web.reactive.HandlerMapping} implementation that
|
||||
* detects and registers {@link org.springframework.web.reactive.result.method.RequestMappingInfo}s for
|
||||
* {@link org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport}
|
||||
* from a Spring Integration HTTP configuration
|
||||
* of {@code <inbound-channel-adapter/>} and {@code <inbound-gateway/>} elements.
|
||||
* <p>
|
||||
* This class is automatically configured as a bean in the application context during the
|
||||
* parsing phase of the {@code <reactive-inbound-gateway/>}
|
||||
* parsing phase of the {@code <inbound-gateway/>}
|
||||
* elements, if there is none registered, yet. However it can be configured as a regular
|
||||
* bean with appropriate configuration for
|
||||
* {@link org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping}.
|
||||
* It is recommended to have only one similar bean in the application context using the 'id'
|
||||
* {@link org.springframework.integration.http.support.HttpContextUtils#REACTIVE_HANDLER_MAPPING_BEAN_NAME}.
|
||||
* {@link WebFluxContextUtils#HANDLER_MAPPING_BEAN_NAME}.
|
||||
* <p>
|
||||
* In most cases, Spring MVC offers to configure Request Mapping via
|
||||
* {@code org.springframework.stereotype.Controller} and
|
||||
@@ -54,10 +59,10 @@ import org.springframework.web.server.WebHandler;
|
||||
* {@code org.springframework.stereotype.Controller} user-class may have their own
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping}.
|
||||
* On the other side, all Spring Integration HTTP Inbound Endpoints are configured on
|
||||
* the basis of the same {@link HttpRequestHandlingEndpointSupport} class and there is no
|
||||
* single {@link org.springframework.web.reactive.result.method.RequestMappingInfo} configuration without
|
||||
* {@link org.springframework.web.method.HandlerMethod} in Spring MVC.
|
||||
* Accordingly {@link ReactiveIntegrationRequestMappingHandlerMapping} is a
|
||||
* the basis of the same {@link org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport}
|
||||
* class and there is no single {@link org.springframework.web.reactive.result.method.RequestMappingInfo}
|
||||
* configuration without {@link org.springframework.web.method.HandlerMethod} in Spring MVC.
|
||||
* Accordingly {@link WebFluxIntegrationRequestMappingHandlerMapping} is a
|
||||
* {@link org.springframework.web.reactive.HandlerMapping}
|
||||
* compromise implementation between method-level annotations and component-level
|
||||
* (e.g. Spring Integration XML) configurations.
|
||||
@@ -69,7 +74,7 @@ import org.springframework.web.server.WebHandler;
|
||||
* @see RequestMapping
|
||||
* @see RequestMappingHandlerMapping
|
||||
*/
|
||||
public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMappingHandlerMapping
|
||||
public class WebFluxIntegrationRequestMappingHandlerMapping extends RequestMappingHandlerMapping
|
||||
implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private static final Method HANDLER_METHOD = ReflectionUtils.findMethod(WebHandler.class,
|
||||
@@ -79,7 +84,7 @@ public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMapp
|
||||
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
return ReactiveHttpInboundEndpoint.class.isAssignableFrom(beanType);
|
||||
return WebFluxInboundEndpoint.class.isAssignableFrom(beanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +92,7 @@ public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMapp
|
||||
if (handler instanceof String) {
|
||||
handler = getApplicationContext().getBean((String) handler);
|
||||
}
|
||||
RequestMappingInfo mapping = getMappingForEndpoint((ReactiveHttpInboundEndpoint) handler);
|
||||
RequestMappingInfo mapping = getMappingForEndpoint((WebFluxInboundEndpoint) handler);
|
||||
if (mapping != null) {
|
||||
registerMapping(mapping, handler, HANDLER_METHOD);
|
||||
}
|
||||
@@ -98,7 +103,7 @@ public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMapp
|
||||
* a Spring Integration Reactive HTTP Inbound Endpoint {@link RequestMapping}.
|
||||
* @see RequestMappingHandlerMapping#getMappingForMethod
|
||||
*/
|
||||
private RequestMappingInfo getMappingForEndpoint(ReactiveHttpInboundEndpoint endpoint) {
|
||||
private RequestMappingInfo getMappingForEndpoint(WebFluxInboundEndpoint endpoint) {
|
||||
org.springframework.web.bind.annotation.RequestMapping requestMappingAnnotation =
|
||||
HttpContextUtils.convertRequestMappingToAnnotation(endpoint.getRequestMapping());
|
||||
if (requestMappingAnnotation != null) {
|
||||
@@ -149,16 +154,11 @@ public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMapp
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
// No-op in favor of onApplicationEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpRequestHandlingEndpointSupport}s may depend on auto-created
|
||||
* {@code requestChannel}s, so MVC Handlers detection should be postponed
|
||||
* {@link org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport}s
|
||||
* may depend on auto-created {@code requestChannel}s, so MVC Handlers detection should be postponed
|
||||
* as late as possible.
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet()
|
||||
* @see RequestMappingHandlerMapping#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
@@ -167,4 +167,9 @@ public class ReactiveIntegrationRequestMappingHandlerMapping extends RequestMapp
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
// No-op in favor of onApplicationEvent
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes supporting inbound endpoints.
|
||||
*/
|
||||
package org.springframework.integration.webflux.inbound;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
package org.springframework.integration.webflux.outbound;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -32,6 +32,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.http.outbound.AbstractHttpRequestExecutingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -54,9 +55,9 @@ import reactor.core.publisher.Mono;
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see HttpRequestExecutingMessageHandler
|
||||
* @see org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler
|
||||
*/
|
||||
public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequestExecutingMessageHandler {
|
||||
public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestExecutingMessageHandler {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
* @param uri The URI.
|
||||
*/
|
||||
public ReactiveHttpRequestExecutingMessageHandler(URI uri) {
|
||||
public WebFluxRequestExecutingMessageHandler(URI uri) {
|
||||
this(new ValueExpression<>(uri));
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
* @param uri The URI.
|
||||
*/
|
||||
public ReactiveHttpRequestExecutingMessageHandler(String uri) {
|
||||
public WebFluxRequestExecutingMessageHandler(String uri) {
|
||||
this(uri, null);
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
* Create a handler that will send requests to the provided URI Expression.
|
||||
* @param uriExpression The URI expression.
|
||||
*/
|
||||
public ReactiveHttpRequestExecutingMessageHandler(Expression uriExpression) {
|
||||
public WebFluxRequestExecutingMessageHandler(Expression uriExpression) {
|
||||
this(uriExpression, null);
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
* @param uri The URI.
|
||||
* @param webClient The WebClient to use.
|
||||
*/
|
||||
public ReactiveHttpRequestExecutingMessageHandler(String uri, WebClient webClient) {
|
||||
public WebFluxRequestExecutingMessageHandler(String uri, WebClient webClient) {
|
||||
this(new LiteralExpression(uri), webClient);
|
||||
/*
|
||||
* We'd prefer to do this assertion first, but the compiler doesn't allow it. However,
|
||||
@@ -105,7 +106,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
* {@link BeanFactory}.
|
||||
* @param webClient The WebClient to use.
|
||||
*/
|
||||
public ReactiveHttpRequestExecutingMessageHandler(Expression uriExpression, WebClient webClient) {
|
||||
public WebFluxRequestExecutingMessageHandler(Expression uriExpression, WebClient webClient) {
|
||||
super(uriExpression);
|
||||
this.webClient = (webClient == null ? WebClient.create() : webClient);
|
||||
this.setAsync(true);
|
||||
@@ -113,7 +114,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (isExpectReply() ? "http:outbound-reactive-gateway" : "http:outbound-reactive-channel-adapter");
|
||||
return (isExpectReply() ? "webflux:outbound-gateway" : "webflux:outbound-channel-adapter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes supporting outbound endpoints.
|
||||
*/
|
||||
package org.springframework.integration.webflux.outbound;
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.webflux.support;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Utility class for accessing WebFlux integration components
|
||||
* from the {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class WebFluxContextUtils {
|
||||
|
||||
private WebFluxContextUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@code boolean} flag to indicate if the
|
||||
* {@code org.springframework.web.reactive.result.method.RequestMappingInfo}
|
||||
* is present in the CLASSPATH to allow to register the Integration server reactive components.
|
||||
*/
|
||||
public static final boolean WEB_FLUX_PRESENT =
|
||||
ClassUtils.isPresent("org.springframework.web.reactive.result.method.RequestMappingInfo",
|
||||
WebFluxContextUtils.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* The name for the infrastructure
|
||||
* {@link org.springframework.integration.webflux.inbound.WebFluxIntegrationRequestMappingHandlerMapping} bean.
|
||||
*/
|
||||
public static final String HANDLER_MAPPING_BEAN_NAME = "webFluxIntegrationRequestMappingHandlerMapping";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes to support WebFlux endpoints.
|
||||
*/
|
||||
package org.springframework.integration.webflux.support;
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.integration.config.IntegrationConfigurationInitializer=\
|
||||
org.springframework.integration.webflux.config.WebFluxIntegrationConfigurationInitializer
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/integration/webflux=org.springframework.integration.webflux.config.WebFluxNamespaceHandler
|
||||
@@ -0,0 +1,2 @@
|
||||
http\://www.springframework.org/schema/integration/webflux/spring-integration-webflux-5.0.xsd=org/springframework/integration/webflux/config/spring-integration-webflux-5.0.xsd
|
||||
http\://www.springframework.org/schema/integration/webflux/spring-integration-webflux.xsd=org/springframework/integration/webflux/config/spring-integration-webflux-5.0.xsd
|
||||
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the integration webflux namespace
|
||||
http\://www.springframework.org/schema/integration/webflux@name=integration webflux Namespace
|
||||
http\://www.springframework.org/schema/integration/webflux@prefix=int-webflux
|
||||
http\://www.springframework.org/schema/integration/webflux@icon=org/springframework/integration/webflux/config/spring-integration-webflux.gif
|
||||
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration/webflux"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-http="http://www.springframework.org/schema/integration/http"
|
||||
targetNamespace="http://www.springframework.org/schema/integration/webflux" elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-5.0.xsd" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration/http"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/http/spring-integration-http-5.0.xsd"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the configuration elements for Spring Integration's WebFlux adapters.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="outbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler'
|
||||
with 'expectReply = false' that sends HTTP requests based on incoming messages reactive manner.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:choice minOccurs="0" maxOccurs="3">
|
||||
<xsd:element name="uri-variable" type="int-http: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"/>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:choice>
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
<xsd:attributeGroup ref="int-http:httpOutboundCommonAttributes"/>
|
||||
<xsd:attribute name="web-client" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.web.reactive.function.client.WebClient"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A reference to an org.springframework.web.reactive.function.client.WebClient bean
|
||||
which is used to send to send the HTTP Requests reactive manner.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<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-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler'
|
||||
that sends HTTP requests based on incoming messages and expects HTTP responses.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="int-http:gatewayType">
|
||||
<xsd:choice minOccurs="0" maxOccurs="3">
|
||||
<xsd:element name="uri-variable" type="int-http: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"/>
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
|
||||
minOccurs="0"/>
|
||||
<xsd:element ref="integration:poller" minOccurs="0"/>
|
||||
</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="int-http:httpOutboundCommonAttributes"/>
|
||||
<xsd:attribute name="web-client" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.web.reactive.function.client.WebClient"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A reference to an org.springframework.web.reactive.function.client.WebClient bean
|
||||
which is used to send to send the HTTP Requests reactive manner.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 578 B |
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration/webflux"
|
||||
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"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/webflux http://www.springframework.org/schema/integration/webflux/spring-integration-webflux.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">
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
<outbound-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"/>
|
||||
|
||||
<outbound-channel-adapter id="reactiveWebClientConfig" url="http://localhost/test1" channel="requests"
|
||||
web-client="webClient"/>
|
||||
|
||||
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
|
||||
factory-method="create"/>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.webflux.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class HttpOutboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveMinimalConfig")
|
||||
private AbstractEndpoint reactiveMinimalConfig;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveWebClientConfig")
|
||||
private AbstractEndpoint reactiveWebClientConfig;
|
||||
|
||||
@Autowired
|
||||
private WebClient webClient;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
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"));
|
||||
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 reactiveWebClientConfig() {
|
||||
assertSame(this.webClient, TestUtils.getPropertyValue(this.reactiveWebClientConfig, "handler.webClient"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration/webflux"
|
||||
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"
|
||||
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">
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
|
||||
factory-method="create"/>
|
||||
|
||||
<si:channel id="replies">
|
||||
<si:queue/>
|
||||
</si:channel>
|
||||
|
||||
<outbound-gateway id="reactiveMinimalConfig" url="http://localhost/test1" request-channel="requests"
|
||||
web-client="webClient"/>
|
||||
|
||||
<outbound-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-gateway>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.webflux.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.assertTrue;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class HttpOutboundGatewayParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveMinimalConfig")
|
||||
private AbstractEndpoint reactiveMinimalConfigEndpoint;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveFullConfig")
|
||||
private AbstractEndpoint reactiveFullConfigEndpoint;
|
||||
|
||||
@Autowired
|
||||
private WebClient webClient;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
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);
|
||||
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(Charset.forName("UTF-8"), handlerAccessor.getPropertyValue("charset"));
|
||||
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
|
||||
assertEquals(false, handlerAccessor.getPropertyValue("transferCookies"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void reactiveFullConfig() {
|
||||
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.reactiveFullConfigEndpoint);
|
||||
Object handler = endpointAccessor.getPropertyValue("handler");
|
||||
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
|
||||
this.reactiveFullConfigEndpoint).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);
|
||||
|
||||
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 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"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.webflux.dsl;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
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.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
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.core.ResolvableType;
|
||||
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.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.http.dsl.Http;
|
||||
import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
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.reactive.server.HttpHandlerConnector;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Shiliang Li
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebAppConfiguration
|
||||
@DirtiesContext
|
||||
public class WebFluxDslTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
|
||||
@Autowired
|
||||
private WebFluxRequestExecutingMessageHandler serviceInternalReactiveGatewayHandler;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc =
|
||||
MockMvcBuilders.webAppContextSetup(this.wac)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
|
||||
this.webTestClient =
|
||||
WebTestClient.bindToApplicationContext(this.wac)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
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(Mono.defer(response::setComplete));
|
||||
});
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
.clientConnector(httpConnector)
|
||||
.build();
|
||||
|
||||
new DirectFieldAccessor(this.serviceInternalReactiveGatewayHandler)
|
||||
.setPropertyValue("webClient", webClient);
|
||||
|
||||
this.mockMvc.perform(
|
||||
get("/service2")
|
||||
.with(httpBasic("guest", "guest"))
|
||||
.param("name", "foo"))
|
||||
.andExpect(
|
||||
content()
|
||||
.string("FOO"));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private PollableChannel storeChannel;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testHttpReactivePost() {
|
||||
this.webTestClient.post().uri("/reactivePost")
|
||||
.body(Flux.just("foo", "bar", "baz"), String.class)
|
||||
.exchange()
|
||||
.expectStatus().isAccepted();
|
||||
|
||||
Message<?> store = this.storeChannel.receive(10_000);
|
||||
assertNotNull(store);
|
||||
assertThat(store.getPayload(), instanceOf(Flux.class));
|
||||
|
||||
StepVerifier
|
||||
.create((Publisher<String>) store.getPayload())
|
||||
.expectNext("foo", "bar", "baz")
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSse() {
|
||||
Flux<String> responseBody =
|
||||
this.webTestClient.get().uri("/sse")
|
||||
.exchange()
|
||||
.returnResult(String.class)
|
||||
.getResponseBody();
|
||||
|
||||
StepVerifier
|
||||
.create(responseBody)
|
||||
.expectNext("foo", "bar", "baz")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableWebSecurity
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication()
|
||||
.withUser("guest")
|
||||
.password("guest")
|
||||
.roles("ADMIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
.anyRequest().hasRole("ADMIN")
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.anonymous().disable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow httpReactiveProxyFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Http.inboundGateway("/service2")
|
||||
.requestMapping(r -> r.params("name")))
|
||||
.handle(WebFlux.<MultiValueMap<String, String>>outboundGateway(m ->
|
||||
UriComponentsBuilder.fromUriString("http://www.springsource.org/spring-integration")
|
||||
.queryParams(m.getPayload())
|
||||
.build()
|
||||
.toUri())
|
||||
.httpMethod(HttpMethod.GET)
|
||||
.expectedResponseType(String.class))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow httpReactiveInboundChannelAdapterFlow() {
|
||||
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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDecisionManager accessDecisionManager() {
|
||||
return new AffirmativeBased(Collections.singletonList(new RoleVoter()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.inbound;
|
||||
package org.springframework.integration.webflux.inbound;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.FluxMessageChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.http.inbound.RequestMapping;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -47,13 +48,13 @@ import reactor.core.publisher.Flux;
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class ReactiveHttpInboundEndpointTests {
|
||||
public class WebFluxInboundEndpointTests {
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@Autowired
|
||||
private ReactiveHttpInboundEndpoint simpleInboundEndpoint;
|
||||
private WebFluxInboundEndpoint simpleInboundEndpoint;
|
||||
|
||||
@Test
|
||||
public void testSimpleGet() {
|
||||
@@ -93,8 +94,8 @@ public class ReactiveHttpInboundEndpointTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveHttpInboundEndpoint simpleInboundEndpoint() {
|
||||
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
|
||||
public WebFluxInboundEndpoint simpleInboundEndpoint() {
|
||||
WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
|
||||
RequestMapping requestMapping = new RequestMapping();
|
||||
requestMapping.setPathPatterns("/test");
|
||||
endpoint.setRequestMapping(requestMapping);
|
||||
@@ -108,8 +109,8 @@ public class ReactiveHttpInboundEndpointTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveHttpInboundEndpoint jsonInboundEndpoint() {
|
||||
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
|
||||
public WebFluxInboundEndpoint jsonInboundEndpoint() {
|
||||
WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
|
||||
RequestMapping requestMapping = new RequestMapping();
|
||||
requestMapping.setPathPatterns("/persons");
|
||||
endpoint.setRequestMapping(requestMapping);
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
package org.springframework.integration.webflux.outbound;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
@@ -48,14 +48,13 @@ import reactor.test.StepVerifier;
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
public class WebFluxRequestExecutingMessageHandlerTests {
|
||||
|
||||
@Test
|
||||
public void testReactiveReturn() throws Throwable {
|
||||
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
return Mono.empty()
|
||||
.then(Mono.defer(response::setComplete));
|
||||
return Mono.defer(response::setComplete);
|
||||
});
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
@@ -63,8 +62,8 @@ public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
.build();
|
||||
|
||||
String destinationUri = "http://www.springsource.org/spring-integration";
|
||||
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
|
||||
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
WebFluxRequestExecutingMessageHandler reactiveHandler =
|
||||
new WebFluxRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
|
||||
FluxMessageChannel ackChannel = new FluxMessageChannel();
|
||||
reactiveHandler.setOutputChannel(ackChannel);
|
||||
@@ -84,8 +83,7 @@ public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
public void testReactiveErrorOneWay() throws Throwable {
|
||||
ClientHttpConnector httpConnector = new HttpHandlerConnector((request, response) -> {
|
||||
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
return Mono.empty()
|
||||
.then(Mono.defer(response::setComplete));
|
||||
return Mono.defer(response::setComplete);
|
||||
});
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
@@ -93,8 +91,8 @@ public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
.build();
|
||||
|
||||
String destinationUri = "http://www.springsource.org/spring-integration";
|
||||
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
|
||||
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
WebFluxRequestExecutingMessageHandler reactiveHandler =
|
||||
new WebFluxRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
reactiveHandler.setExpectReply(false);
|
||||
|
||||
QueueChannel errorChannel = new QueueChannel();
|
||||
@@ -121,8 +119,8 @@ public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
.build();
|
||||
|
||||
String destinationUri = "http://www.springsource.org/spring-integration";
|
||||
ReactiveHttpRequestExecutingMessageHandler reactiveHandler =
|
||||
new ReactiveHttpRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
WebFluxRequestExecutingMessageHandler reactiveHandler =
|
||||
new WebFluxRequestExecutingMessageHandler(destinationUri, webClient);
|
||||
reactiveHandler.setExpectReply(false);
|
||||
|
||||
QueueChannel errorChannel = new QueueChannel();
|
||||
@@ -138,5 +136,4 @@ public class ReactiveHttpRequestExecutingMessageHandlerTests {
|
||||
assertThat(throwable.getMessage(), containsString("Intentional connection error"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
log4j.rootCategory=WARN, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss.SSS} %-5p [%t][%c] %m%n
|
||||
|
||||
#log4j.category.org.springframework=DEBUG
|
||||
log4j.category.org.springframework.integration=WARN
|
||||
log4j.category.org.springframework.integration.webflux=WARN
|
||||
@@ -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]]
|
||||
|
||||
@@ -98,6 +98,8 @@ include::./ip.adoc[]
|
||||
|
||||
include::./twitter.adoc[]
|
||||
|
||||
include::./webflux.adoc[]
|
||||
|
||||
include::./web-sockets.adoc[]
|
||||
|
||||
include::./ws.adoc[]
|
||||
|
||||
240
src/reference/asciidoc/webflux.adoc
Normal file
240
src/reference/asciidoc/webflux.adoc
Normal 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.
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user