Skip to content

Commit f17f125

Browse files
committed
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
1 parent b5673db commit f17f125

File tree

4 files changed

+46
-2
lines changed

4 files changed

+46
-2
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ protected HttpHandler getHttpHandler() {
136136

137137
@Override
138138
protected void doClose() {
139-
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
139+
if (this.isActive()) {
140+
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
141+
}
140142
super.doClose();
141143
}
142144

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ protected void onRefresh() {
164164

165165
@Override
166166
protected void doClose() {
167-
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
167+
if (this.isActive()) {
168+
AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
169+
}
168170
super.doClose();
169171
}
170172

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContextTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.junit.jupiter.api.AfterEach;
2525
import org.junit.jupiter.api.Test;
2626

27+
import org.springframework.beans.factory.BeanCreationException;
2728
import org.springframework.beans.factory.support.RootBeanDefinition;
2829
import org.springframework.boot.availability.AvailabilityChangeEvent;
2930
import org.springframework.boot.availability.ReadinessState;
@@ -141,6 +142,18 @@ void whenContextIsClosedThenApplicationAvailabilityChangesToRefusingTraffic() {
141142
.isEqualTo(ReadinessState.REFUSING_TRAFFIC);
142143
}
143144

145+
@Test
146+
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
147+
addWebServerFactoryBean();
148+
addHttpHandlerBean();
149+
TestApplicationListener listener = new TestApplicationListener();
150+
this.context.addApplicationListener(listener);
151+
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
152+
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
153+
this.context.close();
154+
assertThat(listener.receivedEvents()).isEmpty();
155+
}
156+
144157
@Test
145158
void whenTheContextIsRefreshedThenASubsequentRefreshAttemptWillFail() {
146159
addWebServerFactoryBean();
@@ -186,4 +199,12 @@ List<ApplicationEvent> receivedEvents() {
186199

187200
}
188201

202+
static class RefreshFailure {
203+
204+
RefreshFailure() {
205+
throw new RuntimeException("Fail refresh");
206+
}
207+
208+
}
209+
189210
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContextTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ void applicationIsUnreadyDuringShutdown() {
182182
ContextClosedEvent.class);
183183
}
184184

185+
@Test
186+
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
187+
addWebServerFactoryBean();
188+
TestApplicationListener listener = new TestApplicationListener();
189+
this.context.addApplicationListener(listener);
190+
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
191+
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
192+
this.context.close();
193+
assertThat(listener.receivedEvents()).isEmpty();
194+
}
195+
185196
@Test
186197
void cannotSecondRefresh() {
187198
addWebServerFactoryBean();
@@ -531,4 +542,12 @@ ServletRequest getRequest() {
531542

532543
}
533544

545+
static class RefreshFailure {
546+
547+
RefreshFailure() {
548+
throw new RuntimeException("Fail refresh");
549+
}
550+
551+
}
552+
534553
}

0 commit comments

Comments
 (0)