Allow plugging in a WebSocketHandlerDecorator
The WebSocketMessageBroker config now allows wrapping the SubProtocolWebSocketHandler to enable advanced use cases that may require access to the underlying WebSocketSession. Issue: SPR-12314
This commit is contained in:
@@ -62,7 +62,10 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TestWebSocketSession;
|
||||
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
|
||||
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
|
||||
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
|
||||
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
|
||||
import org.springframework.web.socket.server.HandshakeHandler;
|
||||
@@ -93,7 +96,7 @@ public class MessageBrokerBeanDefinitionParserTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void simpleBroker() {
|
||||
public void simpleBroker() throws Exception {
|
||||
loadBeanDefinitions("websocket-config-broker-simple.xml");
|
||||
|
||||
HandlerMapping hm = this.appContext.getBean(HandlerMapping.class);
|
||||
@@ -113,6 +116,10 @@ public class MessageBrokerBeanDefinitionParserTests {
|
||||
List<HandshakeInterceptor> interceptors = wsHttpRequestHandler.getHandshakeInterceptors();
|
||||
assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class), instanceOf(BarTestInterceptor.class)));
|
||||
|
||||
WebSocketSession session = new TestWebSocketSession("id");
|
||||
wsHttpRequestHandler.getWebSocketHandler().afterConnectionEstablished(session);
|
||||
assertEquals(true, session.getAttributes().get("decorated"));
|
||||
|
||||
WebSocketHandler wsHandler = unwrapWebSocketHandler(wsHttpRequestHandler.getWebSocketHandler());
|
||||
assertNotNull(wsHandler);
|
||||
assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
|
||||
@@ -429,7 +436,6 @@ class CustomArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CustomReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
@@ -443,4 +449,18 @@ class CustomReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TestWebSocketHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {
|
||||
|
||||
@Override
|
||||
public WebSocketHandler decorate(WebSocketHandler handler) {
|
||||
return new WebSocketHandlerDecorator(handler) {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
session.getAttributes().put("decorated", true);
|
||||
super.afterConnectionEstablished(session);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -43,13 +43,17 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.config.WebSocketMessageBrokerStats;
|
||||
import org.springframework.web.socket.handler.TestWebSocketSession;
|
||||
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
|
||||
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
|
||||
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
|
||||
import org.springframework.web.socket.messaging.StompTextMessageBuilder;
|
||||
import org.springframework.web.socket.messaging.SubProtocolHandler;
|
||||
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
|
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -62,19 +66,11 @@ import static org.junit.Assert.assertEquals;
|
||||
*/
|
||||
public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
private AnnotationConfigApplicationContext config;
|
||||
|
||||
|
||||
@Before
|
||||
public void setupOnce() {
|
||||
this.config = new AnnotationConfigApplicationContext();
|
||||
this.config.register(TestWebSocketMessageBrokerConfiguration.class, TestSimpleMessageBrokerConfig.class);
|
||||
this.config.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlerMapping() {
|
||||
SimpleUrlHandlerMapping hm = (SimpleUrlHandlerMapping) this.config.getBean(HandlerMapping.class);
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
SimpleUrlHandlerMapping hm = (SimpleUrlHandlerMapping) config.getBean(HandlerMapping.class);
|
||||
assertEquals(1, hm.getOrder());
|
||||
|
||||
Map<String, Object> handlerMap = hm.getHandlerMap();
|
||||
@@ -84,8 +80,9 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
@Test
|
||||
public void clientInboundChannelSendMessage() throws Exception {
|
||||
TestChannel channel = this.config.getBean("clientInboundChannel", TestChannel.class);
|
||||
SubProtocolWebSocketHandler webSocketHandler = this.config.getBean(SubProtocolWebSocketHandler.class);
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
TestChannel channel = config.getBean("clientInboundChannel", TestChannel.class);
|
||||
SubProtocolWebSocketHandler webSocketHandler = config.getBean(SubProtocolWebSocketHandler.class);
|
||||
|
||||
WebSocketSession session = new TestWebSocketSession("s1");
|
||||
webSocketHandler.afterConnectionEstablished(session);
|
||||
@@ -102,7 +99,8 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
@Test
|
||||
public void clientOutboundChannelChannel() {
|
||||
TestChannel channel = this.config.getBean("clientOutboundChannel", TestChannel.class);
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
TestChannel channel = config.getBean("clientOutboundChannel", TestChannel.class);
|
||||
Set<MessageHandler> handlers = channel.getSubscribers();
|
||||
|
||||
assertEquals(1, handlers.size());
|
||||
@@ -111,8 +109,9 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
@Test
|
||||
public void webSocketTransportOptions() {
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
SubProtocolWebSocketHandler subProtocolWebSocketHandler =
|
||||
this.config.getBean("subProtocolWebSocketHandler", SubProtocolWebSocketHandler.class);
|
||||
config.getBean("subProtocolWebSocketHandler", SubProtocolWebSocketHandler.class);
|
||||
|
||||
assertEquals(1024 * 1024, subProtocolWebSocketHandler.getSendBufferSizeLimit());
|
||||
assertEquals(25 * 1000, subProtocolWebSocketHandler.getSendTimeLimit());
|
||||
@@ -126,8 +125,9 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
@Test
|
||||
public void messageBrokerSockJsTaskScheduler() {
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
ThreadPoolTaskScheduler taskScheduler =
|
||||
this.config.getBean("messageBrokerSockJsTaskScheduler", ThreadPoolTaskScheduler.class);
|
||||
config.getBean("messageBrokerSockJsTaskScheduler", ThreadPoolTaskScheduler.class);
|
||||
|
||||
ScheduledThreadPoolExecutor executor = taskScheduler.getScheduledThreadPoolExecutor();
|
||||
assertEquals(Runtime.getRuntime().availableProcessors(), executor.getCorePoolSize());
|
||||
@@ -136,8 +136,9 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
|
||||
@Test
|
||||
public void webSocketMessageBrokerStats() {
|
||||
ApplicationContext config = createConfig(TestChannelConfig.class, TestConfigurer.class);
|
||||
String name = "webSocketMessageBrokerStats";
|
||||
WebSocketMessageBrokerStats stats = this.config.getBean(name, WebSocketMessageBrokerStats.class);
|
||||
WebSocketMessageBrokerStats stats = config.getBean(name, WebSocketMessageBrokerStats.class);
|
||||
String actual = stats.toString();
|
||||
String expected = "WebSocketSession\\[0 current WS\\(0\\)-HttpStream\\(0\\)-HttpPoll\\(0\\), " +
|
||||
"0 total, 0 closed abnormally \\(0 connect failure, 0 send limit, 0 transport error\\)\\], " +
|
||||
@@ -150,6 +151,29 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
assertTrue("\nExpected: " + expected.replace("\\", "") + "\n Actual: " + actual, actual.matches(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webSocketHandlerDecorator() throws Exception {
|
||||
ApplicationContext config = createConfig(WebSocketHandlerDecoratorConfig.class);
|
||||
WebSocketHandler handler = config.getBean(SubProtocolWebSocketHandler.class);
|
||||
assertNotNull(handler);
|
||||
|
||||
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) config.getBean("stompWebSocketHandlerMapping");
|
||||
WebSocketHttpRequestHandler httpHandler = (WebSocketHttpRequestHandler) mapping.getHandlerMap().get("/test");
|
||||
handler = httpHandler.getWebSocketHandler();
|
||||
|
||||
WebSocketSession session = new TestWebSocketSession("id");
|
||||
handler.afterConnectionEstablished(session);
|
||||
assertEquals(true, session.getAttributes().get("decorated"));
|
||||
}
|
||||
|
||||
|
||||
private ApplicationContext createConfig(Class<?>... configClasses) {
|
||||
AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext();
|
||||
config.register(configClasses);
|
||||
config.refresh();
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
static class TestController {
|
||||
@@ -167,7 +191,7 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestSimpleMessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
static class TestConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Bean
|
||||
public TestController subscriptionController() {
|
||||
@@ -188,7 +212,7 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestWebSocketMessageBrokerConfiguration extends DelegatingWebSocketMessageBrokerConfiguration {
|
||||
static class TestChannelConfig extends DelegatingWebSocketMessageBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
@@ -208,6 +232,31 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WebSocketHandlerDecoratorConfig extends WebSocketMessageBrokerConfigurationSupport {
|
||||
|
||||
@Override
|
||||
protected void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/test");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureWebSocketTransport(WebSocketTransportRegistration registry) {
|
||||
registry.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
|
||||
@Override
|
||||
public WebSocketHandlerDecorator decorate(WebSocketHandler handler) {
|
||||
return new WebSocketHandlerDecorator(handler) {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
session.getAttributes().put("decorated", true);
|
||||
super.afterConnectionEstablished(session);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestChannel extends ExecutorSubscribableChannel {
|
||||
|
||||
private final List<Message<?>> messages = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user