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
This commit is contained in:
parent
de8b304da4
commit
9c68a2ab87
|
@ -33,12 +33,13 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFacto
|
|||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.AnnotationConfigRegistry;
|
||||
import org.springframework.context.aot.ApplicationContextAotGenerator;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
|
@ -55,8 +56,7 @@ import org.springframework.util.Assert;
|
|||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ChildManagementContextInitializer
|
||||
implements ApplicationListener<WebServerInitializedEvent>, BeanRegistrationAotProcessor {
|
||||
class ChildManagementContextInitializer implements BeanRegistrationAotProcessor, SmartLifecycle {
|
||||
|
||||
private final ManagementContextFactory managementContextFactory;
|
||||
|
||||
|
@ -64,6 +64,8 @@ class ChildManagementContextInitializer
|
|||
|
||||
private final ApplicationContextInitializer<ConfigurableApplicationContext> applicationContextInitializer;
|
||||
|
||||
private volatile ConfigurableApplicationContext managementContext;
|
||||
|
||||
ChildManagementContextInitializer(ManagementContextFactory managementContextFactory,
|
||||
ApplicationContext parentContext) {
|
||||
this(managementContextFactory, parentContext, null);
|
||||
|
@ -79,12 +81,33 @@ class ChildManagementContextInitializer
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
if (event.getApplicationContext().equals(this.parentContext)) {
|
||||
public void start() {
|
||||
if (this.managementContext == null) {
|
||||
ConfigurableApplicationContext managementContext = createManagementContext();
|
||||
registerBeans(managementContext);
|
||||
managementContext.refresh();
|
||||
this.managementContext = managementContext;
|
||||
}
|
||||
else {
|
||||
this.managementContext.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.managementContext != null) {
|
||||
this.managementContext.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.managementContext != null && this.managementContext.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return WebServerGracefulShutdownLifecycle.SMART_LIFECYCLE_PHASE + 512;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -55,6 +55,21 @@ class ManagementContextAutoConfigurationTests {
|
|||
.run((context) -> assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void childManagementContextShouldRestartWhenParentIsStoppedThenStarted(CapturedOutput output) {
|
||||
WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(
|
||||
AnnotationConfigServletWebServerApplicationContext::new)
|
||||
.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class,
|
||||
ServletWebServerFactoryAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
|
||||
WebEndpointAutoConfiguration.class, EndpointAutoConfiguration.class));
|
||||
contextRunner.withPropertyValues("server.port=0", "management.server.port=0").run((context) -> {
|
||||
assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 2));
|
||||
context.getSourceApplicationContext().stop();
|
||||
context.getSourceApplicationContext().start();
|
||||
assertThat(output).satisfies(numberOfOccurrences("Tomcat started on port", 4));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenSamePortManagementServerWhenManagementServerAddressIsConfiguredThenContextRefreshFails() {
|
||||
WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(
|
||||
|
|
Loading…
Reference in New Issue