diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java index 9f4e147359e..5db8000d3c4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; +import org.springframework.web.reactive.socket.server.WebSocketService; /** * A subclass of {@code WebFluxConfigurationSupport} that detects and delegates @@ -98,6 +99,12 @@ public class DelegatingWebFluxConfiguration extends WebFluxConfigurationSupport return (messageCodesResolver != null ? messageCodesResolver : super.getMessageCodesResolver()); } + @Override + protected WebSocketService getWebSocketService() { + WebSocketService service = this.configurers.getWebSocketService(); + return (service != null ? service : super.getWebSocketService()); + } + @Override protected void configureViewResolvers(ViewResolverRegistry registry) { this.configurers.configureViewResolvers(registry); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index e61749bb300..c288384d077 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -65,6 +65,8 @@ import org.springframework.web.reactive.result.method.annotation.ResponseBodyRes import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler; import org.springframework.web.reactive.result.view.ViewResolutionResultHandler; import org.springframework.web.reactive.result.view.ViewResolver; +import org.springframework.web.reactive.socket.server.WebSocketService; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebExceptionHandler; import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver; @@ -431,6 +433,24 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { return new SimpleHandlerAdapter(); } + @Bean + public WebSocketHandlerAdapter webFluxWebSocketHandlerAdapter() { + WebSocketService service = getWebSocketService(); + WebSocketHandlerAdapter adapter = (service != null ? + new WebSocketHandlerAdapter(service) : new WebSocketHandlerAdapter()); + + // For backwards compatibility, lower the (default) priority + int defaultOrder = adapter.getOrder(); + adapter.setOrder(defaultOrder + 1); + + return adapter; + } + + @Nullable + protected WebSocketService getWebSocketService() { + return null; + } + @Bean public ResponseEntityResultHandler responseEntityResultHandler( @Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry, diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java index e33b07b801d..43280489f7c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-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. @@ -25,6 +25,7 @@ import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; +import org.springframework.web.reactive.socket.server.WebSocketService; /** * Defines callback methods to customize the configuration for WebFlux @@ -124,6 +125,18 @@ public interface WebFluxConfigurer { return null; } + /** + * Provide the {@link WebSocketService} to create + * {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter} + * with. This can be used to configure server-specific properties through the + * {@link org.springframework.web.reactive.socket.server.RequestUpgradeStrategy}. + * @since 5.3 + */ + @Nullable + default WebSocketService getWebSocketService() { + return null; + } + /** * Configure view resolution for rendering responses with a view and a model, * where the view is typically an HTML template but could also be based on diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java index 2e1eed91cd4..18161d497ed 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-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,6 +30,7 @@ import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; +import org.springframework.web.reactive.socket.server.WebSocketService; /** * A {@link WebFluxConfigurer} that delegates to one or more others. @@ -70,6 +71,12 @@ public class WebFluxConfigurerComposite implements WebFluxConfigurer { this.delegates.forEach(delegate -> delegate.addResourceHandlers(registry)); } + @Nullable + @Override + public WebSocketService getWebSocketService() { + return createSingleBean(WebFluxConfigurer::getWebSocketService, WebSocketService.class); + } + @Override public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { this.delegates.forEach(delegate -> delegate.configureArgumentResolvers(configurer)); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java index 41448844611..a2b01c6afd0 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-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. @@ -17,8 +17,8 @@ package org.springframework.web.reactive.socket.server.support; import reactor.core.publisher.Mono; +import org.springframework.core.Ordered; import org.springframework.util.Assert; -import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.HandlerAdapter; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.socket.WebSocketHandler; @@ -26,18 +26,30 @@ import org.springframework.web.reactive.socket.server.WebSocketService; import org.springframework.web.server.ServerWebExchange; /** - * {@link HandlerAdapter} that allows using a {@link WebSocketHandler} with the - * generic {@link DispatcherHandler} mapping URLs directly to such handlers. - * Requests are handled by delegating to the configured {@link WebSocketService} - * which by default is {@link HandshakeWebSocketService}. + * {@code HandlerAdapter} that allows + * {@link org.springframework.web.reactive.DispatcherHandler} to support + * handlers of type {@link WebSocketHandler} with such handlers mapped to + * URL patterns via + * {@link org.springframework.web.reactive.handler.SimpleUrlHandlerMapping}. + * + *
Requests are handled by delegating to a + * {@link WebSocketService}, by default {@link HandshakeWebSocketService}, + * which checks the WebSocket handshake request parameters, upgrades to a + * WebSocket interaction, and uses the {@link WebSocketHandler} to handle it. + * + *
As of 5.3 the WebFlux Java configuration, imported via + * {@code @EnableWebFlux}, includes a declaration of this adapter and therefore + * it no longer needs to be present in application configuration. * * @author Rossen Stoyanchev * @since 5.0 */ -public class WebSocketHandlerAdapter implements HandlerAdapter { +public class WebSocketHandlerAdapter implements HandlerAdapter, Ordered { private final WebSocketService webSocketService; + private int order = 2; + /** * Default constructor that creates and uses a @@ -56,6 +68,25 @@ public class WebSocketHandlerAdapter implements HandlerAdapter { } + /** + * Set the order value for this adapter. + *
By default this is set to 2.
+ * @param order the value to set to
+ * @since 5.3
+ */
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ /**
+ * Return the {@link #setOrder(int) configured} order for this instance.
+ * @since 5.3
+ */
+ @Override
+ public int getOrder() {
+ return this.order;
+ }
+
/**
* Return the configured {@code WebSocketService} to handle requests.
*/
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
index d5515616b30..1d592013ed0 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
@@ -37,11 +37,14 @@ import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
+import org.springframework.web.reactive.socket.server.WebSocketService;
+import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
@@ -73,6 +76,7 @@ public class DelegatingWebFluxConfigurationTests {
delegatingConfig.setApplicationContext(new StaticApplicationContext());
given(webFluxConfigurer.getValidator()).willReturn(null);
given(webFluxConfigurer.getMessageCodesResolver()).willReturn(null);
+ given(webFluxConfigurer.getWebSocketService()).willReturn(null);
}
@@ -125,6 +129,17 @@ public class DelegatingWebFluxConfigurationTests {
verify(webFluxConfigurer).configurePathMatching(any(PathMatchConfigurer.class));
}
+ @Test
+ void webSocketService() {
+ WebSocketService service = mock(WebSocketService.class);
+ given(webFluxConfigurer.getWebSocketService()).willReturn(service);
+
+ delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer));
+ WebSocketHandlerAdapter adapter = delegatingConfig.webFluxWebSocketHandlerAdapter();
+
+ assertThat(adapter.getWebSocketService()).isSameAs(service);
+ }
+
@Test
public void responseBodyResultHandler() {
delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer));
diff --git a/src/docs/asciidoc/web/webflux-websocket.adoc b/src/docs/asciidoc/web/webflux-websocket.adoc
index 4ad5281f8fa..033751bed2f 100644
--- a/src/docs/asciidoc/web/webflux-websocket.adoc
+++ b/src/docs/asciidoc/web/webflux-websocket.adoc
@@ -54,7 +54,7 @@ The following example shows how to do so:
}
----
-Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the following example shows:
+Then you can map it to a URL:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
@@ -70,11 +70,6 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi
return new SimpleUrlHandlerMapping(map, order);
}
-
- @Bean
- public WebSocketHandlerAdapter handlerAdapter() {
- return new WebSocketHandlerAdapter();
- }
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -90,6 +85,34 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi
return SimpleUrlHandlerMapping(map, order)
}
+ }
+----
+
+If using the <