GH-3533: Register WebSocket endpoints at runtime (#3548)

* GH-3533: Register WebSocket endpoints at runtime

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

* Rework `WebSocketIntegrationConfigurationInitializer` to register beans functional way
to avoid reflection for Spring Native support
* Move `IntegrationServletWebSocketHandlerRegistry` into a separate file for better readability
* Implement `DestructionAwareBeanPostProcessor` for `IntegrationServletWebSocketHandlerRegistry`
to track runtime bean registrations and removals
* Introduce an `IntegrationDynamicWebSocketHandlerMapping` to manage runtime mapping
registrations and removals
* Add `servlet-api` dependency into `websocket` to be able to compile an
`IntegrationDynamicWebSocketHandlerMapping`
* Fix typo in the exception message of the `StandardIntegrationFlowRegistration`
* Start dynamically added beans together with associated `IntegrationFlow` in the
`StandardIntegrationFlowContext`
* Document new feature

* * Fix language in docs
* Don't start those `SmartLifecycle`s together with a dynamic flow
which are not `isAutoStartup()`

* * Fix `TomcatWebSocketTestServer` to configure servlet for `loadOnStartup = 1`
* Fix `WebSocketDslTests` to make `clientWebSocketContainer.setAutoStartup(true)`
This commit is contained in:
Artem Bilan
2021-04-13 17:36:01 -04:00
committed by GitHub
parent 6f8290a0f6
commit 35fd9c5809
12 changed files with 460 additions and 69 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.type.MethodMetadata;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.dsl.IntegrationFlow;
@@ -121,9 +122,15 @@ public final class StandardIntegrationFlowContext implements IntegrationFlowCont
final String theFlowId = flowId;
builder.additionalBeans.forEach((key, value) -> registerBean(key, value, theFlowId));
IntegrationFlowRegistration registration = new StandardIntegrationFlowRegistration(integrationFlow, this, flowId);
IntegrationFlowRegistration registration =
new StandardIntegrationFlowRegistration(integrationFlow, this, flowId);
if (builder.autoStartup) {
registration.start();
builder.additionalBeans.keySet()
.stream()
.filter(SmartLifecycle.class::isInstance)
.filter((lifecycle) -> ((SmartLifecycle) lifecycle).isAutoStartup())
.forEach((lifecycle) -> ((SmartLifecycle) lifecycle).start());
}
this.registry.put(flowId, registration);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2020 the original author or authors.
* Copyright 2018-2021 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.
@@ -52,7 +52,9 @@ class StandardIntegrationFlowRegistration implements IntegrationFlowRegistration
private ConfigurableListableBeanFactory beanFactory;
StandardIntegrationFlowRegistration(IntegrationFlow integrationFlow, IntegrationFlowContext integrationFlowContext, String id) {
StandardIntegrationFlowRegistration(IntegrationFlow integrationFlow, IntegrationFlowContext integrationFlowContext,
String id) {
this.integrationFlow = integrationFlow;
this.integrationFlowContext = integrationFlowContext;
this.id = id;
@@ -81,7 +83,7 @@ class StandardIntegrationFlowRegistration implements IntegrationFlowRegistration
throw new IllegalStateException("Only 'IntegrationFlow' instances started from the 'MessageChannel' " +
"(e.g. extracted from 'IntegrationFlow' Lambdas) can be used " +
"for direct 'send' operation. " +
"But [" + this.integrationFlow + "] ins't one of them.\n" +
"But [" + this.integrationFlow + "] isn't one of them.\n" +
"Consider 'BeanFactory.getBean()' usage for sending messages " +
"to the required 'MessageChannel'.");
}