diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java index 47f47aeca31..e09544be976 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java @@ -24,7 +24,9 @@ import java.util.List; import org.apache.catalina.Context; import org.apache.catalina.Host; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.AprLifecycleListener; import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.AbstractProtocol; @@ -53,6 +55,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac private String protocol = DEFAULT_PROTOCOL; + private List contextLifecycleListeners = Arrays.asList(new AprLifecycleListener()); + private List tomcatContextCustomizers = new ArrayList<>(); private List tomcatConnectorCustomizers = new ArrayList<>(); @@ -115,6 +119,9 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac * @param context the Tomcat context */ protected void configureContext(Context context) { + for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) { + context.addLifecycleListener(lifecycleListener); + } for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) { customizer.customize(context); } @@ -211,6 +218,39 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac return this.tomcatConnectorCustomizers; } + /** + * Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context} + * . Calling this method will replace any existing listeners. + * @param contextLifecycleListeners the listeners to set + */ + public void setContextLifecycleListeners( + Collection contextLifecycleListeners) { + Assert.notNull(contextLifecycleListeners, + "ContextLifecycleListeners must not be null"); + this.contextLifecycleListeners = new ArrayList<>(contextLifecycleListeners); + } + + /** + * Returns a mutable collection of the {@link LifecycleListener}s that will be applied + * to the Tomcat {@link Context} . + * @return the context lifecycle listeners that will be applied + */ + public Collection getContextLifecycleListeners() { + return this.contextLifecycleListeners; + } + + /** + * Add {@link LifecycleListener}s that should be added to the Tomcat {@link Context}. + * @param contextLifecycleListeners the listeners to add + */ + public void addContextLifecycleListeners( + LifecycleListener... contextLifecycleListeners) { + Assert.notNull(contextLifecycleListeners, + "ContextLifecycleListeners must not be null"); + this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners)); + } + + /** * Factory method called to create the {@link TomcatWebServer}. Subclasses can * override this method to return a different {@link TomcatWebServer} or apply diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java index 6ea4fa90f7e..3805340e2d7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java @@ -50,6 +50,7 @@ import org.apache.catalina.WebResourceRoot.ResourceSetType; import org.apache.catalina.WebResourceSet; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.AprLifecycleListener; import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.session.StandardManager; import org.apache.catalina.startup.Tomcat; @@ -122,7 +123,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto private List contextValves = new ArrayList<>(); - private List contextLifecycleListeners = new ArrayList<>(); + private List contextLifecycleListeners = Arrays.asList(new AprLifecycleListener()); private List tomcatContextCustomizers = new ArrayList<>(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java index bbff214c0ff..25a33607846 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java @@ -19,13 +19,17 @@ package org.springframework.boot.web.embedded.tomcat; import java.util.Arrays; import org.apache.catalina.Context; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.AprLifecycleListener; import org.junit.Test; import org.mockito.InOrder; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests; import org.springframework.http.server.reactive.HttpHandler; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -60,6 +64,30 @@ public class TomcatReactiveWebServerFactoryTests } } + @Test + public void defaultTomcatListeners() throws Exception { + TomcatReactiveWebServerFactory factory = getFactory(); + assertThat(factory.getContextLifecycleListeners()) + .hasSize(1) + .first().isInstanceOf(AprLifecycleListener.class); + } + + @Test + public void tomcatListeners() throws Exception { + TomcatReactiveWebServerFactory factory = getFactory(); + LifecycleListener[] listeners = new LifecycleListener[4]; + for (int i = 0; i < listeners.length; i++) { + listeners[i] = mock(LifecycleListener.class); + } + factory.setContextLifecycleListeners(Arrays.asList(listeners[0], listeners[1])); + factory.addContextLifecycleListeners(listeners[2], listeners[3]); + this.webServer = factory.getWebServer(mock(HttpHandler.class)); + InOrder ordered = inOrder((Object[]) listeners); + for (LifecycleListener listener : listeners) { + ordered.verify(listener).lifecycleEvent(any(LifecycleEvent.class)); + } + } + @Test public void setNullConnectorCustomizersShouldThrowException() { TomcatReactiveWebServerFactory factory = getFactory(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java index 917bbe2cf72..d1fdb475577 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java @@ -37,6 +37,7 @@ import org.apache.catalina.Service; import org.apache.catalina.SessionIdGenerator; import org.apache.catalina.Valve; import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.AprLifecycleListener; import org.apache.catalina.core.StandardWrapper; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.util.CharsetMapper; @@ -102,6 +103,14 @@ public class TomcatServletWebServerFactoryTests tomcatWebServer.stop(); } + @Test + public void defaultTomcatListeners() throws Exception { + TomcatServletWebServerFactory factory = getFactory(); + assertThat(factory.getContextLifecycleListeners()) + .hasSize(1) + .first().isInstanceOf(AprLifecycleListener.class); + } + @Test public void tomcatListeners() throws Exception { TomcatServletWebServerFactory factory = getFactory();