From 89c284cb13fab2025a3387b87b61554e0e5f8e26 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 12 May 2017 11:29:12 +0200 Subject: [PATCH] Add reactive websocket auto-configuration for Tomcat This commit adds a `TomcatWebSocketReactiveWebServerCustomizer` that customizes the Tomcat context to accept WebSockets connections. Since reactive servers don't use the JSR 356 for that support, only Tomcat customization is required for now. This commit also reorders the server auto-configuration so that undertow has a chance to be auto-configured before reactor-netty, which should be a popular dependency thanks to its HTTP client library. The existing WebSocket infrastructure for Serlvet based containers has been moved to a dedicated package and renamed accordingly. Fixes gh-9113 --- .../ReactiveWebServerAutoConfiguration.java | 4 +- ...tWebSocketReactiveWebServerCustomizer.java | 53 ++++++++++++++++ .../WebSocketReactiveAutoConfiguration.java | 60 +++++++++++++++++++ .../websocket/reactive/package-info.java | 20 +++++++ ...yWebSocketServletWebServerCustomizer.java} | 6 +- ...tWebSocketServletWebServerCustomizer.java} | 17 ++---- ...wWebSocketServletWebServerCustomizer.java} | 6 +- .../WebSocketMessagingAutoConfiguration.java | 2 +- .../WebSocketServletAutoConfiguration.java} | 30 +++++----- .../websocket/{ => servlet}/package-info.java | 6 +- .../main/resources/META-INF/spring.factories | 5 +- ...SocketMessagingAutoConfigurationTests.java | 10 ++-- ...bSocketServletAutoConfigurationTests.java} | 10 ++-- .../TomcatReactiveWebServerFactory.java | 51 ++++++++++++++++ .../UndertowReactiveWebServerFactory.java | 15 +++++ .../TomcatReactiveWebServerFactoryTests.java | 29 ++++++++- ...UndertowReactiveWebServerFactoryTests.java | 3 +- 17 files changed, 272 insertions(+), 55 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/TomcatWebSocketReactiveWebServerCustomizer.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/package-info.java rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{JettyWebSocketContainerCustomizer.java => servlet/JettyWebSocketServletWebServerCustomizer.java} (92%) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{TomcatWebSocketContainerCustomizer.java => servlet/TomcatWebSocketServletWebServerCustomizer.java} (82%) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{UndertowWebSocketContainerCustomizer.java => servlet/UndertowWebSocketServletWebServerCustomizer.java} (92%) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{ => servlet}/WebSocketMessagingAutoConfiguration.java (98%) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{WebSocketAutoConfiguration.java => servlet/WebSocketServletAutoConfiguration.java} (71%) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/{ => servlet}/package-info.java (75%) rename spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/{ => servlet}/WebSocketMessagingAutoConfigurationTests.java (96%) rename spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/{WebSocketAutoConfigurationTests.java => servlet/WebSocketServletAutoConfigurationTests.java} (90%) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerAutoConfiguration.java index 0f5a00d69b..83fe674cce 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerAutoConfiguration.java @@ -50,8 +50,8 @@ import org.springframework.util.ObjectUtils; @Import({ ReactiveWebServerAutoConfiguration.BeanPostProcessorsRegistrar.class, ReactiveWebServerConfiguration.TomcatAutoConfiguration.class, ReactiveWebServerConfiguration.JettyAutoConfiguration.class, - ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration.class, - ReactiveWebServerConfiguration.UndertowAutoConfiguration.class }) + ReactiveWebServerConfiguration.UndertowAutoConfiguration.class, + ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration.class}) public class ReactiveWebServerAutoConfiguration { @ConditionalOnMissingBean diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/TomcatWebSocketReactiveWebServerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/TomcatWebSocketReactiveWebServerCustomizer.java new file mode 100644 index 0000000000..b1fe9465eb --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/TomcatWebSocketReactiveWebServerCustomizer.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.autoconfigure.websocket.reactive; + +import org.apache.catalina.Context; +import org.apache.tomcat.websocket.server.WsContextListener; + +import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer; +import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.Ordered; + +/** + * WebSocket customizer for {@link TomcatReactiveWebServerFactory}. + * + * @author Brian Clozel + * @since 2.0.0 + */ +public class TomcatWebSocketReactiveWebServerCustomizer + implements WebServerFactoryCustomizer, Ordered { + + @Override + public void customize(TomcatReactiveWebServerFactory factory) { + factory.addContextCustomizers(new TomcatContextCustomizer() { + + @Override + public void customize(Context context) { + context.addApplicationListener(WsContextListener.class.getName()); + } + + }); + } + + @Override + public int getOrder() { + return 0; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java new file mode 100644 index 0000000000..6231b9b7e8 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.autoconfigure.websocket.reactive; + +import javax.servlet.Servlet; +import javax.websocket.server.ServerContainer; + +import org.apache.catalina.startup.Tomcat; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; +import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for WebSocket reactive server in Tomcat, Jetty or Undertow. + * Requires the appropriate WebSocket modules to be on the classpath. + *

