Skip to content

Commit 2b33e31

Browse files
jpmsilvaphilwebb
authored andcommitted
Allow Tomcat be destroyed regardless of exceptions
Update `TomcatWebServer` so that lifecycle exceptions are silently swallowed when attempting shutdown. Prior to this commit it was possible that a Tomcat instance might not be properly destroyed and could leave non daemon threads running, which prevent the JVM from exiting. Fixes gh-16892
1 parent c519932 commit 2b33e31

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private void initialize() throws WebServerException {
122122
}
123123
catch (Exception ex) {
124124
stopSilently();
125+
destroySilently();
125126
throw new WebServerException("Unable to start embedded Tomcat", ex);
126127
}
127128
}
@@ -242,6 +243,15 @@ private void stopSilently() {
242243
}
243244
}
244245

246+
private void destroySilently() {
247+
try {
248+
this.tomcat.destroy();
249+
}
250+
catch (LifecycleException ex) {
251+
// Ignore
252+
}
253+
}
254+
245255
private void stopTomcat() throws LifecycleException {
246256
if (Thread.currentThread()
247257
.getContextClassLoader() instanceof TomcatEmbeddedWebappClassLoader) {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,25 @@ protected void doPost(HttpServletRequest req,
523523
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
524524
}
525525

526+
@Test
527+
public void exceptionThrownOnContextListenerDestroysServer() {
528+
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0) {
529+
@Override
530+
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
531+
try {
532+
return super.getTomcatWebServer(tomcat);
533+
}
534+
finally {
535+
assertThat(tomcat.getServer().getState())
536+
.isEqualTo(LifecycleState.DESTROYED);
537+
}
538+
}
539+
};
540+
assertThatExceptionOfType(WebServerException.class)
541+
.isThrownBy(() -> factory.getWebServer((context) -> context
542+
.addListener(new FailingServletContextListener())));
543+
}
544+
526545
@Override
527546
protected JspServlet getJspServlet() throws ServletException {
528547
Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,15 @@ public void init() throws ServletException {
14011401

14021402
}
14031403

1404+
public static class FailingServletContextListener implements ServletContextListener {
1405+
1406+
@Override
1407+
public void contextInitialized(ServletContextEvent sce) {
1408+
throw new FailingServletException();
1409+
}
1410+
1411+
}
1412+
14041413
private static class FailingServletException extends RuntimeException {
14051414

14061415
FailingServletException() {

0 commit comments

Comments
 (0)