Skip to content

Commit 16c4759

Browse files
committed
Merge branch '2.5.x' into 2.6.x
Closes gh-29905
2 parents 9da6fb4 + 00114f9 commit 16c4759

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ private void handleRunFailure(ConfigurableApplicationContext context, Throwable
786786
reportFailure(getExceptionReporters(context), exception);
787787
if (context != null) {
788788
context.close();
789+
shutdownHook.deregisterFailedApplicationContext(context);
789790
}
790791
}
791792
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationShutdownHook.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
*
4444
* @author Andy Wilkinson
4545
* @author Phillip Webb
46+
* @author Brian Clozel
4647
*/
4748
class SpringApplicationShutdownHook implements Runnable {
4849

@@ -92,6 +93,17 @@ void addRuntimeShutdownHook() {
9293
}
9394
}
9495

96+
void deregisterFailedApplicationContext(ConfigurableApplicationContext applicationContext) {
97+
synchronized (SpringApplicationShutdownHook.class) {
98+
if (!applicationContext.isActive()) {
99+
SpringApplicationShutdownHook.this.contexts.remove(applicationContext);
100+
}
101+
else {
102+
throw new IllegalStateException("Cannot unregister active application context");
103+
}
104+
}
105+
}
106+
95107
@Override
96108
public void run() {
97109
Set<ConfigurableApplicationContext> contexts;

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationShutdownHookTests.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import org.awaitility.Awaitility;
2727
import org.junit.jupiter.api.Test;
2828

29+
import org.springframework.beans.factory.BeanCreationException;
2930
import org.springframework.beans.factory.InitializingBean;
3031
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3132
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -36,12 +37,14 @@
3637
import static org.assertj.core.api.Assertions.assertThat;
3738
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3839
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
40+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3941

4042
/**
4143
* Tests for {@link SpringApplicationShutdownHook}.
4244
*
4345
* @author Phillip Webb
4446
* @author Andy Wilkinson
47+
* @author Brian Clozel
4548
*/
4649
class SpringApplicationShutdownHookTests {
4750

@@ -154,6 +157,29 @@ void removeHandlerActionWhenShuttingDownThrowsException() {
154157
.withMessage("Shutdown in progress");
155158
}
156159

160+
@Test
161+
void failsWhenDeregisterActiveContext() {
162+
TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook();
163+
ConfigurableApplicationContext context = new GenericApplicationContext();
164+
shutdownHook.registerApplicationContext(context);
165+
context.refresh();
166+
assertThatThrownBy(() -> shutdownHook.deregisterFailedApplicationContext(context))
167+
.isInstanceOf(IllegalStateException.class);
168+
assertThat(shutdownHook.isApplicationContextRegistered(context)).isTrue();
169+
}
170+
171+
@Test
172+
void deregistersFailedContext() {
173+
TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook();
174+
GenericApplicationContext context = new GenericApplicationContext();
175+
shutdownHook.registerApplicationContext(context);
176+
context.registerBean(FailingBean.class);
177+
assertThatThrownBy(context::refresh).isInstanceOf(BeanCreationException.class);
178+
assertThat(shutdownHook.isApplicationContextRegistered(context)).isTrue();
179+
shutdownHook.deregisterFailedApplicationContext(context);
180+
assertThat(shutdownHook.isApplicationContextRegistered(context)).isFalse();
181+
}
182+
157183
static class TestSpringApplicationShutdownHook extends SpringApplicationShutdownHook {
158184

159185
private boolean runtimeShutdownHookAdded;
@@ -259,4 +285,13 @@ public void afterPropertiesSet() throws Exception {
259285

260286
}
261287

288+
static class FailingBean implements InitializingBean {
289+
290+
@Override
291+
public void afterPropertiesSet() throws Exception {
292+
throw new IllegalArgumentException("test failure");
293+
}
294+
295+
}
296+
262297
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
package org.springframework.boot;
1818

19+
import java.util.ArrayList;
1920
import java.util.Collections;
2021
import java.util.HashMap;
2122
import java.util.Iterator;
2223
import java.util.LinkedHashSet;
24+
import java.util.List;
2325
import java.util.Map;
2426
import java.util.Set;
2527
import java.util.concurrent.atomic.AtomicInteger;
@@ -150,6 +152,7 @@
150152
* @author Marten Deinum
151153
* @author Nguyen Bao Sach
152154
* @author Chris Bono
155+
* @author Brian Clozel
153156
*/
154157
@ExtendWith(OutputCaptureExtension.class)
155158
class SpringApplicationTests {
@@ -1247,6 +1250,20 @@ void bindsEnvironmentPrefixToSpringApplication() {
12471250
assertThat(application.getEnvironmentPrefix()).isEqualTo("my");
12481251
}
12491252

1253+
@Test
1254+
void deregistersShutdownHookForFailedApplicationContext() {
1255+
SpringApplication application = new SpringApplication(BrokenPostConstructConfig.class);
1256+
List<ApplicationEvent> events = new ArrayList<>();
1257+
application.addListeners(events::add);
1258+
application.setWebApplicationType(WebApplicationType.NONE);
1259+
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(application::run);
1260+
assertThat(events).hasAtLeastOneElementOfType(ApplicationFailedEvent.class);
1261+
ApplicationFailedEvent failure = events.stream().filter((event) -> event instanceof ApplicationFailedEvent)
1262+
.map(ApplicationFailedEvent.class::cast).findFirst().get();
1263+
assertThat(SpringApplicationShutdownHookInstance.get())
1264+
.didNotRegisterApplicationContext(failure.getApplicationContext());
1265+
}
1266+
12501267
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
12511268
S state) {
12521269
return (argument) -> (argument instanceof AvailabilityChangeEvent<?>)

0 commit comments

Comments
 (0)