Skip to content

Commit f08f872

Browse files
committed
Restore original embedded container shutdown order
Update EmbeddedWebApplicationContext so that the servlet container is shutdown after the context is closed. Unfortunately shutting the container down before the context has been closed causes exceptions if the `/shutdown` actuator endpoint is used. It can also cause the Tomcat classloader to throw IllegalStateExceptions if resources are accessed during shutdown. As this commit effectively reverts 0069e41 we need to fix the shutdown deadlock issue reported in gh-4130 in a different way. The deadlock can be caused when an incoming HTTP connection occurs whilst the context is closing. The incoming connection triggers the `FrameworkServlet` to call `initWebApplicationContext` which in turn calls `refresh`. The `FrameworkServlet` checks `ApplicationContext.isActive()` before performing an initialization but prior to this commit we would set active to `false` before stopping the servlet container. We now override `onClose` rather than `doClose` in `EmbeddedWebApplicationContext` to ensure that the active flag is only set to `false` once the servlet container has been stopped. See gh-4130 Fixes gh-4396
1 parent 6d90188 commit f08f872

File tree

3 files changed

+2
-50
lines changed

3 files changed

+2
-50
lines changed

spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ protected void finishRefresh() {
146146
}
147147

148148
@Override
149-
protected void doClose() {
149+
protected void onClose() {
150+
super.onClose();
150151
stopAndReleaseEmbeddedServletContainer();
151-
super.doClose();
152152
}
153153

154154
private synchronized void createEmbeddedServletContainer() {

spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContextTests.java

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,11 @@
3737
import org.mockito.InOrder;
3838

3939
import org.springframework.beans.MutablePropertyValues;
40-
import org.springframework.beans.factory.DisposableBean;
4140
import org.springframework.beans.factory.config.BeanDefinition;
4241
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4342
import org.springframework.beans.factory.config.ConstructorArgumentValues;
4443
import org.springframework.beans.factory.config.Scope;
45-
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
4644
import org.springframework.beans.factory.support.RootBeanDefinition;
47-
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory.MockEmbeddedServletContainer;
4845
import org.springframework.context.ApplicationContextException;
4946
import org.springframework.context.ApplicationListener;
5047
import org.springframework.context.support.AbstractApplicationContext;
@@ -58,7 +55,6 @@
5855

5956
import static org.hamcrest.Matchers.equalTo;
6057
import static org.hamcrest.Matchers.instanceOf;
61-
import static org.hamcrest.Matchers.is;
6258
import static org.hamcrest.Matchers.nullValue;
6359
import static org.hamcrest.Matchers.sameInstance;
6460
import static org.junit.Assert.assertEquals;
@@ -434,22 +430,6 @@ public void doesNotReplaceExistingScopes() throws Exception { // gh-2082
434430
sameInstance(scope));
435431
}
436432

437-
@Test
438-
public void containerIsStoppedBeforeContextIsClosed() {
439-
addEmbeddedServletContainerFactoryBean();
440-
this.context.registerBeanDefinition("shutdownOrderingValidator",
441-
BeanDefinitionBuilder.rootBeanDefinition(ShutdownOrderingValidator.class)
442-
.addConstructorArgReference("embeddedServletContainerFactory")
443-
.getBeanDefinition());
444-
this.context.refresh();
445-
ShutdownOrderingValidator validator = this.context
446-
.getBean(ShutdownOrderingValidator.class);
447-
this.context.close();
448-
assertThat(validator.destroyed, is(true));
449-
assertThat(validator.containerStoppedFirst, is(true));
450-
451-
}
452-
453433
private void addEmbeddedServletContainerFactoryBean() {
454434
this.context.registerBeanDefinition("embeddedServletContainerFactory",
455435
new RootBeanDefinition(MockEmbeddedServletContainerFactory.class));
@@ -499,25 +479,4 @@ public void doFilter(ServletRequest request, ServletResponse response,
499479

500480
}
501481

502-
protected static class ShutdownOrderingValidator implements DisposableBean {
503-
504-
private final MockEmbeddedServletContainer servletContainer;
505-
506-
private boolean destroyed = false;
507-
508-
private boolean containerStoppedFirst = false;
509-
510-
ShutdownOrderingValidator(
511-
MockEmbeddedServletContainerFactory servletContainerFactory) {
512-
this.servletContainer = servletContainerFactory.getContainer();
513-
}
514-
515-
@Override
516-
public void destroy() {
517-
this.destroyed = true;
518-
this.containerStoppedFirst = this.servletContainer.isStopped();
519-
}
520-
521-
}
522-
523482
}

spring-boot/src/test/java/org/springframework/boot/context/embedded/MockEmbeddedServletContainerFactory.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ public static class MockEmbeddedServletContainer implements EmbeddedServletConta
9090

9191
private final int port;
9292

93-
private boolean stopped = false;
94-
9593
public MockEmbeddedServletContainer(ServletContextInitializer[] initializers,
9694
int port) {
9795
this.initializers = initializers;
@@ -190,11 +188,6 @@ public void start() throws EmbeddedServletContainerException {
190188
public void stop() {
191189
this.servletContext = null;
192190
this.registeredServlets.clear();
193-
this.stopped = true;
194-
}
195-
196-
public boolean isStopped() {
197-
return this.stopped;
198191
}
199192

200193
public Servlet[] getServlets() {

0 commit comments

Comments
 (0)