Add XML namespace for WebSocket config

This commit adds an XML namespace equivalent of @EnableWebSocket and
@EnableWebSocketMessageBroker. Those are <websocket:handlers> and
<websocket:message-broker> respectively.

Examples can be found in the test suite.

This commit also alters the way MessageHandler's subscribe to their
respective MessageChannel's of interest. Rather than performing the
subscriptions in configuration code, the message channels are now
passed into MessageHandler's so they can subscribe themselves on
startup.

Issue: SPR-11063
This commit is contained in:
Brian Clozel
2013-11-26 20:04:57 +01:00
committed by Rossen Stoyanchev
parent 8f1fefc159
commit 10f5d96a78
44 changed files with 2434 additions and 171 deletions

View File

@@ -23,6 +23,7 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.web.socket.support.TestWebSocketSession;
import static org.mockito.Mockito.*;
@@ -45,14 +46,17 @@ public class SubProtocolWebSocketHandlerTests {
@Mock SubProtocolHandler defaultHandler;
@Mock MessageChannel channel;
@Mock MessageChannel inClientChannel;
@Mock
SubscribableChannel outClientChannel;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.webSocketHandler = new SubProtocolWebSocketHandler(this.channel);
this.webSocketHandler = new SubProtocolWebSocketHandler(this.inClientChannel, this.outClientChannel);
when(stompHandler.getSupportedProtocols()).thenReturn(Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp"));
when(mqttHandler.getSupportedProtocols()).thenReturn(Arrays.asList("MQTT"));
@@ -67,8 +71,8 @@ public class SubProtocolWebSocketHandlerTests {
this.session.setAcceptedProtocol("v12.sToMp");
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(session, this.channel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.channel);
verify(this.stompHandler).afterSessionStarted(session, this.inClientChannel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
@@ -77,7 +81,7 @@ public class SubProtocolWebSocketHandlerTests {
this.session.setAcceptedProtocol("v12.sToMp");
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(session, this.channel);
verify(this.stompHandler).afterSessionStarted(session, this.inClientChannel);
}
@Test(expected=IllegalStateException.class)
@@ -94,9 +98,9 @@ public class SubProtocolWebSocketHandlerTests {
this.webSocketHandler.setDefaultProtocolHandler(defaultHandler);
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.defaultHandler).afterSessionStarted(session, this.channel);
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.channel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.channel);
verify(this.defaultHandler).afterSessionStarted(session, this.inClientChannel);
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
@@ -105,9 +109,9 @@ public class SubProtocolWebSocketHandlerTests {
this.webSocketHandler.setDefaultProtocolHandler(defaultHandler);
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.defaultHandler).afterSessionStarted(session, this.channel);
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.channel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.channel);
verify(this.defaultHandler).afterSessionStarted(session, this.inClientChannel);
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
@@ -115,7 +119,7 @@ public class SubProtocolWebSocketHandlerTests {
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler));
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(session, this.channel);
verify(this.stompHandler).afterSessionStarted(session, this.inClientChannel);
}
@Test(expected=IllegalStateException.class)

View File

