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:
committed by
Rossen Stoyanchev
parent
8f1fefc159
commit
10f5d96a78
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
Reference in New Issue
Block a user