diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java index 91ef4daf5ae..fc3d9c1380c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.web.servlet; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.servlet.DispatcherType; import javax.servlet.ServletRequest; @@ -24,6 +25,7 @@ import javax.servlet.ServletRequest; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -38,6 +40,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor; import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.WebListenerRegistrar; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -69,8 +72,10 @@ import org.springframework.web.filter.ForwardedHeaderFilter; public class ServletWebServerFactoryAutoConfiguration { @Bean - public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { - return new ServletWebServerFactoryCustomizer(serverProperties); + public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, + ObjectProvider webListenerRegistrars) { + return new ServletWebServerFactoryCustomizer(serverProperties, + webListenerRegistrars.orderedStream().collect(Collectors.toList())); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java index 6ce83bcc7b9..ae259d1a17f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java @@ -16,9 +16,13 @@ package org.springframework.boot.autoconfigure.web.servlet; +import java.util.Collections; +import java.util.List; + import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.WebListenerRegistrar; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.core.Ordered; @@ -37,8 +41,16 @@ public class ServletWebServerFactoryCustomizer private final ServerProperties serverProperties; + private final Iterable webListenerRegistrars; + public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) { + this(serverProperties, Collections.emptyList()); + } + + public ServletWebServerFactoryCustomizer(ServerProperties serverProperties, + List webListenerRegistrars) { this.serverProperties = serverProperties; + this.webListenerRegistrars = webListenerRegistrars; } @Override @@ -62,6 +74,9 @@ public class ServletWebServerFactoryCustomizer map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader); map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters); map.from(this.serverProperties.getShutdown()).to(factory::setShutdown); + for (WebListenerRegistrar registrar : this.webListenerRegistrars) { + registrar.register(factory); + } } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java index fde52183670..d1fe7c98cde 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java @@ -27,6 +27,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.EventListener; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -46,8 +47,11 @@ import org.eclipse.jetty.server.session.DefaultSessionCache; import org.eclipse.jetty.server.session.FileSessionDataStore; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.servlet.ListenerHolder; +import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; +import org.eclipse.jetty.servlet.Source; import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; @@ -336,6 +340,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor configurations.add(getServletContextInitializerConfiguration(webAppContext, initializers)); configurations.add(getErrorPageConfiguration()); configurations.add(getMimeTypeConfiguration()); + configurations.add(new WebListenersConfiguration(getWebListenerClassNames())); configurations.addAll(getConfigurations()); return configurations.toArray(new Configuration[0]); } @@ -604,4 +609,40 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor } + /** + * {@link AbstractConfiguration} to apply {@code @WebListener} classes. + */ + private static class WebListenersConfiguration extends AbstractConfiguration { + + private final Set classNames; + + WebListenersConfiguration(Set webListenerClassNames) { + this.classNames = webListenerClassNames; + } + + @Override + public void configure(WebAppContext context) throws Exception { + ServletHandler servletHandler = context.getServletHandler(); + for (String className : this.classNames) { + configure(context, servletHandler, className); + } + } + + private void configure(WebAppContext context, ServletHandler servletHandler, String className) + throws ClassNotFoundException { + ListenerHolder holder = servletHandler.newListenerHolder(new Source(Source.Origin.ANNOTATION, className)); + holder.setHeldClass(loadClass(context, className)); + servletHandler.addListener(holder); + } + + @SuppressWarnings("unchecked") + private Class loadClass(WebAppContext context, String className) + throws ClassNotFoundException { + ClassLoader classLoader = context.getClassLoader(); + classLoader = (classLoader != null) ? classLoader : getClass().getClassLoader(); + return (Class) classLoader.loadClass(className); + } + + } + } 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 d8b8eb2a097..1dfd70ff987 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 @@ -383,6 +383,9 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto } configureSession(context); new DisableReferenceClearingContextCustomizer().customize(context); + for (String webListenerClassName : getWebListenerClassNames()) { + context.addApplicationListener(webListenerClassName); + } for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) { customizer.customize(context); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java index b5bba89c7d5..21812333033 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EventListener; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -47,6 +48,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; @@ -330,6 +332,7 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac deployment.setEagerFilterInit(this.eagerFilterInit); deployment.setPreservePathOnForward(this.preservePathOnForward); configureMimeMappings(deployment); + configureWebListeners(deployment); for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) { customizer.customize(deployment); } @@ -350,6 +353,22 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac return manager; } + private void configureWebListeners(DeploymentInfo deployment) { + for (String className : getWebListenerClassNames()) { + try { + deployment.addListener(new ListenerInfo(loadWebListenerClass(className))); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException("Failed to load web listener class '" + className + "'", ex); + } + } + } + + @SuppressWarnings("unchecked") + private Class loadWebListenerClass(String className) throws ClassNotFoundException { + return (Class) getServletClassLoader().loadClass(className); + } + private boolean isZeroOrLess(Duration timeoutDuration) { return timeoutDuration == null || timeoutDuration.isZero() || timeoutDuration.isNegative(); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerHandler.java index 5d107c00573..00463e4401b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,9 +38,25 @@ class WebListenerHandler extends ServletComponentHandler { @Override protected void doHandle(Map attributes, AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletListenerRegistrationBean.class); - builder.addPropertyValue("listener", beanDefinition); - registry.registerBeanDefinition(beanDefinition.getBeanClassName(), builder.getBeanDefinition()); + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .rootBeanDefinition(ServletComponentWebListenerRegistrar.class); + builder.addConstructorArgValue(beanDefinition.getBeanClassName()); + registry.registerBeanDefinition(beanDefinition.getBeanClassName() + "Registrar", builder.getBeanDefinition()); + } + + static class ServletComponentWebListenerRegistrar implements WebListenerRegistrar { + + private final String listenerClassName; + + ServletComponentWebListenerRegistrar(String listenerClassName) { + this.listenerClassName = listenerClassName; + } + + @Override + public void register(WebListenerRegistry registry) { + registry.addWebListeners(this.listenerClassName); + } + } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistrar.java new file mode 100644 index 00000000000..e9640103007 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistrar.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.servlet; + +import javax.servlet.annotation.WebListener; + +/** + * Interface to be implemented by types that register {@link WebListener @WebListeners}. + * + * @author Andy Wilkinson + * @since 2.4.0 + */ +public interface WebListenerRegistrar { + + /** + * Register web listeners with the given registry. + * @param registry the web listener registry + */ + void register(WebListenerRegistry registry); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistry.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistry.java new file mode 100644 index 00000000000..31ccf76d24d --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/WebListenerRegistry.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.servlet; + +import javax.servlet.annotation.WebListener; + +/** + * A registry that holds {@link WebListener @WebListeners}. + * + * @author Andy Wilkinson + * @since 2.4.0 + */ +public interface WebListenerRegistry { + + /** + * Adds web listeners that will be registered with the servlet container. + * @param webListenerClassNames the class names of the web listeners + */ + void addWebListeners(String... webListenerClassNames); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java index 38e6e29fca5..335e3654afe 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; @@ -81,6 +82,8 @@ public abstract class AbstractServletWebServerFactory extends AbstractConfigurab private final StaticResourceJars staticResourceJars = new StaticResourceJars(); + private final Set webListenerClassNames = new HashSet(); + /** * Create a new {@link AbstractServletWebServerFactory} instance. */ @@ -283,6 +286,15 @@ public abstract class AbstractServletWebServerFactory extends AbstractConfigurab return this.session.getSessionStoreDirectory().getValidDirectory(mkdirs); } + @Override + public void addWebListeners(String... webListenerClassNames) { + this.webListenerClassNames.addAll(Arrays.asList(webListenerClassNames)); + } + + protected final Set getWebListenerClassNames() { + return this.webListenerClassNames; + } + /** * {@link ServletContextInitializer} to apply appropriate parts of the {@link Session} * configuration. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java index a2cdd5264a5..d58d86ec28d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java @@ -28,6 +28,7 @@ import org.springframework.boot.web.server.ConfigurableWebServerFactory; import org.springframework.boot.web.server.MimeMappings; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.boot.web.servlet.WebListenerRegistry; /** * A configurable {@link ServletWebServerFactory}. @@ -41,7 +42,8 @@ import org.springframework.boot.web.servlet.ServletContextInitializer; * @see ServletWebServerFactory * @see WebServerFactoryCustomizer */ -public interface ConfigurableServletWebServerFactory extends ConfigurableWebServerFactory, ServletWebServerFactory { +public interface ConfigurableServletWebServerFactory + extends ConfigurableWebServerFactory, ServletWebServerFactory, WebListenerRegistry { /** * Sets the context path for the web server. The context should start with a "/" diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java index bf9ffcf7b28..5792bd96e7f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.Map; import java.util.Properties; +import java.util.stream.Stream; import javax.servlet.MultipartConfigElement; import javax.servlet.annotation.WebFilter; @@ -30,12 +31,19 @@ import javax.servlet.annotation.WebListener; import javax.servlet.annotation.WebServlet; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; +import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.boot.web.servlet.testcomponents.TestMultipartServlet; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,6 +60,9 @@ class ServletComponentScanIntegrationTests { private AnnotationConfigServletWebServerApplicationContext context; + @TempDir + File temp; + @AfterEach void cleanUp() { if (this.context != null) { @@ -59,37 +70,40 @@ class ServletComponentScanIntegrationTests { } } - @Test - void componentsAreRegistered() { + @ParameterizedTest(name = "{0}") + @MethodSource("testConfiguration") + void componentsAreRegistered(String serverName, Class configuration) { this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(TestConfiguration.class); + this.context.register(configuration); new ServerPortInfoApplicationContextInitializer().initialize(this.context); this.context.refresh(); String port = this.context.getEnvironment().getProperty("local.server.port"); String response = new RestTemplate().getForObject("http://localhost:" + port + "/test", String.class); - assertThat(response).isEqualTo("alpha bravo"); + assertThat(response).isEqualTo("alpha bravo charlie"); } - @Test - void indexedComponentsAreRegistered(@TempDir File temp) throws IOException { - writeIndex(temp); + @ParameterizedTest(name = "{0}") + @MethodSource("testConfiguration") + void indexedComponentsAreRegistered(String serverName, Class configuration) throws IOException { + writeIndex(this.temp); this.context = new AnnotationConfigServletWebServerApplicationContext(); - try (URLClassLoader classLoader = new URLClassLoader(new URL[] { temp.toURI().toURL() }, + try (URLClassLoader classLoader = new URLClassLoader(new URL[] { this.temp.toURI().toURL() }, getClass().getClassLoader())) { this.context.setClassLoader(classLoader); - this.context.register(TestConfiguration.class); + this.context.register(configuration); new ServerPortInfoApplicationContextInitializer().initialize(this.context); this.context.refresh(); String port = this.context.getEnvironment().getProperty("local.server.port"); String response = new RestTemplate().getForObject("http://localhost:" + port + "/test", String.class); - assertThat(response).isEqualTo("alpha bravo"); + assertThat(response).isEqualTo("alpha bravo charlie"); } } - @Test - void multipartConfigIsHonoured() { + @ParameterizedTest(name = "{0}") + @MethodSource("testConfiguration") + void multipartConfigIsHonoured(String serverName, Class configuration) { this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(TestConfiguration.class); + this.context.register(configuration); new ServerPortInfoApplicationContextInitializer().initialize(this.context); this.context.refresh(); @SuppressWarnings("rawtypes") @@ -118,15 +132,54 @@ class ServletComponentScanIntegrationTests { } } - @Configuration(proxyBeanMethods = false) + static Stream testConfiguration() { + return Stream.of(Arguments.of("Jetty", JettyTestConfiguration.class), + Arguments.of("Tomcat", TomcatTestConfiguration.class), + Arguments.of("Undertow", UndertowTestConfiguration.class)); + } + @ServletComponentScan(basePackages = "org.springframework.boot.web.servlet.testcomponents") - static class TestConfiguration { + abstract static class AbstractTestConfiguration { @Bean - TomcatServletWebServerFactory webServerFactory() { + protected ServletWebServerFactory webServerFactory(ObjectProvider webListenerRegistrars) { + ConfigurableServletWebServerFactory factory = createWebServerFactory(); + webListenerRegistrars.orderedStream().forEach((registrar) -> registrar.register(factory)); + return factory; + } + + abstract ConfigurableServletWebServerFactory createWebServerFactory(); + + } + + @Configuration(proxyBeanMethods = false) + static class JettyTestConfiguration extends AbstractTestConfiguration { + + @Override + ConfigurableServletWebServerFactory createWebServerFactory() { + return new JettyServletWebServerFactory(0); + } + + } + + @Configuration(proxyBeanMethods = false) + static class TomcatTestConfiguration extends AbstractTestConfiguration { + + @Override + ConfigurableServletWebServerFactory createWebServerFactory() { return new TomcatServletWebServerFactory(0); } } + @Configuration(proxyBeanMethods = false) + static class UndertowTestConfiguration extends AbstractTestConfiguration { + + @Override + ConfigurableServletWebServerFactory createWebServerFactory() { + return new UndertowServletWebServerFactory(0); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/WebListenerHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/WebListenerHandlerTests.java index 3fa5b1e5158..f787927469a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/WebListenerHandlerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/WebListenerHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ class WebListenerHandlerTests { given(definition.getMetadata()).willReturn(new SimpleMetadataReaderFactory() .getMetadataReader(TestListener.class.getName()).getAnnotationMetadata()); this.handler.handle(definition, this.registry); - this.registry.getBeanDefinition(TestListener.class.getName()); + this.registry.getBeanDefinition(TestListener.class.getName() + "Registrar"); } @WebListener diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java index 3eb45556ef9..0a8b0caeb83 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,27 @@ package org.springframework.boot.web.servlet.testcomponents; +import java.io.IOException; +import java.util.EnumSet; + +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.annotation.WebListener; @WebListener -class TestListener implements ServletContextListener { +public class TestListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { + sce.getServletContext().addFilter("listenerAddedFilter", new ListenerAddedFilter()) + .addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); sce.getServletContext().setAttribute("listenerAttribute", "alpha"); - } @Override @@ -34,4 +44,15 @@ class TestListener implements ServletContextListener { } + static class ListenerAddedFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + request.setAttribute("listenerAddedFilterAttribute", "charlie"); + chain.doFilter(request, response); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java index a7cc03cabe4..08875488276 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print(((String) req.getServletContext().getAttribute("listenerAttribute")) + " " - + req.getAttribute("filterAttribute")); + + req.getAttribute("filterAttribute") + " " + req.getAttribute("listenerAddedFilterAttribute")); resp.getWriter().flush(); }