Skip to content

Commit 9c68a2a

Browse files
committed
Integrate child management context with parent context's lifecycle
Previously, the child management context was created when the parent context's web server was initialized and it wasn't stopped or closed until the parent context was closed. This resulted in the child context being left running when the parent context was stopped. This would then cause a failure when the parent context was started again as another web server initialized event would be received and a second child management context would be started. This commit updates the initialization of the child management context to integrate it with the lifecycle of the parent context. The management context is now created the first time the parent context is started. It is stopped when the parent context is stopped and restarted if the parent context is started again. This lifecycle management is done using a phase that ensures that the child context is not started until the parent context's web server has been started. Fixes gh-38502
1 parent de8b304 commit 9c68a2a

File tree

2 files changed

+43
-5
lines changed

2 files changed

+43
-5
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ChildManagementContextInitializer.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@
3333
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
3434
import org.springframework.boot.context.event.ApplicationFailedEvent;
3535
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
36-
import org.springframework.boot.web.context.WebServerInitializedEvent;
36+
import org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle;
3737
import org.springframework.context.ApplicationContext;
3838
import org.springframework.context.ApplicationContextInitializer;
3939
import org.springframework.context.ApplicationEvent;
4040
import org.springframework.context.ApplicationListener;
4141
import org.springframework.context.ConfigurableApplicationContext;
42+
import org.springframework.context.SmartLifecycle;
4243
import org.springframework.context.annotation.AnnotationConfigRegistry;
4344
import org.springframework.context.aot.ApplicationContextAotGenerator;
4445
import org.springframework.context.event.ContextClosedEvent;
@@ -55,15 +56,16 @@
5556
* @author Andy Wilkinson
5657
* @author Phillip Webb
5758
*/
58-
class ChildManagementContextInitializer
59-
implements ApplicationListener<WebServerInitializedEvent>, BeanRegistrationAotProcessor {
59+
class ChildManagementContextInitializer implements BeanRegistrationAotProcessor, SmartLifecycle {
6060

6161
private final ManagementContextFactory managementContextFactory;
6262

6363
private final ApplicationContext parentContext;
6464

6565
private final ApplicationContextInitializer<ConfigurableApplicationContext> applicationContextInitializer;
6666

67+
private volatile ConfigurableApplicationContext managementContext;
68+
6769
ChildManagementContextInitializer(ManagementContextFactory managementContextFactory,
6870
ApplicationContext parentContext) {
6971
this(managementContextFactory, parentContext, null);
@@ -79,14 +81,35 @@ private ChildManagementContextInitializer(ManagementContextFactory managementCon
7981
}
8082

8183
@Override
82-
public void onApplicationEvent(WebServerInitializedEvent event) {
83-
if (event.getApplicationContext().equals(this.parentContext)) {
84+
public void start() {
85+
if (this.managementContext == null) {
8486
ConfigurableApplicationContext managementContext = createManagementContext();
8587
registerBeans(managementContext);
8688
managementContext.refresh();
89+
this.managementContext = managementContext;
90+
}
91+
else {
92+
this.managementContext.start();
93+
}
94+
}
95+
96+
@Override
97+
public void stop() {
98+
if (this.managementContext != null) {
99+
this.managementContext.stop();
87100
}
88101
}
89102

103+
@Override
104+
public boolean isRunning() {
105+
return this.managementContext != null && this.managementContext.isRunning();
106+
}
107+
108+
@Override
109+
public int getPhase() {
110+
return WebServerGracefulShutdownLifecycle.SMART_LIFECYCLE_PHASE + 512;
111+
}
112+
90113
@Override
91114
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
92115
Assert.isInstanceOf(ConfigurableApplicationContext.class, this.parentContext);

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfigurationTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ void childManagementContextShouldStartForEmbeddedServer(CapturedOutput output) {
5555
.run((context) -> assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 2)));
5656
}
5757

58+
@Test
59+
void childManagementContextShouldRestartWhenParentIsStoppedThenStarted(CapturedOutput output) {
60+
WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(
61+
AnnotationConfigServletWebServerApplicationContext::new)
62+
.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class,
63+
ServletWebServerFactoryAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
64+
WebEndpointAutoConfiguration.class, EndpointAutoConfiguration.class));
65+
contextRunner.withPropertyValues("server.port=0", "management.server.port=0").run((context) -> {
66+
assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 2));
67+
context.getSourceApplicationContext().stop();
68+
context.getSourceApplicationContext().start();
69+
assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 4));
70+
});
71+
}
72+
5873
@Test
5974
void givenSamePortManagementServerWhenManagementServerAddressIsConfiguredThenContextRefreshFails() {
6075
WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(

0 commit comments

Comments
 (0)