GH-2358: Register IntGraphController for WebFlux

Fixes https://github.com/spring-projects/spring-integration/issues/2358

The WebFlux on the server side can deal with MVC `@Controllers` as well
without any Servlet environment.

* Enable `IntegrationGraphController` when we not only in the
Servlet environment, but also in the WebFlux
* Upgrade to SF-5.0.4 and fix WebFlux tests to meet the latest requirements
  (https://jira.spring.io/browse/SPR-16337)
* Move `WEB_FLUX_PRESENT` to the `HttpContextUtils`;
deprecate it in the `WebFluxContextUtils`
* Mention WebFlux ability for the `IntegrationGraphController` in the
`graph.adoc`
This commit is contained in:
Artem Bilan
2018-02-13 18:12:21 -05:00
committed by Gary Russell
parent a4130c9e59
commit 11240be555
10 changed files with 115 additions and 31 deletions

View File

@@ -138,7 +138,7 @@ subprojects { subproject ->
springSecurityVersion = '5.0.1.RELEASE'
springSocialTwitterVersion = '1.1.2.RELEASE'
springRetryVersion = '1.2.2.RELEASE'
springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.0.3.RELEASE'
springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.0.4.BUILD-SNAPSHOT'
springWsVersion = '3.0.0.RELEASE'
tomcatVersion = "8.5.23"
xmlUnitVersion = '1.6'

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2018 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.
@@ -28,13 +28,15 @@ import org.springframework.integration.http.management.IntegrationGraphControlle
import org.springframework.integration.http.support.HttpContextUtils;
/**
* Enables the {@link IntegrationGraphController} if {@code DispatcherServlet} is present in the classpath.
* Enables the {@link IntegrationGraphController} if
* {@code org.springframework.web.servlet.DispatcherServlet} or
* {@code org.springframework.web.reactive.DispatcherHandler} is present in the classpath.
*
* @author Artem Bilan
*
* @since 4.3
*
* @see IntegrationGraphController
* @see IntegrationGraphController
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -64,6 +66,6 @@ public @interface EnableIntegrationGraphController {
* @return the URLs.
* @since 4.3.5
*/
String[] allowedOrigins() default {};
String[] allowedOrigins() default { };
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -34,12 +34,13 @@ class IntegrationGraphControllerRegistrarImportSelector implements ImportSelecto
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (HttpContextUtils.WEB_MVC_PRESENT) {
if (HttpContextUtils.WEB_MVC_PRESENT || HttpContextUtils.WEB_FLUX_PRESENT) {
return new String[] { IntegrationGraphControllerRegistrar.class.getName() };
}
else {
logger.warn("The 'IntegrationGraphController' isn't registered with the application context because" +
" there is no 'org.springframework.web.servlet.DispatcherServlet' in the classpath.");
" there is no 'org.springframework.web.servlet.DispatcherServlet' or" +
" 'org.springframework.web.reactive.DispatcherHandler' in the classpath.");
return new String[0];
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -39,15 +39,25 @@ public final class HttpContextUtils {
}
/**
* The {@code boolean} flag to indicate if the
* {@code org.springframework.web.servlet.DispatcherServlet}
* is present in the CLASSPATH to allow to register the Integration server components,
* A {@code boolean} flag to indicate if the
* {@code org.springframework.web.servlet.DispatcherServlet} is present in the
* CLASSPATH to allow the registration of Integration server components,
* e.g. {@code IntegrationGraphController}.
*/
public static final boolean WEB_MVC_PRESENT =
ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet",
HttpContextUtils.class.getClassLoader());
/**
* A {@code boolean} flag to indicate if the
* {@code org.springframework.web.reactive.DispatcherHandler} is present in the
* CLASSPATH to allow the registration of Integration server reactive components,
* e.g. {@code IntegrationGraphController}.
*/
public static final boolean WEB_FLUX_PRESENT =
ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler",
HttpContextUtils.class.getClassLoader());
/**
* The name for the infrastructure
* {@link org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping} bean.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -28,6 +28,7 @@ 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.support.HttpContextUtils;
import org.springframework.integration.webflux.inbound.IntegrationHandlerResultHandler;
import org.springframework.integration.webflux.inbound.WebFluxIntegrationRequestMappingHandlerMapping;
import org.springframework.integration.webflux.support.WebFluxContextUtils;
@@ -66,7 +67,7 @@ public class WebFluxIntegrationConfigurationInitializer implements IntegrationCo
* the HTTP server components.
*/
private void registerReactiveRequestMappingHandlerMappingIfNecessary(BeanDefinitionRegistry registry) {
if (WebFluxContextUtils.WEB_FLUX_PRESENT &&
if (HttpContextUtils.WEB_FLUX_PRESENT &&
!registry.containsBeanDefinition(WebFluxContextUtils.HANDLER_MAPPING_BEAN_NAME)) {
BeanDefinitionBuilder requestMappingBuilder =
BeanDefinitionBuilder.genericBeanDefinition(WebFluxIntegrationRequestMappingHandlerMapping.class);

View File

@@ -16,7 +16,7 @@
package org.springframework.integration.webflux.support;
import org.springframework.util.ClassUtils;
import org.springframework.integration.http.support.HttpContextUtils;
/**
* Utility class for accessing WebFlux integration components
@@ -36,10 +36,11 @@ public final class WebFluxContextUtils {
* 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.
* @deprecated since 5.0.2 in favor of {@link HttpContextUtils#WEB_FLUX_PRESENT}.
* Will be removed in the 5.1.
*/
public static final boolean WEB_FLUX_PRESENT =
ClassUtils.isPresent("org.springframework.web.reactive.result.method.RequestMappingInfo",
WebFluxContextUtils.class.getClassLoader());
@Deprecated
public static final boolean WEB_FLUX_PRESENT = HttpContextUtils.WEB_FLUX_PRESENT;
/**
* The name for the infrastructure

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -129,9 +129,7 @@ public class WebFluxDslTests {
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
DataBufferFactory bufferFactory = response.bufferFactory();
return response.writeWith(
Flux.just(bufferFactory.wrap("FOO".getBytes()),
bufferFactory.wrap("BAR".getBytes())))
return response.writeWith(Mono.just(bufferFactory.wrap("FOO\nBAR\n".getBytes())))
.then(Mono.defer(response::setComplete));
});
@@ -198,7 +196,7 @@ public class WebFluxDslTests {
@SuppressWarnings("unchecked")
public void testHttpReactivePost() {
this.webTestClient.post().uri("/reactivePost")
.body(Flux.just("foo", "bar", "baz"), String.class)
.body(Mono.just("foo\nbar\nbaz"), String.class)
.exchange()
.expectStatus().isAccepted();

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2018 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.management;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.http.config.EnableIntegrationGraphController;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.reactive.config.EnableWebFlux;
/**
* @author Artem Bilan
*
* @since 5.0.2
*/
@RunWith(SpringRunner.class)
@WebAppConfiguration
@TestPropertySource(properties = "spring.application.name:testApplication")
@DirtiesContext
public class IntegrationGraphControllerTests {
@Autowired
private WebApplicationContext wac;
@Test
public void testIntegrationGraphGet() {
WebTestClient webTestClient =
WebTestClient.bindToApplicationContext(this.wac)
.build();
webTestClient.get().uri("/testIntegration")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.nodes..name").isArray()
.jsonPath("$.contentDescriptor.name").isEqualTo("testApplication")
.jsonPath("$.links").exists();
}
@Configuration
@EnableWebFlux
@EnableIntegration
@EnableIntegrationGraphController(path = "/testIntegration")
public static class ContextConfiguration {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -198,10 +198,7 @@ public class WebFluxRequestExecutingMessageHandlerTests {
DataBufferFactory bufferFactory = response.bufferFactory();
Flux<DataBuffer> data =
Flux.just(bufferFactory.wrap("foo".getBytes()),
bufferFactory.wrap("bar".getBytes()),
bufferFactory.wrap("baz".getBytes()));
Mono<DataBuffer> data = Mono.just(bufferFactory.wrap("foo\nbar\nbaz".getBytes()));
return response.writeWith(data)
.then(Mono.defer(response::setComplete));

View File

@@ -202,7 +202,7 @@ See also <<programming-tips>> for more information.
=== Integration Graph Controller
If your application is WEB-based (or built on top of Spring Boot using an embedded web container) and the Spring Integration HTTP module (see <<http>>) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service.
If your application is WEB-based (or built on top of Spring Boot using an embedded web container) and the Spring Integration HTTP or WebFlux module (see <<http>> and <<webflux>>) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service.
For this purpose, the `@EnableIntegrationGraphController` `@Configuration` class annotation and the `<int-http:graph-controller/>` XML element, are available in the HTTP module.
Together with the `@EnableWebMvc` annotation (or `<mvc:annotation-driven/>` for xml definitions), this configuration registers an `IntegrationGraphController` `@RestController` where its `@RequestMapping.path` can be configured on the `@EnableIntegrationGraphController` annotation or `<int-http:graph-controller/>` element.
The default path is `/integration`.
@@ -242,8 +242,8 @@ For more sophistication, you can configure the CORS mappings using standard Spri
[source,java]
----
@Configuration
@EnableWebMvc
@EnableWebSecurity
@EnableWebMvc // or @EnableWebFlux
@EnableWebSecurity // or @EnableWebFluxSecurity
@EnableIntegration
@EnableIntegrationGraphController(path = "/testIntegration", allowedOrigins="http://localhost:9090")
public class IntegrationConfiguration extends WebSecurityConfigurerAdapter {