Skip to content

Commit e9a3fd3

Browse files
artembilanspring-builds
authored andcommitted
GH-3055: Fix DirectMLContainer for taskScheduler.shutdown()
Fixes: #3055 Issue link: #3057 The internal `DirectMessageListenerContainer.taskScheduler` is not destroyed when application context is closed. The current `doStop()` implementation is only called from `Lifecycle.stop()`. However, the application context calls the `SmartLifecycle.stop(Runnable)`. That one, in turn, in the `AbstractMessageListenerContainer` calls a `shutdown()`, which does not clean up `taskScheduler` in the `DirectMessageListenerContainer` implementation. In fact, this `shutdown()` is called from other volatile places in the `DirectMessageListenerContainer`, where assumption is that `taskScheduler` is active. Therefore, we cannot move `doStop()` extension into the `actualShutDown()` implementation. * Extract `cleanUpTaskScheduler()` method in the `DirectMessageListenerContainer` and call it from existing `doStop()`, from overridden `stop(Runnable)` and `destroy()` (cherry picked from commit 5480b2b)
1 parent cdb28fe commit e9a3fd3

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/DirectMessageListenerContainer.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -426,14 +426,24 @@ protected void doStart() {
426426
}
427427

428428
@Override
429-
protected void doStop() {
430-
super.doStop();
429+
public void stop(Runnable callback) {
430+
super.stop(callback);
431+
cleanUpTaskScheduler();
432+
}
433+
434+
private void cleanUpTaskScheduler() {
431435
if (!this.taskSchedulerSet && this.taskScheduler != null) {
432436
((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();
433437
this.taskScheduler = null;
434438
}
435439
}
436440

441+
@Override
442+
protected void doStop() {
443+
super.doStop();
444+
cleanUpTaskScheduler();
445+
}
446+
437447
protected void actualStart() {
438448
this.aborted = false;
439449
this.hasStopped = false;
@@ -976,6 +986,12 @@ protected void consumerRemoved(SimpleConsumer consumer) {
976986
// default empty
977987
}
978988

989+
@Override
990+
public void destroy() {
991+
super.destroy();
992+
cleanUpTaskScheduler();
993+
}
994+
979995
/**
980996
* The consumer object.
981997
*/

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/ContainerShutDownTests.java

+23
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.util.Map;
2222
import java.util.concurrent.CountDownLatch;
23+
import java.util.concurrent.ScheduledExecutorService;
2324
import java.util.concurrent.TimeUnit;
2425

2526
import org.junit.jupiter.api.Test;
@@ -140,4 +141,26 @@ private void consumersCorrectlyCancelledOnShutdown(AbstractMessageListenerContai
140141
}
141142
}
142143

144+
@Test
145+
void directMessageListenerContainerShutdownsItsSchedulerOnStopWithCallback() {
146+
DirectMessageListenerContainer container = new DirectMessageListenerContainer();
147+
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
148+
container.setConnectionFactory(cf);
149+
container.setQueueNames("test.shutdown");
150+
container.setMessageListener(m -> {
151+
});
152+
153+
container.start();
154+
155+
ScheduledExecutorService scheduledExecutorService =
156+
TestUtils.getPropertyValue(container, "taskScheduler.scheduledExecutor", ScheduledExecutorService.class);
157+
158+
container.stop(() -> {
159+
});
160+
161+
cf.destroy();
162+
163+
assertThat(scheduledExecutorService.isShutdown()).isTrue();
164+
}
165+
143166
}

0 commit comments

Comments
 (0)