From 2bb7a430cf7757ed00a6ea1b0f417a5b4d2e2c55 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 16 Dec 2015 23:24:22 +0000 Subject: [PATCH] Clean up access logging threads when Undertow is stopped Closes gh-12742 --- ...dertowEmbeddedServletContainerFactory.java | 74 ++++++++++++++----- ...wEmbeddedServletContainerFactoryTests.java | 6 ++ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index 65dce182611..810ab7dbe7e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EventListener; import java.util.List; import java.util.Locale; import java.util.Map; @@ -46,6 +47,8 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import io.undertow.Undertow; @@ -64,6 +67,7 @@ import io.undertow.server.session.SessionManager; import io.undertow.servlet.Servlets; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentManager; +import io.undertow.servlet.api.ListenerInfo; import io.undertow.servlet.api.MimeMapping; import io.undertow.servlet.api.ServletContainerInitializerInfo; import io.undertow.servlet.api.ServletStackTraces; @@ -417,34 +421,41 @@ public class UndertowEmbeddedServletContainerFactory } private void configureAccessLog(DeploymentInfo deploymentInfo) { - deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() { - - @Override - public HttpHandler wrap(HttpHandler handler) { - return createAccessLogHandler(handler); - } - - }); - } - - private AccessLogHandler createAccessLogHandler(HttpHandler handler) { try { createAccessLogDirectoryIfNecessary(); + XnioWorker worker = createWorker(); String prefix = (this.accessLogPrefix != null ? this.accessLogPrefix : "access_log."); - AccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver( - createWorker(), this.accessLogDirectory, prefix, this.accessLogSuffix, + DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver( + worker, this.accessLogDirectory, prefix, this.accessLogSuffix, this.accessLogRotate); - String formatString = (this.accessLogPattern != null ? this.accessLogPattern - : "common"); - return new AccessLogHandler(handler, accessLogReceiver, formatString, - Undertow.class.getClassLoader()); + EventListener listener = new AccessLogShutdownListener(worker, + accessLogReceiver); + deploymentInfo.addListener(new ListenerInfo(AccessLogShutdownListener.class, + new ImmediateInstanceFactory(listener))); + deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() { + + @Override + public HttpHandler wrap(HttpHandler handler) { + return createAccessLogHandler(handler, accessLogReceiver); + } + + }); } catch (IOException ex) { throw new IllegalStateException("Failed to create AccessLogHandler", ex); } } + private AccessLogHandler createAccessLogHandler(HttpHandler handler, + AccessLogReceiver accessLogReceiver) { + createAccessLogDirectoryIfNecessary(); + String formatString = (this.accessLogPattern != null) ? this.accessLogPattern + : "common"; + return new AccessLogHandler(handler, accessLogReceiver, formatString, + Undertow.class.getClassLoader()); + } + private void createAccessLogDirectoryIfNecessary() { Assert.state(this.accessLogDirectory != null, "Access log directory is not set"); if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) { @@ -790,4 +801,33 @@ public class UndertowEmbeddedServletContainerFactory } + private static class AccessLogShutdownListener implements ServletContextListener { + + private final XnioWorker worker; + + private final DefaultAccessLogReceiver accessLogReceiver; + + AccessLogShutdownListener(XnioWorker worker, + DefaultAccessLogReceiver accessLogReceiver) { + this.worker = worker; + this.accessLogReceiver = accessLogReceiver; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + try { + this.accessLogReceiver.close(); + this.worker.shutdown(); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java index b37c7d82a2a..5c46ce48016 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java @@ -37,6 +37,7 @@ import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentManager; import io.undertow.servlet.api.ServletContainer; import org.apache.jasper.servlet.JspServlet; +import org.junit.AfterClass; import org.junit.Test; import org.mockito.InOrder; @@ -73,6 +74,11 @@ public class UndertowEmbeddedServletContainerFactoryTests return new UndertowEmbeddedServletContainerFactory(0); } + @AfterClass + public static void afterClass() { + + } + @Test public void errorPage404() throws Exception { AbstractEmbeddedServletContainerFactory factory = getFactory();