Configure individual timeouts for specific shutdown phases

Closes gh-32985
This commit is contained in:
Juergen Hoeller 2024-06-10 11:00:47 +02:00
parent b7e4fa06c3
commit 457bf9416c
1 changed files with 46 additions and 7 deletions

View File

@ -106,6 +106,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private final Log logger = LogFactory.getLog(getClass());
private final Map<Integer, Long> timeoutsForShutdownPhases = new ConcurrentHashMap<>();
private volatile long timeoutPerShutdownPhase = 10000;
private volatile boolean running;
@ -132,6 +134,37 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
/**
* Specify the maximum time allotted for the shutdown of each given phase
* (group of {@link SmartLifecycle} beans with the same 'phase' value).
* <p>In case of no specific timeout configured, the default timeout per
* shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2.
* @param timeoutsForShutdownPhases a map of phase values (matching
* {@link SmartLifecycle#getPhase()}) and corresponding timeout values
* (in milliseconds)
* @since 6.2
* @see SmartLifecycle#getPhase()
* @see #setTimeoutPerShutdownPhase
*/
public void setTimeoutsForShutdownPhases(Map<Integer, Long> timeoutsForShutdownPhases) {
this.timeoutsForShutdownPhases.putAll(timeoutsForShutdownPhases);
}
/**
* Specify the maximum time allotted for the shutdown of a specific phase
* (group of {@link SmartLifecycle} beans with the same 'phase' value).
* <p>In case of no specific timeout configured, the default timeout per
* shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2.
* @param phase the phase value (matching {@link SmartLifecycle#getPhase()})
* @param timeout the corresponding timeout value (in milliseconds)
* @since 6.2
* @see SmartLifecycle#getPhase()
* @see #setTimeoutPerShutdownPhase
*/
public void setTimeoutForShutdownPhase(int phase, long timeout) {
this.timeoutsForShutdownPhases.put(phase, timeout);
}
/**
* Specify the maximum time allotted in milliseconds for the shutdown of any
* phase (group of {@link SmartLifecycle} beans with the same 'phase' value).
@ -142,6 +175,11 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
}
private long determineTimeout(int phase) {
Long timeout = this.timeoutsForShutdownPhases.get(phase);
return (timeout != null ? timeout : this.timeoutPerShutdownPhase);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) {
@ -250,13 +288,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || isAutoStartupCandidate(beanName, bean)) {
int phase = getPhase(bean);
phases.computeIfAbsent(
phase,
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
int startupPhase = getPhase(bean);
phases.computeIfAbsent(startupPhase,
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, autoStartupOnly)
).add(beanName, bean);
}
});
if (!phases.isEmpty()) {
phases.values().forEach(LifecycleGroup::start);
}
@ -307,13 +345,14 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private void stopBeans() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new TreeMap<>(Comparator.reverseOrder());
lifecycleBeans.forEach((beanName, bean) -> {
int shutdownPhase = getPhase(bean);
phases.computeIfAbsent(
shutdownPhase,
p -> new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false)
phases.computeIfAbsent(shutdownPhase,
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, false)
).add(beanName, bean);
});
if (!phases.isEmpty()) {
phases.values().forEach(LifecycleGroup::stop);
}