+ * If Tomcat's WebSocket support is detected on the classpath we add a customizer that + * installs the Tomcat WebSocket initializer. + * + * @author Brian Clozel + */ +@Configuration +@ConditionalOnClass({ Servlet.class, ServerContainer.class }) +@ConditionalOnWebApplication(type = Type.REACTIVE) +@AutoConfigureBefore(ReactiveWebServerAutoConfiguration.class) +public class WebSocketReactiveAutoConfiguration { + + @Configuration + @ConditionalOnClass(name = "org.apache.tomcat.websocket.server.WsSci", value = Tomcat.class) + static class TomcatWebSocketConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "websocketReactiveWebServerCustomizer") + public TomcatWebSocketReactiveWebServerCustomizer websocketContainerCustomizer() { + return new TomcatWebSocketReactiveWebServerCustomizer(); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/package-info.java new file mode 100644 index 0000000000..82d8e503a3 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2017 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 + * + * http://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. + */ + +/** + * Auto-configuration for WebSocket support in reactive web servers. + */ +package org.springframework.boot.autoconfigure.websocket.reactive; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/JettyWebSocketContainerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java similarity index 92% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/JettyWebSocketContainerCustomizer.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java index ab383c7187..581016c7be 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/JettyWebSocketContainerCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.webapp.AbstractConfiguration; @@ -32,9 +32,9 @@ import org.springframework.core.Ordered; * @author Dave Syer * @author Phillip Webb * @author Andy Wilkinson - * @since 1.2.0 + * @since 2.0.0 */ -public class JettyWebSocketContainerCustomizer +public class JettyWebSocketServletWebServerCustomizer implements WebServerFactoryCustomizer, Ordered { @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/TomcatWebSocketContainerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/TomcatWebSocketServletWebServerCustomizer.java similarity index 82% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/TomcatWebSocketContainerCustomizer.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/TomcatWebSocketServletWebServerCustomizer.java index fdb80908a0..23331ad4d3 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/TomcatWebSocketContainerCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/TomcatWebSocketServletWebServerCustomizer.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; -import org.apache.catalina.Context; import org.apache.tomcat.websocket.server.WsContextListener; import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer; @@ -30,21 +29,15 @@ import org.springframework.core.Ordered; * @author Dave Syer * @author Phillip Webb * @author Andy Wilkinson - * @since 1.2.0 + * @since 2.0.0 */ -public class TomcatWebSocketContainerCustomizer +public class TomcatWebSocketServletWebServerCustomizer implements WebServerFactoryCustomizer, Ordered { @Override public void customize(TomcatServletWebServerFactory factory) { - factory.addContextCustomizers(new TomcatContextCustomizer() { - - @Override - public void customize(Context context) { - context.addApplicationListener(WsContextListener.class.getName()); - } - - }); + factory.addContextCustomizers((TomcatContextCustomizer) context -> + context.addApplicationListener(WsContextListener.class.getName())); } @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/UndertowWebSocketContainerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/UndertowWebSocketServletWebServerCustomizer.java similarity index 92% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/UndertowWebSocketContainerCustomizer.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/UndertowWebSocketServletWebServerCustomizer.java index d037ce09bf..ff71f4320c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/UndertowWebSocketContainerCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/UndertowWebSocketServletWebServerCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; @@ -28,9 +28,9 @@ import org.springframework.core.Ordered; * WebSocket customizer for {@link UndertowServletWebServerFactory}. * * @author Phillip Webb - * @since 1.2.0 + * @since 2.0.0 */ -public class UndertowWebSocketContainerCustomizer +public class UndertowWebSocketServletWebServerCustomizer implements WebServerFactoryCustomizer, Ordered { @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java similarity index 98% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfiguration.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java index 99cb55308f..367a3476fd 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import java.util.List; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java similarity index 71% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java index 1f832a93b2..cf66fe20a8 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import javax.servlet.Servlet; import javax.websocket.server.ServerContainer; @@ -32,11 +32,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * Auto configuration for websocket server in embedded Tomcat, Jetty or Undertow. Requires - * the appropriate WebSocket modules to be on the classpath. + * Auto configuration for WebSocket servlet server in embedded Tomcat, Jetty or Undertow. + * Requires the appropriate WebSocket modules to be on the classpath. *

