Skip to content

Commit 457bf94

Browse files
committed
Configure individual timeouts for specific shutdown phases
Closes gh-32985
1 parent b7e4fa0 commit 457bf94

File tree

1 file changed

+46
-7
lines changed

1 file changed

+46
-7
lines changed

spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java

+46-7
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
106106

107107
private final Log logger = LogFactory.getLog(getClass());
108108

109+
private final Map<Integer, Long> timeoutsForShutdownPhases = new ConcurrentHashMap<>();
110+
109111
private volatile long timeoutPerShutdownPhase = 10000;
110112

111113
private volatile boolean running;
@@ -132,6 +134,37 @@ else if (checkpointOnRefresh) {
132134
}
133135

134136

137+
/**
138+
* Specify the maximum time allotted for the shutdown of each given phase
139+
* (group of {@link SmartLifecycle} beans with the same 'phase' value).
140+
* <p>In case of no specific timeout configured, the default timeout per
141+
* shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2.
142+
* @param timeoutsForShutdownPhases a map of phase values (matching
143+
* {@link SmartLifecycle#getPhase()}) and corresponding timeout values
144+
* (in milliseconds)
145+
* @since 6.2
146+
* @see SmartLifecycle#getPhase()
147+
* @see #setTimeoutPerShutdownPhase
148+
*/
149+
public void setTimeoutsForShutdownPhases(Map<Integer, Long> timeoutsForShutdownPhases) {
150+
this.timeoutsForShutdownPhases.putAll(timeoutsForShutdownPhases);
151+
}
152+
153+
/**
154+
* Specify the maximum time allotted for the shutdown of a specific phase
155+
* (group of {@link SmartLifecycle} beans with the same 'phase' value).
156+
* <p>In case of no specific timeout configured, the default timeout per
157+
* shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2.
158+
* @param phase the phase value (matching {@link SmartLifecycle#getPhase()})
159+
* @param timeout the corresponding timeout value (in milliseconds)
160+
* @since 6.2
161+
* @see SmartLifecycle#getPhase()
162+
* @see #setTimeoutPerShutdownPhase
163+
*/
164+
public void setTimeoutForShutdownPhase(int phase, long timeout) {
165+
this.timeoutsForShutdownPhases.put(phase, timeout);
166+
}
167+
135168
/**
136169
* Specify the maximum time allotted in milliseconds for the shutdown of any
137170
* phase (group of {@link SmartLifecycle} beans with the same 'phase' value).
@@ -142,6 +175,11 @@ public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
142175
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
143176
}
144177

178+
private long determineTimeout(int phase) {
179+
Long timeout = this.timeoutsForShutdownPhases.get(phase);
180+
return (timeout != null ? timeout : this.timeoutPerShutdownPhase);
181+
}
182+
145183
@Override
146184
public void setBeanFactory(BeanFactory beanFactory) {
147185
if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) {
@@ -250,13 +288,13 @@ private void startBeans(boolean autoStartupOnly) {
250288

251289
lifecycleBeans.forEach((beanName, bean) -> {
252290
if (!autoStartupOnly || isAutoStartupCandidate(beanName, bean)) {
253-
int phase = getPhase(bean);
254-
phases.computeIfAbsent(
255-
phase,
256-
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
291+
int startupPhase = getPhase(bean);
292+
phases.computeIfAbsent(startupPhase,
293+
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, autoStartupOnly)
257294
).add(beanName, bean);
258295
}
259296
});
297+
260298
if (!phases.isEmpty()) {
261299
phases.values().forEach(LifecycleGroup::start);
262300
}
@@ -307,13 +345,14 @@ private boolean toBeStarted(String beanName, Lifecycle bean) {
307345
private void stopBeans() {
308346
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
309347
Map<Integer, LifecycleGroup> phases = new TreeMap<>(Comparator.reverseOrder());
348+
310349
lifecycleBeans.forEach((beanName, bean) -> {
311350
int shutdownPhase = getPhase(bean);
312-
phases.computeIfAbsent(
313-
shutdownPhase,
314-
p -> new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false)
351+
phases.computeIfAbsent(shutdownPhase,
352+
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, false)
315353
).add(beanName, bean);
316354
});
355+
317356
if (!phases.isEmpty()) {
318357
phases.values().forEach(LifecycleGroup::stop);
319358
}

0 commit comments

Comments
 (0)