Rely on MProducerSupport.active for Flux (#3423)

* Rely on `MProducerSupport.active` for `Flux`

* Fix `MessageProducerSupport` to extract an `active` flag and set it before
`isRunning` - the `Flux` subscription relies on the `takeWhile()`
where in case of `autoStartup = false` we will never start consume because
it is set to `true` already after `doStart()`
* Refactor all the `MessageProducerSupport` implementation with similar
`active` state to use already one from the super class

**Cherry-pick to 5.3.x**

* * Remove `MessageProducerSupport.setActive()`
to not let to mutate it from the implementations
* Set `active` to `false` in the `destroy()`
* Clean up and fix typos in the affected `JmsMessageDrivenEndpoint`

* * Pull `active` flag down to the `AbstractEndpoint`
* Set `active = true` in the `start()` before calling `doStart()`
* Do same for `active = false` in the `stop()`
* Clean up `AbstractEndpoint` impls to not call `doStart/doStop` for nothing
* Refactor endpoints to rely on the `active` state from the `AbstractEndpoint`
not their own
This commit is contained in:
Artem Bilan
2020-11-06 13:51:03 -05:00
committed by Gary Russell
parent 27b464ad27
commit 002382e647
23 changed files with 1167 additions and 253 deletions

View File

@@ -47,14 +47,6 @@ import org.springframework.util.StringUtils;
public abstract class AbstractEndpoint extends IntegrationObjectSupport
implements SmartLifecycle, DisposableBean {
private boolean autoStartupSetExplicitly;
private volatile boolean autoStartup = true;
private volatile int phase = 0;
private volatile boolean running;
protected final ReentrantLock lifecycleLock = new ReentrantLock(); // NOSONAR
protected final Condition lifecycleCondition = this.lifecycleLock.newCondition(); // NOSONAR
@@ -63,6 +55,16 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
private SmartLifecycleRoleController roleController;
private boolean autoStartup = true;
private boolean autoStartupSetExplicitly;
private int phase = 0;
private volatile boolean running;
private volatile boolean active;
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
this.autoStartupSetExplicitly = true;
@@ -120,6 +122,7 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
@Override
public void destroy() {
stop();
if (this.roleController != null) {
this.roleController.removeLifecycle(this);
}
@@ -153,6 +156,7 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
this.lifecycleLock.lock();
try {
if (!this.running) {
this.active = true;
doStart();
this.running = true;
if (logger.isInfoEnabled()) {
@@ -170,6 +174,7 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
this.lifecycleLock.lock();
try {
if (this.running) {
this.active = false;
doStop();
this.running = false;
if (logger.isInfoEnabled()) {
@@ -187,6 +192,7 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
this.lifecycleLock.lock();
try {
if (this.running) {
this.active = false;
doStop(callback);
this.running = false;
if (logger.isInfoEnabled()) {
@@ -211,6 +217,10 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
callback.run();
}
public boolean isActive() {
return this.active;
}
/**
* Subclasses must implement this method with the start behavior.
* This method will be invoked while holding the {@link #lifecycleLock}.

View File

@@ -368,7 +368,7 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
triggerContext.lastActualExecutionTime(),
new Date())
)), 1)
.repeat(this::isRunning)
.repeat(this::isActive)
.doOnSubscribe(subs -> this.subscription = subs);
}

View File

@@ -222,7 +222,7 @@ public abstract class MessageProducerSupport extends AbstractEndpoint implements
.map(this::trackMessageIfAny)
.doOnComplete(this::stop)
.doOnCancel(this::stop)
.takeWhile((message) -> isRunning());
.takeWhile((message) -> isActive());
if (channelForSubscription instanceof ReactiveStreamsSubscribableChannel) {
((ReactiveStreamsSubscribableChannel) channelForSubscription).subscribeTo(messageFlux);

View File

@@ -1016,6 +1016,12 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
}
}
@Override
public void destroy() {
super.destroy();
this.gatewayMap.values().forEach(MethodInvocationGateway::destroy);
}
private static final class MethodInvocationGateway extends MessagingGatewaySupport {
private Expression receiveTimeoutExpression;

View File

@@ -16,7 +16,7 @@
package org.springframework.integration.endpoint;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import org.junit.jupiter.api.Test;
@@ -50,17 +50,18 @@ public class ReactiveMessageProducerTests {
@Test
public void test() {
assertThat(this.producer.isRunning()).isTrue();
StepVerifier stepVerifier =
StepVerifier.create(
Flux.from(this.fluxMessageChannel)
.map(Message::getPayload)
.cast(String.class))
.expectNext("test1", "test2")
.thenCancel()
.verifyLater();
StepVerifier.create(
Flux.from(this.fluxMessageChannel)
.map(Message::getPayload)
.cast(String.class))
.expectNext("test1", "test2")
.thenCancel()
.verify();
this.producer.start();
assertThat(this.producer.isRunning()).isFalse();
stepVerifier.verify(Duration.ofSeconds(10));
}
@Configuration
@@ -79,10 +80,12 @@ public class ReactiveMessageProducerTests {
@Override
protected void doStart() {
super.doStart();
subscribeToPublisher(Flux.just("test1", "test2").map(GenericMessage::new));
}
};
producer.setAutoStartup(false);
producer.setOutputChannel(fluxMessageChannel());
return producer;
}