diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java
index fdb2f1575c..6c8adc18c4 100644
--- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java
+++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java
@@ -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.
@@ -24,6 +24,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
@@ -60,13 +62,18 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* {@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
+ * the basis of the same {@link BaseHttpInboundEndpoint} class and there is no
* single {@link RequestMappingInfo} configuration without
* {@link org.springframework.web.method.HandlerMethod} in Spring MVC.
* Accordingly {@link IntegrationRequestMappingHandlerMapping} is a
* {@link org.springframework.web.servlet.HandlerMapping}
* compromise implementation between method-level annotations and component-level
* (e.g. Spring Integration XML) configurations.
+ *
+ * Starting with version 5.1, this class implements {@link DestructionAwareBeanPostProcessor} to
+ * register HTTP endpoints at runtime for dynamically declared beans, e.g. via
+ * {@link org.springframework.integration.dsl.context.IntegrationFlowContext}, and unregister
+ * them during the {@link BaseHttpInboundEndpoint} destruction.
*
* @author Artem Bilan
*
@@ -76,13 +83,35 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* @see RequestMappingHandlerMapping
*/
public final class IntegrationRequestMappingHandlerMapping extends RequestMappingHandlerMapping
- implements ApplicationListener {
+ implements ApplicationListener, DestructionAwareBeanPostProcessor {
private static final Method HANDLE_REQUEST_METHOD = ReflectionUtils.findMethod(HttpRequestHandler.class,
"handleRequest", HttpServletRequest.class, HttpServletResponse.class);
private final AtomicBoolean initialized = new AtomicBoolean();
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (this.initialized.get() && isHandler(bean.getClass())) {
+ detectHandlerMethods(bean);
+ }
+
+ return bean;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
+ if (isHandler(bean.getClass())) {
+ unregisterMapping(getMappingForEndpoint((BaseHttpInboundEndpoint) bean));
+ }
+ }
+
+ @Override
+ public boolean requiresDestruction(Object bean) {
+ return isHandler(bean.getClass());
+ }
+
@Override
protected boolean isHandler(Class> beanType) {
return HttpRequestHandlingEndpointSupport.class.isAssignableFrom(beanType);
@@ -97,6 +126,7 @@ public final class IntegrationRequestMappingHandlerMapping extends RequestMappin
handler = bean;
}
}
+
return super.getHandlerExecutionChain(handler, request);
}
diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/dsl/HttpDslTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/dsl/HttpDslTests.java
index 6a59700ebd..9d1ba1bd2a 100644
--- a/spring-integration-http/src/test/java/org/springframework/integration/http/dsl/HttpDslTests.java
+++ b/spring-integration-http/src/test/java/org/springframework/integration/http/dsl/HttpDslTests.java
@@ -38,6 +38,7 @@ 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.dsl.context.IntegrationFlowContext;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.integration.security.channel.SecuredChannel;
@@ -80,6 +81,9 @@ public class HttpDslTests {
@Autowired
private HttpRequestExecutingMessageHandler serviceInternalGatewayHandler;
+ @Autowired
+ private IntegrationFlowContext integrationFlowContext;
+
private MockMvc mockMvc;
@Before
@@ -114,6 +118,37 @@ public class HttpDslTests {
.isForbidden());
}
+ @Test
+ public void testDynamicHttpEndpoint() throws Exception {
+ IntegrationFlow flow =
+ IntegrationFlows.from(Http.inboundGateway("/dynamic")
+ .requestMapping(r -> r.params("name"))
+ .payloadExpression("#requestParams.name[0]"))
+ .transform(String::toLowerCase)
+ .get();
+
+ IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
+ this.integrationFlowContext.registration(flow).register();
+
+ this.mockMvc.perform(
+ get("/dynamic")
+ .with(httpBasic("admin", "admin"))
+ .param("name", "BAR"))
+ .andExpect(
+ content()
+ .string("bar"));
+
+ flowRegistration.destroy();
+
+ this.mockMvc.perform(
+ get("/dynamic")
+ .with(httpBasic("admin", "admin"))
+ .param("name", "BAZ"))
+ .andExpect(
+ status()
+ .isNotFound());
+ }
+
@Configuration
@EnableWebSecurity
@EnableIntegration
diff --git a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxIntegrationRequestMappingHandlerMapping.java b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxIntegrationRequestMappingHandlerMapping.java
index cacb45fd49..d9540932e0 100644
--- a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxIntegrationRequestMappingHandlerMapping.java
+++ b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxIntegrationRequestMappingHandlerMapping.java
@@ -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.
@@ -19,6 +19,8 @@ package org.springframework.integration.webflux.inbound;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.integration.http.config.HttpContextUtils;
@@ -66,6 +68,11 @@ import org.springframework.web.server.WebHandler;
* {@link org.springframework.web.reactive.HandlerMapping}
* compromise implementation between method-level annotations and component-level
* (e.g. Spring Integration XML) configurations.
+ *
+ * Starting with version 5.1, this class implements {@link DestructionAwareBeanPostProcessor} to
+ * register HTTP endpoints at runtime for dynamically declared beans, e.g. via
+ * {@link org.springframework.integration.dsl.context.IntegrationFlowContext}, and unregister
+ * them during the {@link WebFluxInboundEndpoint} destruction.
*
* @author Artem Bilan
*
@@ -75,13 +82,35 @@ import org.springframework.web.server.WebHandler;
* @see RequestMappingHandlerMapping
*/
public class WebFluxIntegrationRequestMappingHandlerMapping extends RequestMappingHandlerMapping
- implements ApplicationListener {
+ implements ApplicationListener, DestructionAwareBeanPostProcessor {
private static final Method HANDLER_METHOD = ReflectionUtils.findMethod(WebHandler.class,
"handle", ServerWebExchange.class);
private final AtomicBoolean initialized = new AtomicBoolean();
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (this.initialized.get() && isHandler(bean.getClass())) {
+ detectHandlerMethods(bean);
+ }
+
+ return bean;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
+ if (isHandler(bean.getClass())) {
+ unregisterMapping(getMappingForEndpoint((WebFluxInboundEndpoint) bean));
+ }
+ }
+
+ @Override
+ public boolean requiresDestruction(Object bean) {
+ return isHandler(bean.getClass());
+ }
+
@Override
protected boolean isHandler(Class> beanType) {
return WebFluxInboundEndpoint.class.isAssignableFrom(beanType);
diff --git a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java
index df6118bda5..3e8d888356 100644
--- a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java
+++ b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java
@@ -51,6 +51,7 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
+import org.springframework.integration.dsl.context.IntegrationFlowContext;
import org.springframework.integration.http.HttpHeaders;
import org.springframework.integration.http.dsl.Http;
import org.springframework.integration.support.MessageBuilder;
@@ -109,6 +110,9 @@ public class WebFluxDslTests {
@Autowired
private WebApplicationContext wac;
+ @Autowired
+ private IntegrationFlowContext integrationFlowContext;
+
@Autowired
@Qualifier("webFluxWithReplyPayloadToFlux.handler")
private WebFluxRequestExecutingMessageHandler webFluxWithReplyPayloadToFlux;
@@ -246,6 +250,33 @@ public class WebFluxDslTests {
.verifyComplete();
}
+ @Test
+ public void testDynamicHttpEndpoint() throws Exception {
+ IntegrationFlow flow =
+ IntegrationFlows.from(WebFlux.inboundGateway("/dynamic")
+ .requestMapping(r -> r.params("name"))
+ .payloadExpression("#requestParams.name[0]"))
+ .transform(String::toLowerCase)
+ .get();
+
+ IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
+ this.integrationFlowContext.registration(flow).register();
+
+ this.webTestClient.get().uri("/dynamic?name=BAR")
+ .attributes(basicAuthenticationCredentials("guest", "guest"))
+ .exchange()
+ .expectBody(String.class)
+ .isEqualTo("bar");
+
+ flowRegistration.destroy();
+
+ this.webTestClient.get().uri("/dynamic?name=BAZ")
+ .attributes(basicAuthenticationCredentials("guest", "guest"))
+ .exchange()
+ .expectStatus()
+ .isNotFound();
+ }
+
@Configuration
@EnableWebFlux
@EnableWebSecurity