* If Tomcat's WebSocket support is detected on the classpath we add a customizer that - * installs the Tomcat Websocket initializer. In a non-embedded server it should already + * installs the Tomcat WebSocket initializer. In a non-embedded server it should already * be there. *

* If Jetty's WebSocket support is detected on the classpath we add a configuration that @@ -44,7 +44,7 @@ import org.springframework.context.annotation.Configuration; * already be there. *

* If Undertow's WebSocket support is detected on the classpath we add a customizer that - * installs the Undertow Websocket DeploymentInfo Customizer. In a non-embedded server it + * installs the Undertow WebSocket DeploymentInfo Customizer. In a non-embedded server it * should already be there. * * @author Dave Syer @@ -55,16 +55,16 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnClass({ Servlet.class, ServerContainer.class }) @ConditionalOnWebApplication(type = Type.SERVLET) @AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class) -public class WebSocketAutoConfiguration { +public class WebSocketServletAutoConfiguration { @Configuration @ConditionalOnClass(name = "org.apache.tomcat.websocket.server.WsSci", value = Tomcat.class) static class TomcatWebSocketConfiguration { @Bean - @ConditionalOnMissingBean(name = "websocketContainerCustomizer") - public TomcatWebSocketContainerCustomizer websocketContainerCustomizer() { - return new TomcatWebSocketContainerCustomizer(); + @ConditionalOnMissingBean(name = "websocketServletWebServerCustomizer") + public TomcatWebSocketServletWebServerCustomizer websocketContainerCustomizer() { + return new TomcatWebSocketServletWebServerCustomizer(); } } @@ -74,9 +74,9 @@ public class WebSocketAutoConfiguration { static class JettyWebSocketConfiguration { @Bean - @ConditionalOnMissingBean(name = "websocketContainerCustomizer") - public JettyWebSocketContainerCustomizer websocketContainerCustomizer() { - return new JettyWebSocketContainerCustomizer(); + @ConditionalOnMissingBean(name = "websocketServletWebServerCustomizer") + public JettyWebSocketServletWebServerCustomizer websocketContainerCustomizer() { + return new JettyWebSocketServletWebServerCustomizer(); } } @@ -86,9 +86,9 @@ public class WebSocketAutoConfiguration { static class UndertowWebSocketConfiguration { @Bean - @ConditionalOnMissingBean(name = "websocketContainerCustomizer") - public UndertowWebSocketContainerCustomizer websocketContainerCustomizer() { - return new UndertowWebSocketContainerCustomizer(); + @ConditionalOnMissingBean(name = "websocketServletWebServerCustomizer") + public UndertowWebSocketServletWebServerCustomizer websocketContainerCustomizer() { + return new UndertowWebSocketServletWebServerCustomizer(); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/package-info.java similarity index 75% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/package-info.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/package-info.java index de04bfabbc..037c33652c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/package-info.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -15,6 +15,6 @@ */ /** - * Auto-configuration for Spring WebSocket. + * Auto-configuration for WebSocket support in servlet web servers. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 0d4090fcef..14474fb5bd 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -118,8 +118,9 @@ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfigurati org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ -org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ -org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ +org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ +org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ +org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration # Failure analyzers diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java similarity index 96% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfigurationTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java index 35eb5966e9..f95f1c45a6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketMessagingAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import java.lang.reflect.Type; import java.util.ArrayList; @@ -207,10 +207,10 @@ public class WebSocketMessagingAutoConfigurationTests { @EnableWebSocket @EnableConfigurationProperties @EnableWebSocketMessageBroker - @ImportAutoConfiguration({ JacksonAutoConfiguration.class, + @ImportAutoConfiguration({JacksonAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class, WebSocketMessagingAutoConfiguration.class, - DispatcherServletAutoConfiguration.class }) + DispatcherServletAutoConfiguration.class}) static class WebSocketMessagingConfiguration extends AbstractWebSocketMessageBrokerConfigurer { @@ -235,8 +235,8 @@ public class WebSocketMessagingAutoConfigurationTests { } @Bean - public TomcatWebSocketContainerCustomizer tomcatCustomizer() { - return new TomcatWebSocketContainerCustomizer(); + public TomcatWebSocketServletWebServerCustomizer tomcatCustomizer() { + return new TomcatWebSocketServletWebServerCustomizer(); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java similarity index 90% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java index 43293d1ad7..ce30dcff1e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.websocket; +package org.springframework.boot.autoconfigure.websocket.servlet; import javax.websocket.server.ServerContainer; @@ -33,11 +33,11 @@ import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link WebSocketAutoConfiguration} + * Tests for {@link WebSocketServletAutoConfiguration} * * @author Andy Wilkinson */ -public class WebSocketAutoConfigurationTests { +public class WebSocketServletAutoConfigurationTests { private AnnotationConfigServletWebServerApplicationContext context; @@ -56,13 +56,13 @@ public class WebSocketAutoConfigurationTests { @Test public void tomcatServerContainerIsAvailableFromTheServletContext() { serverContainerIsAvailableFromTheServletContext(TomcatConfiguration.class, - WebSocketAutoConfiguration.TomcatWebSocketConfiguration.class); + WebSocketServletAutoConfiguration.TomcatWebSocketConfiguration.class); } @Test public void jettyServerContainerIsAvailableFromTheServletContext() { serverContainerIsAvailableFromTheServletContext(JettyConfiguration.class, - WebSocketAutoConfiguration.JettyWebSocketConfiguration.class); + WebSocketServletAutoConfiguration.JettyWebSocketConfiguration.class); } private void serverContainerIsAvailableFromTheServletContext( diff --git a/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java b/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java index 62b5688e80..a11f1fd20c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java @@ -17,7 +17,12 @@ package org.springframework.boot.web.embedded.tomcat; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.apache.catalina.Context; import org.apache.catalina.Host; import org.apache.catalina.connector.Connector; import org.apache.catalina.loader.WebappLoader; @@ -48,6 +53,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac private String protocol = DEFAULT_PROTOCOL; + private List tomcatContextCustomizers = new ArrayList<>(); + /** * Create a new {@link TomcatServletWebServerFactory} instance. */ @@ -97,9 +104,20 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac context.setLoader(loader); Tomcat.addServlet(context, "httpHandlerServlet", servlet); context.addServletMappingDecoded("/", "httpHandlerServlet"); + configureContext(context); host.addChild(context); } + /** + * Configure the Tomcat {@link Context}. + * @param context the Tomcat context + */ + protected void configureContext(Context context) { + for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) { + customizer.customize(context); + } + } + // Needs to be protected so it can be used by subclasses protected void customizeConnector(Connector connector) { int port = (getPort() >= 0 ? getPort() : 0); @@ -122,6 +140,39 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac } } + /** + * Set {@link TomcatContextCustomizer}s that should be applied to the Tomcat + * {@link Context} . Calling this method will replace any existing customizers. + * @param tomcatContextCustomizers the customizers to set + */ + public void setTomcatContextCustomizers( + Collection tomcatContextCustomizers) { + Assert.notNull(tomcatContextCustomizers, + "TomcatContextCustomizers must not be null"); + this.tomcatContextCustomizers = new ArrayList<>(tomcatContextCustomizers); + } + + /** + * Returns a mutable collection of the {@link TomcatContextCustomizer}s that will be + * applied to the Tomcat {@link Context} . + * @return the listeners that will be applied + */ + public Collection getTomcatContextCustomizers() { + return this.tomcatContextCustomizers; + } + + /** + * Add {@link TomcatContextCustomizer}s that should be added to the Tomcat + * {@link Context}. + * @param tomcatContextCustomizers the customizers to add + */ + public void addContextCustomizers( + TomcatContextCustomizer... tomcatContextCustomizers) { + Assert.notNull(tomcatContextCustomizers, + "TomcatContextCustomizers must not be null"); + this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers)); + } + /** * 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/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java b/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java index 3b9d41b5ff..479206bbf0 100644 --- a/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java @@ -88,4 +88,19 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF return getAddress().getHostAddress(); } + public void setBufferSize(Integer bufferSize) { + this.bufferSize = bufferSize; + } + + public void setIoThreads(Integer ioThreads) { + this.ioThreads = ioThreads; + } + + public void setWorkerThreads(Integer workerThreads) { + this.workerThreads = workerThreads; + } + + public void setDirectBuffers(Boolean directBuffers) { + this.directBuffers = directBuffers; + } } diff --git a/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java index 093e1cab7e..f7b166b42d 100644 --- a/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java @@ -16,8 +16,18 @@ package org.springframework.boot.web.embedded.tomcat; -import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; +import java.util.Arrays; + +import org.apache.catalina.Context; +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.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; /** * Tests for {@link TomcatReactiveWebServerFactory}. @@ -28,8 +38,23 @@ public class TomcatReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests { @Override - protected AbstractReactiveWebServerFactory getFactory() { + protected TomcatReactiveWebServerFactory getFactory() { return new TomcatReactiveWebServerFactory(0); } + @Test + public void tomcatCustomizers() throws Exception { + TomcatReactiveWebServerFactory factory = getFactory(); + TomcatContextCustomizer[] listeners = new TomcatContextCustomizer[4]; + for (int i = 0; i < listeners.length; i++) { + listeners[i] = mock(TomcatContextCustomizer.class); + } + factory.setTomcatContextCustomizers(Arrays.asList(listeners[0], listeners[1])); + factory.addContextCustomizers(listeners[2], listeners[3]); + this.webServer = factory.getWebServer(mock(HttpHandler.class)); + InOrder ordered = inOrder((Object[]) listeners); + for (TomcatContextCustomizer listener : listeners) { + ordered.verify(listener).customize(any(Context.class)); + } + } } diff --git a/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java index c1fd9d1eeb..6f90f6ce4e 100644 --- a/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java @@ -16,7 +16,6 @@ package org.springframework.boot.web.embedded.undertow; -import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests; /** @@ -28,7 +27,7 @@ public class UndertowReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests { @Override - protected AbstractReactiveWebServerFactory getFactory() { + protected UndertowReactiveWebServerFactory getFactory() { return new UndertowReactiveWebServerFactory(0); }