@@ -51,7 +51,8 @@ public class WebMvcStompEndpointRegistrationTests {
@Before
public void setup() {
this.wsHandler = new SubProtocolWebSocketHandler(new ExecutorSubscribableChannel());
this.wsHandler = new SubProtocolWebSocketHandler(
new ExecutorSubscribableChannel(), new ExecutorSubscribableChannel());
this.scheduler = Mockito.mock(TaskScheduler.class);
}

View File

@@ -22,6 +22,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.simp.handler.DefaultUserSessionRegistry;
import org.springframework.messaging.simp.handler.UserSessionRegistry;
import org.springframework.scheduling.TaskScheduler;
@@ -49,8 +50,9 @@ public class WebMvcStompEndpointRegistryTests {
@Before
public void setup() {
MessageChannel channel = Mockito.mock(MessageChannel.class);
this.webSocketHandler = new SubProtocolWebSocketHandler(channel);
MessageChannel inChannel = Mockito.mock(MessageChannel.class);
SubscribableChannel outChannel = Mockito.mock(SubscribableChannel.class);
this.webSocketHandler = new SubProtocolWebSocketHandler(inChannel, outChannel);
this.userSessionRegistry = new DefaultUserSessionRegistry();
TaskScheduler taskScheduler = Mockito.mock(TaskScheduler.class);
this.registry = new WebMvcStompEndpointRegistry(webSocketHandler, userSessionRegistry, taskScheduler);
@@ -62,7 +64,7 @@ public class WebMvcStompEndpointRegistryTests {
this.registry.addEndpoint("/stomp");
Map<String, SubProtocolHandler> protocolHandlers = webSocketHandler.getProtocolHandlers();
Map<String, SubProtocolHandler> protocolHandlers = webSocketHandler.getProtocolHandlerMap();
assertEquals(3, protocolHandlers.size());
assertNotNull(protocolHandlers.get("v10.stomp"));
assertNotNull(protocolHandlers.get("v11.stomp"));

View File

@@ -0,0 +1,267 @@
/*
* Copyright 2002-2013 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.web.socket.messaging.config.xml;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.handler.*;
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
import org.springframework.messaging.support.channel.AbstractSubscribableChannel;
import org.springframework.messaging.support.converter.CompositeMessageConverter;
import org.springframework.messaging.support.converter.MessageConverter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.HttpRequestHandler;
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.messaging.SubProtocolWebSocketHandler;
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler;
import org.springframework.web.socket.support.WebSocketHandlerDecorator;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
/**
* Test fixture for the configuration in websocket-config-broker*.xml test files.
* @author Brian Clozel
*/
public class MessageBrokerBeanDefinitionParserTests {
private GenericWebApplicationContext appContext;
@Before
public void setup() {
this.appContext = new GenericWebApplicationContext();
}
@Test
public void simpleBroker() {
loadBeanDefinitions("websocket-config-broker-simple.xml");
HandlerMapping hm = this.appContext.getBean(HandlerMapping.class);
assertNotNull(hm);
assertThat(hm, Matchers.instanceOf(SimpleUrlHandlerMapping.class));
SimpleUrlHandlerMapping suhm = (SimpleUrlHandlerMapping) hm;
assertThat(suhm.getUrlMap().keySet(), Matchers.hasSize(4));
assertThat(suhm.getUrlMap().values(), Matchers.hasSize(4));
HttpRequestHandler httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/foo");
assertNotNull(httpRequestHandler);
assertThat(httpRequestHandler, Matchers.instanceOf(WebSocketHttpRequestHandler.class));
WebSocketHttpRequestHandler wsHttpRequestHandler = (WebSocketHttpRequestHandler) httpRequestHandler;
WebSocketHandler wsHandler = unwrapWebSocketHandler(wsHttpRequestHandler.getWebSocketHandler());
assertNotNull(wsHandler);
assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
SubProtocolWebSocketHandler subProtocolWsHandler = (SubProtocolWebSocketHandler) wsHandler;
assertEquals(Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp"), subProtocolWsHandler.getSubProtocols());
httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/test/**");
assertNotNull(httpRequestHandler);
assertThat(httpRequestHandler, Matchers.instanceOf(SockJsHttpRequestHandler.class));
SockJsHttpRequestHandler sockJsHttpRequestHandler = (SockJsHttpRequestHandler) httpRequestHandler;
wsHandler = unwrapWebSocketHandler(sockJsHttpRequestHandler.getWebSocketHandler());
assertNotNull(wsHandler);
assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
assertNotNull(sockJsHttpRequestHandler.getSockJsService());
UserDestinationResolver userDestResolver = this.appContext.getBean(UserDestinationResolver.class);
assertNotNull(userDestResolver);
assertThat(userDestResolver, Matchers.instanceOf(DefaultUserDestinationResolver.class));
DefaultUserDestinationResolver defaultUserDestResolver = (DefaultUserDestinationResolver) userDestResolver;
assertEquals("/personal/", defaultUserDestResolver.getDestinationPrefix());
List<Class<? extends MessageHandler>> subscriberTypes =
Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);
testChannel("clientInboundChannel", subscriberTypes, 0);
testExecutor("clientInboundChannel", 1, Integer.MAX_VALUE, 60);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
testChannel("clientOutboundChannel", subscriberTypes, 0);
testExecutor("clientOutboundChannel", 1, Integer.MAX_VALUE, 60);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(
SimpleBrokerMessageHandler.class, UserDestinationMessageHandler.class);
testChannel("brokerChannel", subscriberTypes, 0);
try {
this.appContext.getBean("brokerChannelExecutor", ThreadPoolTaskExecutor.class);
fail("expected exception");
}
catch (NoSuchBeanDefinitionException ex) {
// expected
}
}
@Test
public void stompBrokerRelay() {
loadBeanDefinitions("websocket-config-broker-relay.xml");
HandlerMapping hm = this.appContext.getBean(HandlerMapping.class);
assertNotNull(hm);
assertThat(hm, Matchers.instanceOf(SimpleUrlHandlerMapping.class));
SimpleUrlHandlerMapping suhm = (SimpleUrlHandlerMapping) hm;
assertThat(suhm.getUrlMap().keySet(), Matchers.hasSize(1));
assertThat(suhm.getUrlMap().values(), Matchers.hasSize(1));
assertEquals(2, suhm.getOrder());
HttpRequestHandler httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/foo/**");
assertNotNull(httpRequestHandler);
assertThat(httpRequestHandler, Matchers.instanceOf(SockJsHttpRequestHandler.class));
SockJsHttpRequestHandler sockJsHttpRequestHandler = (SockJsHttpRequestHandler) httpRequestHandler;
WebSocketHandler wsHandler = unwrapWebSocketHandler(sockJsHttpRequestHandler.getWebSocketHandler());
assertNotNull(wsHandler);
assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
assertNotNull(sockJsHttpRequestHandler.getSockJsService());
UserDestinationResolver userDestResolver = this.appContext.getBean(UserDestinationResolver.class);
assertNotNull(userDestResolver);
assertThat(userDestResolver, Matchers.instanceOf(DefaultUserDestinationResolver.class));
DefaultUserDestinationResolver defaultUserDestResolver = (DefaultUserDestinationResolver) userDestResolver;
assertEquals("/user/", defaultUserDestResolver.getDestinationPrefix());
StompBrokerRelayMessageHandler messageBroker = this.appContext.getBean(StompBrokerRelayMessageHandler.class);
assertNotNull(messageBroker);
assertEquals("login", messageBroker.getSystemLogin());
assertEquals("pass", messageBroker.getSystemPasscode());
assertEquals("relayhost", messageBroker.getRelayHost());
assertEquals(1234, messageBroker.getRelayPort());
assertEquals("spring.io", messageBroker.getVirtualHost());
assertEquals(5000, messageBroker.getSystemHeartbeatReceiveInterval());
assertEquals(5000, messageBroker.getSystemHeartbeatSendInterval());
assertThat(messageBroker.getDestinationPrefixes(), Matchers.containsInAnyOrder("/topic","/queue"));
List<Class<? extends MessageHandler>> subscriberTypes =
Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
UserDestinationMessageHandler.class, StompBrokerRelayMessageHandler.class);
testChannel("clientInboundChannel", subscriberTypes, 0);
testExecutor("clientInboundChannel", 1, Integer.MAX_VALUE, 60);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
testChannel("clientOutboundChannel", subscriberTypes, 0);
testExecutor("clientOutboundChannel", 1, Integer.MAX_VALUE, 60);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(
StompBrokerRelayMessageHandler.class, UserDestinationMessageHandler.class);
testChannel("brokerChannel", subscriberTypes, 0);
try {
this.appContext.getBean("brokerChannelExecutor", ThreadPoolTaskExecutor.class);
fail("expected exception");
}
catch (NoSuchBeanDefinitionException ex) {
// expected
}
}
@Test
public void annotationMethodMessageHandler() {
loadBeanDefinitions("websocket-config-broker-simple.xml");
SimpAnnotationMethodMessageHandler annotationMethodMessageHandler =
this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);
assertNotNull(annotationMethodMessageHandler);
MessageConverter messageConverter = annotationMethodMessageHandler.getMessageConverter();
assertNotNull(messageConverter);
assertTrue(messageConverter instanceof CompositeMessageConverter);
CompositeMessageConverter compositeMessageConverter = this.appContext.getBean(CompositeMessageConverter.class);
assertNotNull(compositeMessageConverter);
SimpMessagingTemplate simpMessagingTemplate = this.appContext.getBean(SimpMessagingTemplate.class);
assertNotNull(simpMessagingTemplate);
assertEquals("/personal", simpMessagingTemplate.getUserDestinationPrefix());
}
@Test
public void customChannels() {
loadBeanDefinitions("websocket-config-broker-customchannels.xml");
List<Class<? extends MessageHandler>> subscriberTypes =
Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);
testChannel("clientInboundChannel", subscriberTypes, 1);
testExecutor("clientInboundChannel", 100, 200, 600);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
testChannel("clientOutboundChannel", subscriberTypes, 2);
testExecutor("clientOutboundChannel", 101, 201, 601);
subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SimpleBrokerMessageHandler.class,
UserDestinationMessageHandler.class);
testChannel("brokerChannel", subscriberTypes, 0);
testExecutor("brokerChannel", 102, 202, 602);
}
private void testChannel(String channelName, List<Class<? extends MessageHandler>> subscriberTypes,
int interceptorCount) {
AbstractSubscribableChannel channel = this.appContext.getBean(channelName, AbstractSubscribableChannel.class);
for (Class<? extends MessageHandler> subscriberType : subscriberTypes) {
MessageHandler subscriber = this.appContext.getBean(subscriberType);
assertNotNull("No subsription for " + subscriberType, subscriber);
assertTrue(channel.hasSubscription(subscriber));
}
assertEquals(interceptorCount, channel.getInterceptors().size());
}
private void testExecutor(String channelName, int corePoolSize, int maxPoolSize, int keepAliveSeconds) {
ThreadPoolTaskExecutor taskExecutor =
this.appContext.getBean(channelName + "Executor", ThreadPoolTaskExecutor.class);
assertEquals(corePoolSize, taskExecutor.getCorePoolSize());
assertEquals(maxPoolSize, taskExecutor.getMaxPoolSize());
assertEquals(keepAliveSeconds, taskExecutor.getKeepAliveSeconds());
}
private void loadBeanDefinitions(String fileName) {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.appContext);
ClassPathResource resource = new ClassPathResource(fileName, MessageBrokerBeanDefinitionParserTests.class);
reader.loadBeanDefinitions(resource);
this.appContext.refresh();
}
private WebSocketHandler unwrapWebSocketHandler(WebSocketHandler handler) {
return (handler instanceof WebSocketHandlerDecorator) ?
((WebSocketHandlerDecorator) handler).getLastHandler() : handler;
}
}

View File

@@ -0,0 +1,291 @@
/*
* Copyright 2002-2013 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.web.socket.server.config.xml;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
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.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.server.DefaultHandshakeHandler;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
import org.springframework.web.socket.sockjs.transport.handler.EventSourceTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.HtmlFileTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.JsonpPollingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.JsonpReceivingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrPollingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrReceivingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrStreamingTransportHandler;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Test fixture for HandlersBeanDefinitionParser.
* See test configuration files websocket-config-handlers*.xml.
*
* @author Brian Clozel
*/
public class HandlersBeanDefinitionParserTests {
private GenericWebApplicationContext appContext;
@Before
public void setup() {
appContext = new GenericWebApplicationContext();
}
@Test
public void webSocketHandlers() {
loadBeanDefinitions("websocket-config-handlers.xml");
Map<String, HandlerMapping> handlersMap = appContext.getBeansOfType(HandlerMapping.class);
assertNotNull(handlersMap);
assertThat(handlersMap.values(), Matchers.hasSize(2));
for(HandlerMapping handlerMapping : handlersMap.values()) {
assertTrue(handlerMapping instanceof SimpleUrlHandlerMapping);
SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
if(urlHandlerMapping.getUrlMap().keySet().contains("/foo")) {
assertThat(urlHandlerMapping.getUrlMap().keySet(),Matchers.contains("/foo","/bar"));
WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler)
urlHandlerMapping.getUrlMap().get("/foo");
assertNotNull(handler);
checkDelegateHandlerType(handler.getWebSocketHandler(), FooWebSocketHandler.class);
HandshakeHandler handshakeHandler = (HandshakeHandler)
new DirectFieldAccessor(handler).getPropertyValue("handshakeHandler");
assertNotNull(handshakeHandler);
assertTrue(handshakeHandler instanceof DefaultHandshakeHandler);
}
else {
assertThat(urlHandlerMapping.getUrlMap().keySet(),Matchers.contains("/test"));
WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler)
urlHandlerMapping.getUrlMap().get("/test");
assertNotNull(handler);
checkDelegateHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
HandshakeHandler handshakeHandler = (HandshakeHandler)
new DirectFieldAccessor(handler).getPropertyValue("handshakeHandler");
assertNotNull(handshakeHandler);
assertTrue(handshakeHandler instanceof DefaultHandshakeHandler);
}
}
}
@Test
public void websocketHandlersAttributes() {
loadBeanDefinitions("websocket-config-handlers-attributes.xml");
HandlerMapping handlerMapping = appContext.getBean(HandlerMapping.class);
assertNotNull(handlerMapping);
assertTrue(handlerMapping instanceof SimpleUrlHandlerMapping);
SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
assertEquals(2, urlHandlerMapping.getOrder());
WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler) urlHandlerMapping.getUrlMap().get("/foo");
assertNotNull(handler);
checkDelegateHandlerType(handler.getWebSocketHandler(), FooWebSocketHandler.class);
HandshakeHandler handshakeHandler = (HandshakeHandler)
new DirectFieldAccessor(handler).getPropertyValue("handshakeHandler");
assertNotNull(handshakeHandler);
assertTrue(handshakeHandler instanceof TestHandshakeHandler);
List<HandshakeInterceptor> handshakeInterceptorList = (List<HandshakeInterceptor>)
new DirectFieldAccessor(handler).getPropertyValue("interceptors");
assertNotNull(handshakeInterceptorList);
assertThat(handshakeInterceptorList, Matchers.contains(
Matchers.instanceOf(FooTestInterceptor.class), Matchers.instanceOf(BarTestInterceptor.class)));
handler = (WebSocketHttpRequestHandler) urlHandlerMapping.getUrlMap().get("/test");
assertNotNull(handler);
checkDelegateHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
handshakeHandler = (HandshakeHandler) new DirectFieldAccessor(handler).getPropertyValue("handshakeHandler");
assertNotNull(handshakeHandler);
assertTrue(handshakeHandler instanceof TestHandshakeHandler);
handshakeInterceptorList = (List<HandshakeInterceptor>)
new DirectFieldAccessor(handler).getPropertyValue("interceptors");
assertNotNull(handshakeInterceptorList);
assertThat(handshakeInterceptorList, Matchers.contains(
Matchers.instanceOf(FooTestInterceptor.class), Matchers.instanceOf(BarTestInterceptor.class)));
}
@Test
public void sockJSSupport() {
loadBeanDefinitions("websocket-config-handlers-sockjs.xml");
SimpleUrlHandlerMapping handlerMapping = appContext.getBean(SimpleUrlHandlerMapping.class);
assertNotNull(handlerMapping);
SockJsHttpRequestHandler testHandler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/test/**");
assertNotNull(testHandler);
checkDelegateHandlerType(testHandler.getWebSocketHandler(), TestWebSocketHandler.class);
SockJsService testSockJsService = testHandler.getSockJsService();
SockJsHttpRequestHandler fooHandler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/foo/**");
assertNotNull(fooHandler);
checkDelegateHandlerType(fooHandler.getWebSocketHandler(), FooWebSocketHandler.class);
SockJsService sockJsService = fooHandler.getSockJsService();
assertNotNull(sockJsService);
assertEquals(testSockJsService, sockJsService);
assertThat(sockJsService, Matchers.instanceOf(DefaultSockJsService.class));
DefaultSockJsService defaultSockJsService = (DefaultSockJsService) sockJsService;
assertThat(defaultSockJsService.getTaskScheduler(), Matchers.instanceOf(ThreadPoolTaskScheduler.class));
assertThat(defaultSockJsService.getTransportHandlers().values(), Matchers.containsInAnyOrder(
Matchers.instanceOf(XhrPollingTransportHandler.class),
Matchers.instanceOf(XhrReceivingTransportHandler.class),
Matchers.instanceOf(JsonpPollingTransportHandler.class),
Matchers.instanceOf(JsonpReceivingTransportHandler.class),
Matchers.instanceOf(XhrStreamingTransportHandler.class),
Matchers.instanceOf(EventSourceTransportHandler.class),
Matchers.instanceOf(HtmlFileTransportHandler.class),
Matchers.instanceOf(WebSocketTransportHandler.class)));
}
@Test
public void sockJSAttributesSupport() {
loadBeanDefinitions("websocket-config-handlers-sockjs-attributes.xml");
SimpleUrlHandlerMapping handlerMapping = appContext.getBean(SimpleUrlHandlerMapping.class);
assertNotNull(handlerMapping);
SockJsHttpRequestHandler handler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/test/**");
assertNotNull(handler);
checkDelegateHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
SockJsService sockJsService = handler.getSockJsService();
assertNotNull(sockJsService);
assertThat(sockJsService, Matchers.instanceOf(DefaultSockJsService.class));
DefaultSockJsService defaultSockJsService = (DefaultSockJsService) sockJsService;
assertThat(defaultSockJsService.getTaskScheduler(), Matchers.instanceOf(TestTaskScheduler.class));
assertThat(defaultSockJsService.getTransportHandlers().values(), Matchers.containsInAnyOrder(
Matchers.instanceOf(XhrPollingTransportHandler.class),
Matchers.instanceOf(XhrStreamingTransportHandler.class)));
assertEquals("testSockJsService", defaultSockJsService.getName());
assertFalse(defaultSockJsService.isWebSocketEnabled());
assertFalse(defaultSockJsService.isSessionCookieNeeded());
assertEquals(2048, defaultSockJsService.getStreamBytesLimit());
assertEquals(256, defaultSockJsService.getDisconnectDelay());
assertEquals(1024, defaultSockJsService.getHttpMessageCacheSize());
assertEquals(20, defaultSockJsService.getHeartbeatTime());
}
private void loadBeanDefinitions(String fileName) {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext);
ClassPathResource resource = new ClassPathResource(fileName, HandlersBeanDefinitionParserTests.class);
reader.loadBeanDefinitions(resource);
appContext.refresh();
}
private void checkDelegateHandlerType(WebSocketHandler handler, Class<?> handlerClass) {
do {
handler = (WebSocketHandler) new DirectFieldAccessor(handler).getPropertyValue("delegate");
}
while (new DirectFieldAccessor(handler).isReadableProperty("delegate"));
assertTrue(handlerClass.isInstance(handler));
}
}
class TestWebSocketHandler implements WebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {}
@Override
public boolean supportsPartialMessages() { return false; }
}
class FooWebSocketHandler extends TestWebSocketHandler { }
class TestHandshakeHandler implements HandshakeHandler {
@Override
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes)
throws HandshakeFailureException {
return false;
}
}
class FooTestInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes)
throws Exception {
return false;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
}
class BarTestInterceptor extends FooTestInterceptor {}
class TestTaskScheduler implements TaskScheduler {
@Override
public ScheduledFuture schedule(Runnable task, Trigger trigger) { return null; }
@Override
public ScheduledFuture schedule(Runnable task, Date startTime) { return null; }
@Override
public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { return null; }
@Override
public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { return null; }
@Override
public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { return null; }
@Override
public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { return null; }
}