Commit f17f1255 authored by Andy Wilkinson's avatar Andy Wilkinson

Do not change availability on close unless context is active

Previously, an AvailabilityChangeEvent was published when the servlet
and reactive web server application contexts were closed, irrespective
of whether or not the context was active. This caused problems when
the context was not active due to a refresh failure as the event
publication could then trigger bean creation and post-processing that
relied upon beans that had been destroyed when cleaning up after the
refresh failure. The most commonly seen symptom was a missing
importRegistry bean that is required by ImportAwareBeanPostProcessor.

This commit updates the two web server application contexts to only
publish the availability change event if the context is active.

Fixes gh-21588
parent b5673db6
......@@ -136,7 +136,9 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
@Override
protected void doClose() {
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
if (this.isActive()) {
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
}
super.doClose();
}
......
......@@ -164,7 +164,9 @@ public class ServletWebServerApplicationContext extends GenericWebApplicationCon
@Override
protected void doClose() {
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
if (this.isActive()) {
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
}
super.doClose();
}
......
......@@ -24,6 +24,7 @@ import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
......@@ -141,6 +142,18 @@ class ReactiveWebServerApplicationContextTests {
.isEqualTo(ReadinessState.REFUSING_TRAFFIC);
}
@Test
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
addWebServerFactoryBean();
addHttpHandlerBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.addApplicationListener(listener);
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
this.context.close();
assertThat(listener.receivedEvents()).isEmpty();
}
@Test
void whenTheContextIsRefreshedThenASubsequentRefreshAttemptWillFail() {
addWebServerFactoryBean();
......@@ -186,4 +199,12 @@ class ReactiveWebServerApplicationContextTests {
}
static class RefreshFailure {
RefreshFailure() {
throw new RuntimeException("Fail refresh");
}
}
}
......@@ -182,6 +182,17 @@ class ServletWebServerApplicationContextTests {
ContextClosedEvent.class);
}
@Test
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
addWebServerFactoryBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.addApplicationListener(listener);
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
this.context.close();
assertThat(listener.receivedEvents()).isEmpty();
}
@Test
void cannotSecondRefresh() {
addWebServerFactoryBean();
......@@ -531,4 +542,12 @@ class ServletWebServerApplicationContextTests {
}
static class RefreshFailure {
RefreshFailure() {
throw new RuntimeException("Fail refresh");
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment