From a21bfc2ff55bc5589b26597cd662b8fe644583cd Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 11 Nov 2025 16:45:48 +0000 Subject: [PATCH] Delay ServletContext destruction until Undertow is destroyed Previously, all destruction was done in the stop method including closing any Closeables registered with the server. One of these Closeables managed the lifecycle of the DeploymentManager for the servlet deployment. Closing it made the servlet context unusable in `@PreDestroy` methods and upon restart. This commit moves the closing of the registered Closeables into destroy(). This allows `@PreDestory` methods to use the ServletContext. It also allows the server to be stopped and then restarted without making the ServletContext unusable as it's left running while the server itself is stopped and not accepting requests. Fixes gh-47141 --- .../web/embedded/undertow/UndertowWebServer.java | 16 +++++++++++++--- .../UndertowServletWebServerFactoryTests.java | 12 ------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java index 1567aa77d6a..702f2dcb0f6 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java @@ -279,9 +279,6 @@ public class UndertowWebServer implements WebServer { } try { this.undertow.stop(); - for (Closeable closeable : this.closeables) { - closeable.close(); - } } catch (Exception ex) { throw new WebServerException("Unable to stop Undertow", ex); @@ -289,6 +286,19 @@ public class UndertowWebServer implements WebServer { } } + @Override + public void destroy() { + stop(); + try { + for (Closeable closeable : this.closeables) { + closeable.close(); + } + } + catch (IOException ex) { + throw new WebServerException("Unable to destroy Undertow", ex); + } + } + @Override public int getPort() { List ports = getActualPorts(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java index 4e82019deeb..8af9aec3931 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java @@ -255,18 +255,6 @@ class UndertowServletWebServerFactoryTests extends AbstractServletWebServerFacto super.portClashOfSecondaryConnectorResultsInPortInUseException(); } - @Test - @Override - @Disabled("Restart after stop is not supported with Undertow") - protected void restartAfterStop() { - } - - @Test - @Override - @Disabled("Undertow's architecture prevents separating stop and destroy") - protected void servletContextListenerContextDestroyedIsNotCalledWhenContainerIsStopped() { - } - private void testAccessLog(String prefix, String suffix, String expectedFile) throws IOException, URISyntaxException { UndertowServletWebServerFactory factory = getFactory();