diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java index f8e9487058..54eb6aaa76 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.web.reactive; +import io.undertow.Undertow.Builder; import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; @@ -222,6 +223,21 @@ class ReactiveWebServerFactoryAutoConfigurationTests { }); } + @Test + void jettyServerCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { + new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(Tomcat.class, HttpServer.class)) + .withUserConfiguration(DoubleRegistrationJettyServerCustomizerConfiguration.class, + HttpHandlerConfiguration.class) + .withPropertyValues("server.port=0").run((context) -> { + JettyReactiveWebServerFactory factory = context.getBean(JettyReactiveWebServerFactory.class); + JettyServerCustomizer customizer = context.getBean("serverCustomizer", JettyServerCustomizer.class); + assertThat(factory.getServerCustomizers()).contains(customizer); + verify(customizer, times(1)).customize(any(Server.class)); + }); + } + @Test void undertowDeploymentInfoCustomizerBeanIsAddedToFactory() { new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebApplicationContext::new) @@ -247,6 +263,22 @@ class ReactiveWebServerFactoryAutoConfigurationTests { }); } + @Test + void undertowBuilderCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { + new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(Tomcat.class, HttpServer.class, Server.class)) + .withUserConfiguration(DoubleRegistrationUndertowBuilderCustomizerConfiguration.class, + HttpHandlerConfiguration.class) + .withPropertyValues("server.port: 0").run((context) -> { + UndertowReactiveWebServerFactory factory = context.getBean(UndertowReactiveWebServerFactory.class); + UndertowBuilderCustomizer customizer = context.getBean("builderCustomizer", + UndertowBuilderCustomizer.class); + assertThat(factory.getBuilderCustomizers()).contains(customizer); + verify(customizer, times(1)).customize(any(Builder.class)); + }); + } + @Test void forwardedHeaderTransformerShouldBeConfigured() { this.contextRunner.withUserConfiguration(HttpHandlerConfiguration.class) @@ -401,6 +433,23 @@ class ReactiveWebServerFactoryAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class DoubleRegistrationJettyServerCustomizerConfiguration { + + private final JettyServerCustomizer customizer = mock(JettyServerCustomizer.class); + + @Bean + JettyServerCustomizer serverCustomizer() { + return this.customizer; + } + + @Bean + WebServerFactoryCustomizer jettyCustomizer() { + return (jetty) -> jetty.addServerCustomizers(this.customizer); + } + + } + @Configuration(proxyBeanMethods = false) static class UndertowBuilderCustomizerConfiguration { @@ -412,6 +461,23 @@ class ReactiveWebServerFactoryAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class DoubleRegistrationUndertowBuilderCustomizerConfiguration { + + private final UndertowBuilderCustomizer customizer = mock(UndertowBuilderCustomizer.class); + + @Bean + UndertowBuilderCustomizer builderCustomizer() { + return this.customizer; + } + + @Bean + WebServerFactoryCustomizer undertowCustomizer() { + return (undertow) -> undertow.addBuilderCustomizers(this.customizer); + } + + } + @Configuration(proxyBeanMethods = false) static class UndertowDeploymentInfoCustomizerConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java index 700698c5e4..cb7306bb0c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java @@ -22,6 +22,8 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import io.undertow.Undertow.Builder; +import io.undertow.servlet.api.DeploymentInfo; import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; @@ -163,6 +165,22 @@ class ServletWebServerFactoryAutoConfigurationTests { }); } + @Test + void jettyServerCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withClassLoader(new FilteredClassLoader(Tomcat.class, HttpServer.class)) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withUserConfiguration(DoubleRegistrationJettyServerCustomizerConfiguration.class) + .withPropertyValues("server.port: 0"); + runner.run((context) -> { + JettyServletWebServerFactory factory = context.getBean(JettyServletWebServerFactory.class); + JettyServerCustomizer customizer = context.getBean("serverCustomizer", JettyServerCustomizer.class); + assertThat(factory.getServerCustomizers()).contains(customizer); + verify(customizer, times(1)).customize(any(Server.class)); + }); + } + @Test void undertowDeploymentInfoCustomizerBeanIsAddedToFactory() { WebApplicationContextRunner runner = new WebApplicationContextRunner( @@ -177,6 +195,40 @@ class ServletWebServerFactoryAutoConfigurationTests { }); } + @Test + void undertowDeploymentInfoCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withClassLoader(new FilteredClassLoader(Tomcat.class, HttpServer.class, Server.class)) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withUserConfiguration(DoubleRegistrationUndertowDeploymentInfoCustomizerConfiguration.class) + .withPropertyValues("server.port: 0"); + runner.run((context) -> { + UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class); + UndertowDeploymentInfoCustomizer customizer = context.getBean("deploymentInfoCustomizer", + UndertowDeploymentInfoCustomizer.class); + assertThat(factory.getDeploymentInfoCustomizers()).contains(customizer); + verify(customizer, times(1)).customize(any(DeploymentInfo.class)); + }); + } + + @Test + void undertowBuilderCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withClassLoader(new FilteredClassLoader(Tomcat.class, HttpServer.class, Server.class)) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withUserConfiguration(DoubleRegistrationUndertowBuilderCustomizerConfiguration.class) + .withPropertyValues("server.port: 0"); + runner.run((context) -> { + UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class); + UndertowBuilderCustomizer customizer = context.getBean("builderCustomizer", + UndertowBuilderCustomizer.class); + assertThat(factory.getBuilderCustomizers()).contains(customizer); + verify(customizer, times(1)).customize(any(Builder.class)); + }); + } + @Test void undertowBuilderCustomizerBeanIsAddedToFactory() { WebApplicationContextRunner runner = new WebApplicationContextRunner( @@ -510,6 +562,23 @@ class ServletWebServerFactoryAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class DoubleRegistrationJettyServerCustomizerConfiguration { + + private final JettyServerCustomizer customizer = mock(JettyServerCustomizer.class); + + @Bean + JettyServerCustomizer serverCustomizer() { + return this.customizer; + } + + @Bean + WebServerFactoryCustomizer jettyCustomizer() { + return (jetty) -> jetty.addServerCustomizers(this.customizer); + } + + } + @Configuration(proxyBeanMethods = false) static class UndertowBuilderCustomizerConfiguration { @@ -521,6 +590,23 @@ class ServletWebServerFactoryAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class DoubleRegistrationUndertowBuilderCustomizerConfiguration { + + private final UndertowBuilderCustomizer customizer = mock(UndertowBuilderCustomizer.class); + + @Bean + UndertowBuilderCustomizer builderCustomizer() { + return this.customizer; + } + + @Bean + WebServerFactoryCustomizer undertowCustomizer() { + return (undertow) -> undertow.addBuilderCustomizers(this.customizer); + } + + } + @Configuration(proxyBeanMethods = false) static class UndertowDeploymentInfoCustomizerConfiguration { @@ -532,6 +618,23 @@ class ServletWebServerFactoryAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class DoubleRegistrationUndertowDeploymentInfoCustomizerConfiguration { + + private final UndertowDeploymentInfoCustomizer customizer = mock(UndertowDeploymentInfoCustomizer.class); + + @Bean + UndertowDeploymentInfoCustomizer deploymentInfoCustomizer() { + return this.customizer; + } + + @Bean + WebServerFactoryCustomizer undertowCustomizer() { + return (undertow) -> undertow.addDeploymentInfoCustomizers(this.customizer); + } + + } + @Configuration(proxyBeanMethods = false) static class ForwardedHeaderFilterConfiguration { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java index 53c93f124d..6b602226b3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java @@ -17,10 +17,10 @@ package org.springframework.boot.web.embedded.jetty; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; +import java.util.LinkedHashSet; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -68,7 +68,7 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact private boolean useForwardHeaders; - private List jettyServerCustomizers = new ArrayList<>(); + private Set jettyServerCustomizers = new LinkedHashSet<>(); private JettyResourceFactory resourceFactory; @@ -119,7 +119,7 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact */ public void setServerCustomizers(Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); - this.jettyServerCustomizers = new ArrayList<>(customizers); + this.jettyServerCustomizers = new LinkedHashSet<>(customizers); } /** 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 8f7b2ca124..36faf40b18 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,7 +27,9 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.server.AbstractConnector; @@ -101,7 +103,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor */ private int selectors = -1; - private List jettyServerCustomizers = new ArrayList<>(); + private Set jettyServerCustomizers = new LinkedHashSet<>(); private ResourceLoader resourceLoader; @@ -425,7 +427,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor */ public void setServerCustomizers(Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); - this.jettyServerCustomizers = new ArrayList<>(customizers); + this.jettyServerCustomizers = new LinkedHashSet<>(customizers); } /** 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 11f26bb70c..2fd8f68c5e 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,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.apache.catalina.Context; import org.apache.catalina.Engine; @@ -72,11 +73,11 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac private List contextLifecycleListeners = getDefaultLifecycleListeners(); - private Collection tomcatContextCustomizers = new LinkedHashSet<>(); + private Set tomcatContextCustomizers = new LinkedHashSet<>(); - private Collection tomcatConnectorCustomizers = new LinkedHashSet<>(); + private Set tomcatConnectorCustomizers = new LinkedHashSet<>(); - private Collection> tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(); + private Set> tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(); private final List additionalTomcatConnectors = new ArrayList<>(); @@ -234,7 +235,7 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac */ public void setTomcatContextCustomizers(Collection tomcatContextCustomizers) { Assert.notNull(tomcatContextCustomizers, "TomcatContextCustomizers must not be null"); - this.tomcatContextCustomizers = new ArrayList<>(tomcatContextCustomizers); + this.tomcatContextCustomizers = new LinkedHashSet<>(tomcatContextCustomizers); } /** @@ -265,7 +266,7 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac public void setTomcatConnectorCustomizers( Collection tomcatConnectorCustomizers) { Assert.notNull(tomcatConnectorCustomizers, "TomcatConnectorCustomizers must not be null"); - this.tomcatConnectorCustomizers = new ArrayList<>(tomcatConnectorCustomizers); + this.tomcatConnectorCustomizers = new LinkedHashSet<>(tomcatConnectorCustomizers); } /** @@ -297,7 +298,7 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac public void setTomcatProtocolHandlerCustomizers( Collection> tomcatProtocolHandlerCustomizers) { Assert.notNull(tomcatProtocolHandlerCustomizers, "TomcatProtocolHandlerCustomizers must not be null"); - this.tomcatProtocolHandlerCustomizers = new ArrayList<>(tomcatProtocolHandlerCustomizers); + this.tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(tomcatProtocolHandlerCustomizers); } /** 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 131ff8d265..8050c91749 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 @@ -116,11 +116,11 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto private List contextLifecycleListeners = getDefaultLifecycleListeners(); - private Collection tomcatContextCustomizers = new LinkedHashSet<>(); + private Set tomcatContextCustomizers = new LinkedHashSet<>(); - private Collection tomcatConnectorCustomizers = new LinkedHashSet<>(); + private Set tomcatConnectorCustomizers = new LinkedHashSet<>(); - private Collection> tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(); + private Set> tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(); private final List additionalTomcatConnectors = new ArrayList<>(); @@ -569,7 +569,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto */ public void setTomcatContextCustomizers(Collection tomcatContextCustomizers) { Assert.notNull(tomcatContextCustomizers, "TomcatContextCustomizers must not be null"); - this.tomcatContextCustomizers = new ArrayList<>(tomcatContextCustomizers); + this.tomcatContextCustomizers = new LinkedHashSet<>(tomcatContextCustomizers); } /** @@ -595,7 +595,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto public void setTomcatConnectorCustomizers( Collection tomcatConnectorCustomizers) { Assert.notNull(tomcatConnectorCustomizers, "TomcatConnectorCustomizers must not be null"); - this.tomcatConnectorCustomizers = new ArrayList<>(tomcatConnectorCustomizers); + this.tomcatConnectorCustomizers = new LinkedHashSet<>(tomcatConnectorCustomizers); } @Override @@ -622,7 +622,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto public void setTomcatProtocolHandlerCustomizers( Collection> tomcatProtocolHandlerCustomizer) { Assert.notNull(tomcatProtocolHandlerCustomizer, "TomcatProtocolHandlerCustomizers must not be null"); - this.tomcatProtocolHandlerCustomizers = new ArrayList<>(tomcatProtocolHandlerCustomizer); + this.tomcatProtocolHandlerCustomizers = new LinkedHashSet<>(tomcatProtocolHandlerCustomizer); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java index 272cc7812f..49db2c7a6c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java @@ -22,7 +22,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import io.undertow.Handlers; @@ -52,7 +54,7 @@ import org.springframework.util.Assert; public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerFactory implements ConfigurableUndertowWebServerFactory { - private List builderCustomizers = new ArrayList<>(); + private Set builderCustomizers = new LinkedHashSet<>(); private List deploymentInfoCustomizers = new ArrayList<>(); @@ -295,7 +297,7 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF */ public void setBuilderCustomizers(Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); - this.builderCustomizers = new ArrayList<>(customizers); + this.builderCustomizers = new LinkedHashSet<>(customizers); } /** 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 3e17e4fe6c..aa534c8c93 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 @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EventListener; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -92,9 +93,9 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac private static final Set> NO_CLASSES = Collections.emptySet(); - private List builderCustomizers = new ArrayList<>(); + private Set builderCustomizers = new LinkedHashSet<>(); - private List deploymentInfoCustomizers = new ArrayList<>(); + private Set deploymentInfoCustomizers = new LinkedHashSet<>(); private ResourceLoader resourceLoader; @@ -157,7 +158,7 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac */ public void setBuilderCustomizers(Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); - this.builderCustomizers = new ArrayList<>(customizers); + this.builderCustomizers = new LinkedHashSet<>(customizers); } /** @@ -183,7 +184,7 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac */ public void setDeploymentInfoCustomizers(Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); - this.deploymentInfoCustomizers = new ArrayList<>(customizers); + this.deploymentInfoCustomizers = new LinkedHashSet<>(customizers); } /**