Description
Starting with Spring Framework 6.1
the ExecutorConfigurationSupport
now supports a lifecycle management.
It comes with a private int phase = DEFAULT_PHASE;
.
It has this logic:
/**
* Pause this executor, triggering the given callback
* once all currently executing tasks have completed.
* @since 6.1
*/
@Override
public void stop(Runnable callback) {
if (this.lifecycleDelegate != null && !this.lateShutdown) {
this.lifecycleDelegate.stop(callback);
}
else {
callback.run();
}
}
So, it waits for scheduled tasks to be finished.
The AbstractPollingEndpoint
uses Integer.MAX_VALUE / 2
and calls this.runningTask.cancel(true);
in its stop()
. So, no new messages are emitted to the application.
However this combination leads to dead-lock and unexpected behavior.
According to the phase logic
* The default phase for {@code SmartLifecycle}: {@code Integer.MAX_VALUE}.
* <p>This is different from the common phase {@code 0} associated with regular
* {@link Lifecycle} implementations, putting the typically auto-started
* {@code SmartLifecycle} beans into a later startup phase and an earlier
* shutdown phase.
The ThreadPoolTaskScheduler
is stopped earlier, then PollingConsumer
.
And the former waits for its tasks to be finished, not giving the later a chance to cancel its task.
Therefore the ThreadPoolTaskScheduler
configuration in the DefaultConfiguringBeanFactoryPostProcessor.registerTaskScheduler()
should be adjusted to use the same Integer.MAX_VALUE / 2
phase.