Browse Source
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-11063pull/423/head
44 changed files with 2434 additions and 171 deletions
@ -0,0 +1,436 @@
@@ -0,0 +1,436 @@
|
||||
/* |
||||
* 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.springframework.beans.MutablePropertyValues; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues; |
||||
import org.springframework.beans.factory.config.RuntimeBeanReference; |
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition; |
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition; |
||||
import org.springframework.beans.factory.support.ManagedList; |
||||
import org.springframework.beans.factory.support.ManagedMap; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser; |
||||
import org.springframework.beans.factory.xml.ParserContext; |
||||
import org.springframework.messaging.simp.SimpMessagingTemplate; |
||||
import org.springframework.messaging.simp.handler.DefaultUserDestinationResolver; |
||||
import org.springframework.messaging.simp.handler.DefaultUserSessionRegistry; |
||||
import org.springframework.messaging.simp.handler.SimpAnnotationMethodMessageHandler; |
||||
import org.springframework.messaging.simp.handler.SimpleBrokerMessageHandler; |
||||
import org.springframework.messaging.simp.handler.UserDestinationMessageHandler; |
||||
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler; |
||||
import org.springframework.messaging.support.channel.ExecutorSubscribableChannel; |
||||
import org.springframework.messaging.support.converter.ByteArrayMessageConverter; |
||||
import org.springframework.messaging.support.converter.CompositeMessageConverter; |
||||
import org.springframework.messaging.support.converter.DefaultContentTypeResolver; |
||||
import org.springframework.messaging.support.converter.MappingJackson2MessageConverter; |
||||
import org.springframework.messaging.support.converter.StringMessageConverter; |
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.MimeTypeUtils; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.util.xml.DomUtils; |
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; |
||||
import org.springframework.web.socket.messaging.StompSubProtocolHandler; |
||||
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler; |
||||
import org.springframework.web.socket.server.config.xml.WebSocketNamespaceUtils; |
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler; |
||||
import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler; |
||||
import org.w3c.dom.Element; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* A {@link org.springframework.beans.factory.xml.BeanDefinitionParser} |
||||
* that provides the configuration for the |
||||
* {@code <websocket:message-broker/>} XML namespace element. |
||||
* <p> |
||||
* Registers a Spring MVC {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping} |
||||
* with order=1 to map HTTP WebSocket handshake requests from STOMP/WebSocket clients. |
||||
* <p> |
||||
* Registers the following {@link org.springframework.messaging.MessageChannel}s: |
||||
* <ul> |
||||
* <li>"clientInboundChannel" for receiving messages from clients (e.g. WebSocket clients) |
||||
* <li>"clientOutboundChannel" for sending messages to clients (e.g. WebSocket clients) |
||||
* <li>"brokerChannel" for sending messages from within the application to the message broker |
||||
* </ul> |
||||
* <p> |
||||
* Registers one of the following based on the selected message broker options: |
||||
* <ul> |
||||
* <li> a {@link SimpleBrokerMessageHandler} if the <simple-broker/> is used |
||||
* <li> a {@link StompBrokerRelayMessageHandler} if the <stomp-broker-relay/> is used |
||||
* </ul> |
||||
* <p> |
||||
* Registers a {@link UserDestinationMessageHandler} for handling user destinations. |
||||
* |
||||
* @author Brian Clozel |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser { |
||||
|
||||
protected static final String SOCKJS_SCHEDULER_BEAN_NAME = "messageBrokerSockJsScheduler"; |
||||
|
||||
private static final int DEFAULT_MAPPING_ORDER = 1; |
||||
|
||||
private static final boolean jackson2Present= ClassUtils.isPresent( |
||||
"com.fasterxml.jackson.databind.ObjectMapper", MessageBrokerBeanDefinitionParser.class.getClassLoader()); |
||||
|
||||
|
||||
@Override |
||||
public BeanDefinition parse(Element element, ParserContext parserCxt) { |
||||
|
||||
Object source = parserCxt.extractSource(element); |
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); |
||||
parserCxt.pushContainingComponent(compDefinition); |
||||
|
||||
String orderAttribute = element.getAttribute("order"); |
||||
int order = orderAttribute.isEmpty() ? DEFAULT_MAPPING_ORDER : Integer.valueOf(orderAttribute); |
||||
|
||||
ManagedMap<String, Object> urlMap = new ManagedMap<String, Object>(); |
||||
urlMap.setSource(source); |
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class); |
||||
handlerMappingDef.getPropertyValues().add("order", order); |
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap); |
||||
|
||||
String channelName = "clientInboundChannel"; |
||||
Element channelElem = DomUtils.getChildElementByTagName(element, "client-inbound-channel"); |
||||
RuntimeBeanReference clientInChannel = getMessageChannel(channelName, channelElem, parserCxt, source); |
||||
|
||||
channelName = "clientOutboundChannel"; |
||||
channelElem = DomUtils.getChildElementByTagName(element, "client-outbound-channel"); |
||||
RuntimeBeanReference clientOutChannel = getMessageChannel(channelName, channelElem, parserCxt, source); |
||||
|
||||
RootBeanDefinition userSessionRegistryDef = new RootBeanDefinition(DefaultUserSessionRegistry.class); |
||||
String userSessionRegistryName = registerBeanDef(userSessionRegistryDef, parserCxt, source); |
||||
RuntimeBeanReference userSessionRegistry = new RuntimeBeanReference(userSessionRegistryName); |
||||
|
||||
RuntimeBeanReference subProtocolWebSocketHandler = registerSubProtocolWebSocketHandler( |
||||
clientInChannel, clientOutChannel, userSessionRegistry, parserCxt, source); |
||||
|
||||
List<Element> stompEndpointElements = DomUtils.getChildElementsByTagName(element, "stomp-endpoint"); |
||||
for(Element stompEndpointElement : stompEndpointElements) { |
||||
|
||||
RuntimeBeanReference requestHandler = registerHttpRequestHandler( |
||||
stompEndpointElement, subProtocolWebSocketHandler, parserCxt, source); |
||||
|
||||
List<String> paths = Arrays.asList(stompEndpointElement.getAttribute("path").split(",")); |
||||
for(String path : paths) { |
||||
if (DomUtils.getChildElementByTagName(stompEndpointElement, "sockjs") != null) { |
||||
path = path.endsWith("/") ? path + "**" : path + "/**"; |
||||
} |
||||
urlMap.put(path, requestHandler); |
||||
} |
||||
} |
||||
|
||||
registerBeanDef(handlerMappingDef, parserCxt, source); |
||||
|
||||
channelName = "brokerChannel"; |
||||
channelElem = DomUtils.getChildElementByTagName(element, "broker-channel"); |
||||
RuntimeBeanReference brokerChannel = getMessageChannel(channelName, channelElem, parserCxt, source); |
||||
registerMessageBroker(element, clientInChannel, clientOutChannel, brokerChannel, parserCxt, source); |
||||
|
||||
RuntimeBeanReference messageConverter = registerBrokerMessageConverter(parserCxt, source); |
||||
RuntimeBeanReference messagingTemplate = registerBrokerMessagingTemplate(element, brokerChannel, |
||||
messageConverter, parserCxt, source); |
||||
|
||||
registerAnnotationMethodMessageHandler(element, clientInChannel, clientOutChannel, |
||||
messageConverter, messagingTemplate, parserCxt, source); |
||||
|
||||
RuntimeBeanReference userDestinationResolver = registerUserDestinationResolver(element, |
||||
userSessionRegistryDef, parserCxt, source); |
||||
|
||||
registerUserDestinationMessageHandler(clientInChannel, clientOutChannel, brokerChannel, |
||||
userDestinationResolver, parserCxt, source); |
||||
|
||||
parserCxt.popAndRegisterContainingComponent(); |
||||
|
||||
return null; |
||||
} |
||||
|
||||
private RuntimeBeanReference getMessageChannel(String channelName, Element channelElement, |
||||
ParserContext parserCxt, Object source) { |
||||
|
||||
RootBeanDefinition executorDef = null; |
||||
|
||||
if (channelElement != null) { |
||||
Element executor = DomUtils.getChildElementByTagName(channelElement, "executor"); |
||||
if (executor != null) { |
||||
executorDef = new RootBeanDefinition(ThreadPoolTaskExecutor.class); |
||||
String attrValue = executor.getAttribute("core-pool-size"); |
||||
if (!StringUtils.isEmpty(attrValue)) { |
||||
executorDef.getPropertyValues().add("corePoolSize", attrValue); |
||||
} |
||||
attrValue = executor.getAttribute("max-pool-size"); |
||||
if (!StringUtils.isEmpty(attrValue)) { |
||||
executorDef.getPropertyValues().add("maxPoolSize", attrValue); |
||||
} |
||||
attrValue = executor.getAttribute("keep-alive-seconds"); |
||||
if (!StringUtils.isEmpty(attrValue)) { |
||||
executorDef.getPropertyValues().add("keepAliveSeconds", attrValue); |
||||
} |
||||
attrValue = executor.getAttribute("queue-capacity"); |
||||
if (!StringUtils.isEmpty(attrValue)) { |
||||
executorDef.getPropertyValues().add("queueCapacity", attrValue); |
||||
} |
||||
} |
||||
} |
||||
else if (!channelName.equals("brokerChannel")) { |
||||
executorDef = new RootBeanDefinition(ThreadPoolTaskExecutor.class); |
||||
} |
||||
|
||||
ConstructorArgumentValues values = new ConstructorArgumentValues(); |
||||
if (executorDef != null) { |
||||
executorDef.getPropertyValues().add("threadNamePrefix", channelName + "-"); |
||||
String executorName = channelName + "Executor"; |
||||
registerBeanDefByName(executorName, executorDef, parserCxt, source); |
||||
values.addIndexedArgumentValue(0, new RuntimeBeanReference(executorName)); |
||||
} |
||||
|
||||
RootBeanDefinition channelDef = new RootBeanDefinition(ExecutorSubscribableChannel.class, values, null); |
||||
|
||||
if (channelElement != null) { |
||||
Element interceptorsElement = DomUtils.getChildElementByTagName(channelElement, "interceptors"); |
||||
ManagedList<?> interceptorList = WebSocketNamespaceUtils.parseBeanSubElements(interceptorsElement, parserCxt); |
||||
channelDef.getPropertyValues().add("interceptors", interceptorList); |
||||
} |
||||
|
||||
registerBeanDefByName(channelName, channelDef, parserCxt, source); |
||||
return new RuntimeBeanReference(channelName); |
||||
} |
||||
|
||||
private RuntimeBeanReference registerSubProtocolWebSocketHandler( |
||||
RuntimeBeanReference clientInChannel, RuntimeBeanReference clientOutChannel, |
||||
RuntimeBeanReference userSessionRegistry, ParserContext parserCxt, Object source) { |
||||
|
||||
RootBeanDefinition stompHandlerDef = new RootBeanDefinition(StompSubProtocolHandler.class); |
||||
stompHandlerDef.getPropertyValues().add("userSessionRegistry", userSessionRegistry); |
||||
registerBeanDef(stompHandlerDef, parserCxt, source); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, clientInChannel); |
||||
cavs.addIndexedArgumentValue(1, clientOutChannel); |
||||
|
||||
RootBeanDefinition subProtocolWshDef = new RootBeanDefinition(SubProtocolWebSocketHandler.class, cavs, null); |
||||
subProtocolWshDef.getPropertyValues().addPropertyValue("protocolHandlers", stompHandlerDef); |
||||
String subProtocolWshName = registerBeanDef(subProtocolWshDef, parserCxt, source); |
||||
return new RuntimeBeanReference(subProtocolWshName); |
||||
} |
||||
|
||||
private RuntimeBeanReference registerHttpRequestHandler(Element stompEndpointElement, |
||||
RuntimeBeanReference subProtocolWebSocketHandler, ParserContext parserCxt, Object source) { |
||||
|
||||
RootBeanDefinition httpRequestHandlerDef; |
||||
|
||||
RuntimeBeanReference handshakeHandler = |
||||
WebSocketNamespaceUtils.registerHandshakeHandler(stompEndpointElement, parserCxt, source); |
||||
|
||||
RuntimeBeanReference sockJsService = WebSocketNamespaceUtils.registerSockJsService( |
||||
stompEndpointElement, SOCKJS_SCHEDULER_BEAN_NAME, parserCxt, source); |
||||
|
||||
if (sockJsService != null) { |
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, sockJsService); |
||||
cavs.addIndexedArgumentValue(1, subProtocolWebSocketHandler); |
||||
httpRequestHandlerDef = new RootBeanDefinition(SockJsHttpRequestHandler.class, cavs, null); |
||||
} |
||||
else { |
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, subProtocolWebSocketHandler); |
||||
if(handshakeHandler != null) { |
||||
cavs.addIndexedArgumentValue(1, handshakeHandler); |
||||
} |
||||
httpRequestHandlerDef = new RootBeanDefinition(WebSocketHttpRequestHandler.class, cavs, null); |
||||
// TODO: httpRequestHandlerDef.getPropertyValues().add("handshakeInterceptors", ...);
|
||||
} |
||||
|
||||
String httpRequestHandlerBeanName = registerBeanDef(httpRequestHandlerDef, parserCxt, source); |
||||
return new RuntimeBeanReference(httpRequestHandlerBeanName); |
||||
} |
||||
|
||||
private void registerMessageBroker(Element messageBrokerElement, RuntimeBeanReference clientInChannelDef, |
||||
RuntimeBeanReference clientOutChannelDef, RuntimeBeanReference brokerChannelDef, |
||||
ParserContext parserCxt, Object source) { |
||||
|
||||
Element simpleBrokerElem = DomUtils.getChildElementByTagName(messageBrokerElement, "simple-broker"); |
||||
Element brokerRelayElem = DomUtils.getChildElementByTagName(messageBrokerElement, "stomp-broker-relay"); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, clientInChannelDef); |
||||
cavs.addIndexedArgumentValue(1, clientOutChannelDef); |
||||
cavs.addIndexedArgumentValue(2, brokerChannelDef); |
||||
|
||||
if (simpleBrokerElem != null) { |
||||
|
||||
String prefix = simpleBrokerElem.getAttribute("prefix"); |
||||
cavs.addIndexedArgumentValue(3, Arrays.asList(prefix.split(","))); |
||||
RootBeanDefinition brokerDef = new RootBeanDefinition(SimpleBrokerMessageHandler.class, cavs, null); |
||||
registerBeanDef(brokerDef, parserCxt, source); |
||||
} |
||||
else if (brokerRelayElem != null) { |
||||
|
||||
String prefix = brokerRelayElem.getAttribute("prefix"); |
||||
cavs.addIndexedArgumentValue(3, Arrays.asList(prefix.split(","))); |
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues(); |
||||
String relayHost = brokerRelayElem.getAttribute("relay-host"); |
||||
if(!relayHost.isEmpty()) { |
||||
mpvs.add("relayHost",relayHost); |
||||
} |
||||
String relayPort = brokerRelayElem.getAttribute("relay-port"); |
||||
if(!relayPort.isEmpty()) { |
||||
mpvs.add("relayPort", Integer.valueOf(relayPort)); |
||||
} |
||||
String systemLogin = brokerRelayElem.getAttribute("system-login"); |
||||
if(!systemLogin.isEmpty()) { |
||||
mpvs.add("systemLogin",systemLogin); |
||||
} |
||||
String systemPasscode = brokerRelayElem.getAttribute("system-passcode"); |
||||
if(!systemPasscode.isEmpty()) { |
||||
mpvs.add("systemPasscode",systemPasscode); |
||||
} |
||||
String systemHeartbeatSendInterval = brokerRelayElem.getAttribute("system-heartbeat-send-interval"); |
||||
if(!systemHeartbeatSendInterval.isEmpty()) { |
||||
mpvs.add("systemHeartbeatSendInterval",Long.parseLong(systemHeartbeatSendInterval)); |
||||
} |
||||
String systemHeartbeatReceiveInterval = brokerRelayElem.getAttribute("system-heartbeat-receive-interval"); |
||||
if(!systemHeartbeatReceiveInterval.isEmpty()) { |
||||
mpvs.add("systemHeartbeatReceiveInterval",Long.parseLong(systemHeartbeatReceiveInterval)); |
||||
} |
||||
String virtualHost = brokerRelayElem.getAttribute("virtual-host"); |
||||
if(!virtualHost.isEmpty()) { |
||||
mpvs.add("virtualHost",virtualHost); |
||||
} |
||||
|
||||
RootBeanDefinition messageBrokerDef = new RootBeanDefinition(StompBrokerRelayMessageHandler.class, cavs, mpvs); |
||||
registerBeanDef(messageBrokerDef, parserCxt, source); |
||||
} |
||||
|
||||
} |
||||
|
||||
private RuntimeBeanReference registerBrokerMessageConverter(ParserContext parserCxt, Object source) { |
||||
|
||||
RootBeanDefinition contentTypeResolverDef = new RootBeanDefinition(DefaultContentTypeResolver.class); |
||||
|
||||
ManagedList<RootBeanDefinition> convertersDef = new ManagedList<RootBeanDefinition>(); |
||||
if (jackson2Present) { |
||||
convertersDef.add(new RootBeanDefinition(MappingJackson2MessageConverter.class)); |
||||
contentTypeResolverDef.getPropertyValues().add("defaultMimeType", MimeTypeUtils.APPLICATION_JSON); |
||||
} |
||||
convertersDef.add(new RootBeanDefinition(StringMessageConverter.class)); |
||||
convertersDef.add(new RootBeanDefinition(ByteArrayMessageConverter.class)); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, convertersDef); |
||||
cavs.addIndexedArgumentValue(1, contentTypeResolverDef); |
||||
|
||||
RootBeanDefinition brokerMessage = new RootBeanDefinition(CompositeMessageConverter.class, cavs, null); |
||||
return new RuntimeBeanReference(registerBeanDef(brokerMessage,parserCxt, source)); |
||||
} |
||||
|
||||
private RuntimeBeanReference registerBrokerMessagingTemplate( |
||||
Element element, RuntimeBeanReference brokerChannelDef, RuntimeBeanReference messageConverterRef, |
||||
ParserContext parserCxt, Object source) { |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, brokerChannelDef); |
||||
RootBeanDefinition messagingTemplateDef = new RootBeanDefinition(SimpMessagingTemplate.class,cavs, null); |
||||
|
||||
String userDestinationPrefixAttribute = element.getAttribute("user-destination-prefix"); |
||||
if(!userDestinationPrefixAttribute.isEmpty()) { |
||||
messagingTemplateDef.getPropertyValues().add("userDestinationPrefix", userDestinationPrefixAttribute); |
||||
} |
||||
messagingTemplateDef.getPropertyValues().add("messageConverter", messageConverterRef); |
||||
|
||||
return new RuntimeBeanReference(registerBeanDef(messagingTemplateDef,parserCxt, source)); |
||||
} |
||||
|
||||
private void registerAnnotationMethodMessageHandler(Element messageBrokerElement, |
||||
RuntimeBeanReference clientInChannelDef, RuntimeBeanReference clientOutChannelDef, |
||||
RuntimeBeanReference brokerMessageConverterRef, RuntimeBeanReference brokerMessagingTemplateRef, |
||||
ParserContext parserCxt, Object source) { |
||||
|
||||
String applicationDestinationPrefix = messageBrokerElement.getAttribute("application-destination-prefix"); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, clientInChannelDef); |
||||
cavs.addIndexedArgumentValue(1, clientOutChannelDef); |
||||
cavs.addIndexedArgumentValue(2, brokerMessagingTemplateRef); |
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues(); |
||||
mpvs.add("destinationPrefixes",Arrays.asList(applicationDestinationPrefix.split(","))); |
||||
mpvs.add("messageConverter", brokerMessageConverterRef); |
||||
|
||||
RootBeanDefinition annotationMethodMessageHandlerDef = |
||||
new RootBeanDefinition(SimpAnnotationMethodMessageHandler.class, cavs, mpvs); |
||||
|
||||
registerBeanDef(annotationMethodMessageHandlerDef, parserCxt, source); |
||||
} |
||||
|
||||
private RuntimeBeanReference registerUserDestinationResolver(Element messageBrokerElement, |
||||
BeanDefinition userSessionRegistryDef, ParserContext parserCxt, Object source) { |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, userSessionRegistryDef); |
||||
RootBeanDefinition userDestinationResolverDef = |
||||
new RootBeanDefinition(DefaultUserDestinationResolver.class, cavs, null); |
||||
String prefix = messageBrokerElement.getAttribute("user-destination-prefix"); |
||||
if (!prefix.isEmpty()) { |
||||
userDestinationResolverDef.getPropertyValues().add("userDestinationPrefix", prefix); |
||||
} |
||||
String userDestinationResolverName = registerBeanDef(userDestinationResolverDef, parserCxt, source); |
||||
return new RuntimeBeanReference(userDestinationResolverName); |
||||
} |
||||
|
||||
private RuntimeBeanReference registerUserDestinationMessageHandler(RuntimeBeanReference clientInChannelDef, |
||||
RuntimeBeanReference clientOutChannelDef, RuntimeBeanReference brokerChannelDef, |
||||
RuntimeBeanReference userDestinationResolverRef, ParserContext parserCxt, Object source) { |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, clientInChannelDef); |
||||
cavs.addIndexedArgumentValue(1, clientOutChannelDef); |
||||
cavs.addIndexedArgumentValue(2, brokerChannelDef); |
||||
cavs.addIndexedArgumentValue(3, userDestinationResolverRef); |
||||
|
||||
RootBeanDefinition userDestinationMessageHandlerDef = |
||||
new RootBeanDefinition(UserDestinationMessageHandler.class, cavs, null); |
||||
|
||||
String userDestinationMessageHandleName = registerBeanDef(userDestinationMessageHandlerDef, parserCxt, source); |
||||
return new RuntimeBeanReference(userDestinationMessageHandleName); |
||||
} |
||||
|
||||
|
||||
private static String registerBeanDef(RootBeanDefinition beanDef, ParserContext parserCxt, Object source) { |
||||
String beanName = parserCxt.getReaderContext().generateBeanName(beanDef); |
||||
registerBeanDefByName(beanName, beanDef, parserCxt, source); |
||||
return beanName; |
||||
} |
||||
|
||||
private static void registerBeanDefByName(String beanName, RootBeanDefinition beanDef, |
||||
ParserContext parserCxt, Object source) { |
||||
|
||||
beanDef.setSource(source); |
||||
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
parserCxt.getRegistry().registerBeanDefinition(beanName, beanDef); |
||||
parserCxt.registerComponent(new BeanComponentDefinition(beanDef, beanName)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/* |
||||
* 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.springframework.beans.factory.xml.NamespaceHandlerSupport; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.web.socket.server.config.xml.HandlersBeanDefinitionParser; |
||||
|
||||
|
||||
/** |
||||
* {@link org.springframework.beans.factory.xml.NamespaceHandler} for Spring WebSocket |
||||
* configuration namespace. |
||||
* |
||||
* @author Brian Clozel |
||||
* @since 4.0 |
||||
*/ |
||||
public class WebSocketNamespaceHandler extends NamespaceHandlerSupport { |
||||
|
||||
private static boolean isSpringMessagingPresent = ClassUtils.isPresent( |
||||
"org.springframework.messaging.Message", WebSocketNamespaceHandler.class.getClassLoader()); |
||||
|
||||
|
||||
@Override |
||||
public void init() { |
||||
registerBeanDefinitionParser("handlers", new HandlersBeanDefinitionParser()); |
||||
if (isSpringMessagingPresent) { |
||||
registerBeanDefinitionParser("message-broker", new MessageBrokerBeanDefinitionParser()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
/** |
||||
* Support for the {@code <websocket:message-broker>} XML namespace element. |
||||
*/ |
||||
package org.springframework.web.socket.messaging.config.xml; |
||||
@ -0,0 +1,194 @@
@@ -0,0 +1,194 @@
|
||||
/* |
||||
* 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.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues; |
||||
import org.springframework.beans.factory.config.RuntimeBeanReference; |
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition; |
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition; |
||||
import org.springframework.beans.factory.support.ManagedList; |
||||
import org.springframework.beans.factory.support.ManagedMap; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser; |
||||
import org.springframework.beans.factory.xml.ParserContext; |
||||
import org.springframework.util.xml.DomUtils; |
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; |
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler; |
||||
import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler; |
||||
import org.w3c.dom.Element; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* A {@link BeanDefinitionParser} that provides the configuration for the |
||||
* {@code <websocket:handlers/>} namespace element. It registers a Spring MVC |
||||
* {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping} |
||||
* to map HTTP WebSocket handshake requests to |
||||
* {@link org.springframework.web.socket.WebSocketHandler}s. |
||||
* |
||||
* @author Brian Clozel |
||||
* @since 4.0 |
||||
*/ |
||||
public class HandlersBeanDefinitionParser implements BeanDefinitionParser { |
||||
|
||||
private static final String SOCK_JS_SCHEDULER_NAME = "SockJsScheduler"; |
||||
|
||||
private static final int DEFAULT_MAPPING_ORDER = 1; |
||||
|
||||
|
||||
@Override |
||||
public BeanDefinition parse(Element element, ParserContext parserCxt) { |
||||
|
||||
Object source = parserCxt.extractSource(element); |
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); |
||||
parserCxt.pushContainingComponent(compDefinition); |
||||
|
||||
String orderAttribute = element.getAttribute("order"); |
||||
int order = orderAttribute.isEmpty() ? DEFAULT_MAPPING_ORDER : Integer.valueOf(orderAttribute); |
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class); |
||||
handlerMappingDef.setSource(source); |
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
handlerMappingDef.getPropertyValues().add("order", order); |
||||
String handlerMappingName = parserCxt.getReaderContext().registerWithGeneratedName(handlerMappingDef); |
||||
|
||||
RuntimeBeanReference handshakeHandler = WebSocketNamespaceUtils.registerHandshakeHandler(element, parserCxt, source); |
||||
Element interceptorsElement = DomUtils.getChildElementByTagName(element, "handshake-interceptors"); |
||||
ManagedList<?> interceptors = WebSocketNamespaceUtils.parseBeanSubElements(interceptorsElement, parserCxt); |
||||
RuntimeBeanReference sockJsServiceRef = |
||||
WebSocketNamespaceUtils.registerSockJsService(element, SOCK_JS_SCHEDULER_NAME, parserCxt, source); |
||||
|
||||
HandlerMappingStrategy strategy = createHandlerMappingStrategy(sockJsServiceRef, handshakeHandler, interceptors); |
||||
|
||||
List<Element> mappingElements = DomUtils.getChildElementsByTagName(element, "mapping"); |
||||
ManagedMap<String, Object> urlMap = new ManagedMap<String, Object>(); |
||||
urlMap.setSource(source); |
||||
|
||||
for(Element mappingElement : mappingElements) { |
||||
urlMap.putAll(strategy.createMappings(mappingElement, parserCxt)); |
||||
} |
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap); |
||||
|
||||
parserCxt.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingName)); |
||||
parserCxt.popAndRegisterContainingComponent(); |
||||
return null; |
||||
} |
||||
|
||||
|
||||
private interface HandlerMappingStrategy { |
||||
|
||||
public ManagedMap<String, Object> createMappings(Element mappingElement, ParserContext parserContext); |
||||
} |
||||
|
||||
private HandlerMappingStrategy createHandlerMappingStrategy( |
||||
RuntimeBeanReference sockJsServiceRef, RuntimeBeanReference handshakeHandlerRef, |
||||
ManagedList<? extends Object> interceptorsList) { |
||||
|
||||
if(sockJsServiceRef != null) { |
||||
SockJSHandlerMappingStrategy strategy = new SockJSHandlerMappingStrategy(); |
||||
strategy.setSockJsServiceRef(sockJsServiceRef); |
||||
return strategy; |
||||
} |
||||
else { |
||||
WebSocketHandlerMappingStrategy strategy = new WebSocketHandlerMappingStrategy(); |
||||
strategy.setHandshakeHandlerReference(handshakeHandlerRef); |
||||
strategy.setInterceptorsList(interceptorsList); |
||||
return strategy; |
||||
} |
||||
} |
||||
|
||||
private class WebSocketHandlerMappingStrategy implements HandlerMappingStrategy { |
||||
|
||||
private RuntimeBeanReference handshakeHandlerReference; |
||||
|
||||
private ManagedList<?> interceptorsList; |
||||
|
||||
public void setHandshakeHandlerReference(RuntimeBeanReference handshakeHandlerReference) { |
||||
this.handshakeHandlerReference = handshakeHandlerReference; |
||||
} |
||||
|
||||
public void setInterceptorsList(ManagedList<?> interceptorsList) { this.interceptorsList = interceptorsList; } |
||||
|
||||
@Override |
||||
public ManagedMap<String, Object> createMappings(Element mappingElement, ParserContext parserContext) { |
||||
|
||||
ManagedMap<String, Object> urlMap = new ManagedMap<String, Object>(); |
||||
Object source = parserContext.extractSource(mappingElement); |
||||
|
||||
List<String> mappings = Arrays.asList(mappingElement.getAttribute("path").split(",")); |
||||
RuntimeBeanReference webSocketHandlerReference = new RuntimeBeanReference(mappingElement.getAttribute("handler")); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, webSocketHandlerReference); |
||||
if(this.handshakeHandlerReference != null) { |
||||
cavs.addIndexedArgumentValue(1, this.handshakeHandlerReference); |
||||
} |
||||
RootBeanDefinition requestHandlerDef = new RootBeanDefinition(WebSocketHttpRequestHandler.class, cavs, null); |
||||
requestHandlerDef.setSource(source); |
||||
requestHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
requestHandlerDef.getPropertyValues().add("handshakeInterceptors", this.interceptorsList); |
||||
String requestHandlerName = parserContext.getReaderContext().registerWithGeneratedName(requestHandlerDef); |
||||
RuntimeBeanReference requestHandlerRef = new RuntimeBeanReference(requestHandlerName); |
||||
|
||||
for(String mapping : mappings) { |
||||
urlMap.put(mapping, requestHandlerRef); |
||||
} |
||||
|
||||
return urlMap; |
||||
} |
||||
} |
||||
|
||||
private class SockJSHandlerMappingStrategy implements HandlerMappingStrategy { |
||||
|
||||
private RuntimeBeanReference sockJsServiceRef; |
||||
|
||||
public void setSockJsServiceRef(RuntimeBeanReference sockJsServiceRef) { |
||||
this.sockJsServiceRef = sockJsServiceRef; |
||||
} |
||||
|
||||
@Override |
||||
public ManagedMap<String, Object> createMappings(Element mappingElement, ParserContext parserContext) { |
||||
|
||||
ManagedMap<String, Object> urlMap = new ManagedMap<String, Object>(); |
||||
Object source = parserContext.extractSource(mappingElement); |
||||
|
||||
List<String> mappings = Arrays.asList(mappingElement.getAttribute("path").split(",")); |
||||
RuntimeBeanReference webSocketHandlerReference = new RuntimeBeanReference(mappingElement.getAttribute("handler")); |
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
cavs.addIndexedArgumentValue(0, this.sockJsServiceRef, "SockJsService"); |
||||
cavs.addIndexedArgumentValue(1, webSocketHandlerReference, "WebSocketHandler"); |
||||
|
||||
RootBeanDefinition requestHandlerDef = new RootBeanDefinition(SockJsHttpRequestHandler.class, cavs, null); |
||||
requestHandlerDef.setSource(source); |
||||
requestHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
String requestHandlerName = parserContext.getReaderContext().registerWithGeneratedName(requestHandlerDef); |
||||
RuntimeBeanReference requestHandlerRef = new RuntimeBeanReference(requestHandlerName); |
||||
|
||||
for(String path : mappings) { |
||||
String pathPattern = path.endsWith("/") ? path + "**" : path + "/**"; |
||||
urlMap.put(pathPattern, requestHandlerRef); |
||||
} |
||||
|
||||
return urlMap; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,178 @@
@@ -0,0 +1,178 @@
|
||||
/* |
||||
* 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.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues; |
||||
import org.springframework.beans.factory.config.RuntimeBeanReference; |
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition; |
||||
import org.springframework.beans.factory.support.ManagedList; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.factory.xml.ParserContext; |
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
||||
import org.springframework.util.xml.DomUtils; |
||||
import org.springframework.web.socket.server.DefaultHandshakeHandler; |
||||
import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService; |
||||
import org.w3c.dom.Element; |
||||
|
||||
import java.util.Collections; |
||||
|
||||
/** |
||||
* Provides utility methods for parsing common WebSocket XML namespace elements. |
||||
* |
||||
* @author Brian Clozel |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class WebSocketNamespaceUtils { |
||||
|
||||
|
||||
public static RuntimeBeanReference registerHandshakeHandler(Element element, ParserContext parserContext, Object source) { |
||||
|
||||
RuntimeBeanReference handlerRef; |
||||
|
||||
Element handlerElem = DomUtils.getChildElementByTagName(element, "handshake-handler"); |
||||
if(handlerElem != null) { |
||||
handlerRef = new RuntimeBeanReference(handlerElem.getAttribute("ref")); |
||||
} |
||||
else { |
||||
RootBeanDefinition defaultHandlerDef = new RootBeanDefinition(DefaultHandshakeHandler.class); |
||||
defaultHandlerDef.setSource(source); |
||||
defaultHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
String handlerName = parserContext.getReaderContext().registerWithGeneratedName(defaultHandlerDef); |
||||
handlerRef = new RuntimeBeanReference(handlerName); |
||||
} |
||||
|
||||
return handlerRef; |
||||
} |
||||
|
||||
public static RuntimeBeanReference registerSockJsService(Element element, String sockJsSchedulerName, |
||||
ParserContext parserContext, Object source) { |
||||
|
||||
Element sockJsElement = DomUtils.getChildElementByTagName(element, "sockjs"); |
||||
|
||||
if(sockJsElement != null) { |
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues(); |
||||
|
||||
// TODO: polish the way constructor arguments are set
|
||||
|
||||
String customTaskSchedulerName = sockJsElement.getAttribute("task-scheduler"); |
||||
if(!customTaskSchedulerName.isEmpty()) { |
||||
cavs.addIndexedArgumentValue(0, new RuntimeBeanReference(customTaskSchedulerName)); |
||||
} |
||||
else { |
||||
cavs.addIndexedArgumentValue(0, registerSockJsTaskScheduler(sockJsSchedulerName, parserContext, source)); |
||||
} |
||||
|
||||
Element transportHandlersElement = DomUtils.getChildElementByTagName(sockJsElement, "transport-handlers"); |
||||
boolean registerDefaults = true; |
||||
if(transportHandlersElement != null) { |
||||
String registerDefaultsAttribute = transportHandlersElement.getAttribute("register-defaults"); |
||||
registerDefaults = !registerDefaultsAttribute.equals("false"); |
||||
} |
||||
|
||||
ManagedList<?> transportHandlersList = parseBeanSubElements(transportHandlersElement, parserContext); |
||||
|
||||
if(registerDefaults) { |
||||
cavs.addIndexedArgumentValue(1, Collections.emptyList()); |
||||
if(transportHandlersList.isEmpty()) { |
||||
cavs.addIndexedArgumentValue(2, new ConstructorArgumentValues.ValueHolder(null)); |
||||
} |
||||
else { |
||||
cavs.addIndexedArgumentValue(2, transportHandlersList); |
||||
} |
||||
} |
||||
else { |
||||
if(transportHandlersList.isEmpty()) { |
||||
cavs.addIndexedArgumentValue(1, new ConstructorArgumentValues.ValueHolder(null)); |
||||
} |
||||
else { |
||||
cavs.addIndexedArgumentValue(1, transportHandlersList); |
||||
} |
||||
cavs.addIndexedArgumentValue(2, new ConstructorArgumentValues.ValueHolder(null)); |
||||
} |
||||
|
||||
RootBeanDefinition sockJsServiceDef = new RootBeanDefinition(DefaultSockJsService.class, cavs, null); |
||||
sockJsServiceDef.setSource(source); |
||||
|
||||
String attrValue = sockJsElement.getAttribute("name"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("name", attrValue); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("websocket-enabled"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("webSocketsEnabled", Boolean.valueOf(attrValue)); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("session-cookie-needed"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("sessionCookieNeeded", Boolean.valueOf(attrValue)); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("stream-bytes-limit"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("streamBytesLimit", Integer.valueOf(attrValue)); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("disconnect-delay"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("disconnectDelay", Long.valueOf(attrValue)); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("http-message-cache-size"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("httpMessageCacheSize", Integer.valueOf(attrValue)); |
||||
} |
||||
attrValue = sockJsElement.getAttribute("heartbeat-time"); |
||||
if(!attrValue.isEmpty()) { |
||||
sockJsServiceDef.getPropertyValues().add("heartbeatTime", Long.valueOf(attrValue)); |
||||
} |
||||
|
||||
sockJsServiceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
String sockJsServiceName = parserContext.getReaderContext().registerWithGeneratedName(sockJsServiceDef); |
||||
return new RuntimeBeanReference(sockJsServiceName); |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
private static RuntimeBeanReference registerSockJsTaskScheduler(String schedulerName, |
||||
ParserContext parserContext, Object source) { |
||||
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(schedulerName)) { |
||||
RootBeanDefinition taskSchedulerDef = new RootBeanDefinition(ThreadPoolTaskScheduler.class); |
||||
taskSchedulerDef.setSource(source); |
||||
taskSchedulerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
taskSchedulerDef.getPropertyValues().add("threadNamePrefix", schedulerName + "-"); |
||||
parserContext.getRegistry().registerBeanDefinition(schedulerName, taskSchedulerDef); |
||||
parserContext.registerComponent(new BeanComponentDefinition(taskSchedulerDef, schedulerName)); |
||||
} |
||||
|
||||
return new RuntimeBeanReference(schedulerName); |
||||
} |
||||
|
||||
public static ManagedList<? super Object> parseBeanSubElements(Element parentElement, ParserContext parserContext) { |
||||
|
||||
ManagedList<? super Object> beans = new ManagedList<Object>(); |
||||
if (parentElement != null) { |
||||
beans.setSource(parserContext.extractSource(parentElement)); |
||||
for (Element beanElement : DomUtils.getChildElementsByTagName(parentElement, new String[] { "bean", "ref" })) { |
||||
Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null); |
||||
beans.add(object); |
||||
} |
||||
} |
||||
|
||||
return beans; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
/** |
||||
* Support for the {@code <websocket:handlers>} XML namespace element. |
||||
*/ |
||||
package org.springframework.web.socket.server.config.xml; |
||||
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
# |
||||
# 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. |
||||
# |
||||
|
||||
http\://www.springframework.org/schema/websocket=org.springframework.web.socket.messaging.config.xml.WebSocketNamespaceHandler |
||||
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
# |
||||
# 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. |
||||
# |
||||
|
||||
http\://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd=org/springframework/web/socket/server/config/xml/spring-websocket-4.0.xsd |
||||
http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/server/config/xml/spring-websocket-4.0.xsd |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the mvc namespace |
||||
http\://www.springframework.org/schema/websocket@name=websocket Namespace |
||||
http\://www.springframework.org/schema/websocket@prefix=websocket |
||||
http\://www.springframework.org/schema/websocket@icon=org/springframework/web/socket/server/config/xml/spring-websocket.gif |
||||
@ -0,0 +1,217 @@
@@ -0,0 +1,217 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/websocket" |
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
||||
xmlns:beans="http://www.springframework.org/schema/beans" |
||||
targetNamespace="http://www.springframework.org/schema/websocket" |
||||
elementFormDefault="qualified" |
||||
attributeFormDefault="unqualified"> |
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" |
||||
schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.0.xsd" /> |
||||
|
||||
<xsd:complexType name="mapping"> |
||||
<xsd:attribute name="path" type="xsd:string" use="required"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="handler" type="xsd:string" use="required"> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="handshake-handler"> |
||||
<xsd:attribute name="ref" type="xsd:string" use="required"> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="handshake-interceptors"> |
||||
<xsd:sequence> |
||||
<xsd:choice maxOccurs="unbounded"> |
||||
<xsd:element ref="beans:bean"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
An HandshakeInterceptor bean definition. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
<xsd:element ref="beans:ref"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
A reference to a HandshakeInterceptor bean. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
</xsd:choice> |
||||
</xsd:sequence> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="sockjs-service"> |
||||
<xsd:sequence> |
||||
<xsd:element name="transport-handlers" minOccurs="0" maxOccurs="1"> |
||||
<xsd:complexType> |
||||
<xsd:sequence> |
||||
<xsd:choice maxOccurs="unbounded"> |
||||
<xsd:element ref="beans:bean"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
An TransportHandler bean definition. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
<xsd:element ref="beans:ref"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
A reference to a TransportHandler bean. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
</xsd:choice> |
||||
</xsd:sequence> |
||||
<xsd:attribute name="register-defaults" type="xsd:boolean" default="true"> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
</xsd:element> |
||||
|
||||
</xsd:sequence> |
||||
<xsd:attribute name="name" type="xsd:string"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="client-library-url" type="xsd:string"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="stream-bytes-limit" type="xsd:long"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="session-cookie-needed" type="xsd:boolean"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="heartbeat-time" type="xsd:long"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="disconnect-delay" type="xsd:long"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="http-message-cache-size" type="xsd:long"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="websocket-enabled" type="xsd:boolean"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="task-scheduler" type="xsd:string"> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="stomp-broker-relay"> |
||||
<xsd:attribute name="prefix" type="xsd:string"></xsd:attribute> |
||||
<xsd:attribute name="relay-host" type="xsd:string"></xsd:attribute> |
||||
<xsd:attribute name="relay-port" type="xsd:integer"></xsd:attribute> |
||||
<xsd:attribute name="system-login" type="xsd:string"></xsd:attribute> |
||||
<xsd:attribute name="system-passcode" type="xsd:string"></xsd:attribute> |
||||
<xsd:attribute name="system-heartbeat-send-interval" type="xsd:long"></xsd:attribute> |
||||
<xsd:attribute name="system-heartbeat-receive-interval" type="xsd:long"></xsd:attribute> |
||||
<xsd:attribute name="auto-startup" type="xsd:boolean"></xsd:attribute> |
||||
<xsd:attribute name="virtual-host" type="xsd:string"></xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="simple-broker"> |
||||
<xsd:attribute name="prefix" type="xsd:string"></xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="channel"> |
||||
<xsd:sequence> |
||||
<xsd:element name="executor" type="channel-executor" minOccurs="0" maxOccurs="1" /> |
||||
<xsd:element name="interceptors" type="channel-interceptors" minOccurs="0" maxOccurs="1"/> |
||||
</xsd:sequence> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="channel-executor"> |
||||
<xsd:attribute name="core-pool-size" type="xsd:int" use="optional"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="max-pool-size" type="xsd:int" use="optional"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="keep-alive-seconds" type="xsd:int" use="optional"> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="queue-capacity" type="xsd:int" use="optional"> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
|
||||
<xsd:complexType name="channel-interceptors"> |
||||
<xsd:sequence> |
||||
<xsd:choice maxOccurs="unbounded"> |
||||
<xsd:element ref="beans:bean"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
A ChannelInterceptor bean definition. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
<xsd:element ref="beans:ref"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
A reference to a ChannelInterceptor bean. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:element> |
||||
</xsd:choice> |
||||
</xsd:sequence> |
||||
</xsd:complexType> |
||||
|
||||
<!-- Elements definitions --> |
||||
|
||||
<xsd:element name="handlers"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
|
||||
|
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
<xsd:complexType> |
||||
<xsd:sequence> |
||||
<xsd:element name="mapping" type="mapping" minOccurs="1" maxOccurs="unbounded" /> |
||||
<xsd:element name="handshake-handler" type="handshake-handler" minOccurs="0" maxOccurs="1" /> |
||||
<xsd:element name="handshake-interceptors" type="handshake-interceptors" minOccurs="0" maxOccurs="1"/> |
||||
<xsd:element name="sockjs" type="sockjs-service" minOccurs="0" maxOccurs="1"/> |
||||
</xsd:sequence> |
||||
<xsd:attribute name="order" type="xsd:integer"></xsd:attribute> |
||||
</xsd:complexType> |
||||
</xsd:element> |
||||
|
||||
<xsd:element name="message-broker"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
|
||||
|
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
<xsd:complexType> |
||||
<xsd:sequence> |
||||
<xsd:element name="stomp-endpoint" maxOccurs="unbounded"> |
||||
<xsd:complexType> |
||||
<xsd:sequence> |
||||
<xsd:element name="handshake-handler" type="handshake-handler" minOccurs="0" maxOccurs="1" /> |
||||
<xsd:element name="sockjs" type="sockjs-service" minOccurs="0" maxOccurs="1"/> |
||||
</xsd:sequence> |
||||
<xsd:attribute name="path" type="xsd:string"></xsd:attribute> |
||||
</xsd:complexType> |
||||
</xsd:element> |
||||
<xsd:choice> |
||||
<xsd:element name="simple-broker" type="simple-broker" /> |
||||
<xsd:element name="stomp-broker-relay" type="stomp-broker-relay" /> |
||||
</xsd:choice> |
||||
<xsd:element name="client-inbound-channel" type="channel" minOccurs="0" maxOccurs="1" /> |
||||
<xsd:element name="client-outbound-channel" type="channel" minOccurs="0" maxOccurs="1" /> |
||||
<xsd:element name="broker-channel" type="channel" minOccurs="0" maxOccurs="1" /> |
||||
</xsd:sequence> |
||||
<xsd:attribute name="application-destination-prefix" type="xsd:string" /> |
||||
<xsd:attribute name="user-destination-prefix" type="xsd:string" /> |
||||
<xsd:attribute name="order" type="xsd:integer" /> |
||||
</xsd:complexType> |
||||
</xsd:element> |
||||
</xsd:schema> |
||||
@ -0,0 +1,267 @@
@@ -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 @@
@@ -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; } |
||||
} |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal"> |
||||
<websocket:stomp-endpoint path="/foo,/bar"> |
||||
<websocket:handshake-handler ref="myHandler" /> |
||||
</websocket:stomp-endpoint> |
||||
<websocket:simple-broker prefix="/topic" /> |
||||
<websocket:client-inbound-channel> |
||||
<websocket:executor core-pool-size="100" max-pool-size="200" keep-alive-seconds="600" /> |
||||
<websocket:interceptors> |
||||
<ref bean="myInterceptor" /> |
||||
</websocket:interceptors> |
||||
</websocket:client-inbound-channel> |
||||
<websocket:client-outbound-channel> |
||||
<websocket:executor core-pool-size="101" max-pool-size="201" keep-alive-seconds="601" /> |
||||
<websocket:interceptors> |
||||
<ref bean="myInterceptor" /> |
||||
<bean class="org.springframework.messaging.support.channel.ChannelInterceptorAdapter" /> |
||||
</websocket:interceptors> |
||||
</websocket:client-outbound-channel> |
||||
<websocket:broker-channel> |
||||
<websocket:executor core-pool-size="102" max-pool-size="202" keep-alive-seconds="602" /> |
||||
</websocket:broker-channel> |
||||
</websocket:message-broker> |
||||
|
||||
<bean id="myHandler" class="org.springframework.web.socket.server.config.xml.TestHandshakeHandler" /> |
||||
|
||||
<bean id="myInterceptor" class="org.springframework.messaging.support.channel.ChannelInterceptorAdapter" /> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:message-broker order="2"> |
||||
<websocket:stomp-endpoint path="/foo"> |
||||
<websocket:handshake-handler ref="myHandler" /> |
||||
<websocket:sockjs /> |
||||
</websocket:stomp-endpoint> |
||||
<websocket:stomp-broker-relay |
||||
prefix="/topic,/queue" |
||||
relay-host="relayhost" |
||||
relay-port="1234" |
||||
system-login="login" |
||||
system-passcode="pass" |
||||
system-heartbeat-send-interval="5000" |
||||
system-heartbeat-receive-interval="5000" |
||||
virtual-host="spring.io"/> |
||||
</websocket:message-broker> |
||||
|
||||
<bean id="myHandler" class="org.springframework.web.socket.server.config.xml.TestWebSocketHandler" /> |
||||
</beans> |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal"> |
||||
<websocket:stomp-endpoint path="/foo,/bar"> |
||||
<websocket:handshake-handler ref="myHandler" /> |
||||
</websocket:stomp-endpoint> |
||||
<websocket:stomp-endpoint path="/test,/sockjs"> |
||||
<websocket:handshake-handler ref="myHandler" /> |
||||
<websocket:sockjs /> |
||||
</websocket:stomp-endpoint> |
||||
<websocket:simple-broker prefix="/topic" /> |
||||
</websocket:message-broker> |
||||
|
||||
<bean id="myHandler" class="org.springframework.web.socket.server.config.xml.TestHandshakeHandler" /> |
||||
</beans> |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation=" |
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:handlers order="2"> |
||||
<websocket:mapping path="/foo" handler="fooHandler" /> |
||||
<websocket:mapping path="/test" handler="testHandler" /> |
||||
<websocket:handshake-handler ref="testHandshakeHandler" /> |
||||
<websocket:handshake-interceptors> |
||||
<bean class="org.springframework.web.socket.server.config.xml.FooTestInterceptor"/> |
||||
<ref bean="barTestInterceptor" /> |
||||
</websocket:handshake-interceptors> |
||||
</websocket:handlers> |
||||
|
||||
<bean id="testHandler" class="org.springframework.web.socket.server.config.xml.TestWebSocketHandler" /> |
||||
<bean id="fooHandler" class="org.springframework.web.socket.server.config.xml.FooWebSocketHandler" /> |
||||
|
||||
<bean id="barTestInterceptor" class="org.springframework.web.socket.server.config.xml.BarTestInterceptor"/> |
||||
<bean id="testHandshakeHandler" class="org.springframework.web.socket.server.config.xml.TestHandshakeHandler" /> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation=" |
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:handlers > |
||||
<websocket:mapping path="/test" handler="testHandler" /> |
||||
<websocket:sockjs task-scheduler="testTaskScheduler" |
||||
name="testSockJsService" |
||||
websocket-enabled="false" |
||||
session-cookie-needed="false" |
||||
stream-bytes-limit="2048" |
||||
disconnect-delay="256" |
||||
http-message-cache-size="1024" |
||||
heartbeat-time="20"> |
||||
<websocket:transport-handlers register-defaults="false"> |
||||
<bean class="org.springframework.web.socket.sockjs.transport.handler.XhrPollingTransportHandler"/> |
||||
<ref bean="xhrStreamingTransportHandler" /> |
||||
</websocket:transport-handlers> |
||||
</websocket:sockjs> |
||||
</websocket:handlers> |
||||
|
||||
<bean id="testHandler" |
||||
class="org.springframework.web.socket.server.config.xml.TestWebSocketHandler" /> |
||||
|
||||
<bean id="xhrStreamingTransportHandler" |
||||
class="org.springframework.web.socket.sockjs.transport.handler.XhrStreamingTransportHandler" /> |
||||
|
||||
<bean id="testTaskScheduler" |
||||
class="org.springframework.web.socket.server.config.xml.TestTaskScheduler" /> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation=" |
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:handlers> |
||||
<websocket:mapping path="/test" handler="testHandler" /> |
||||
<websocket:mapping path="/foo/" handler="fooHandler" /> |
||||
<websocket:sockjs/> |
||||
</websocket:handlers> |
||||
|
||||
<bean id="testHandler" class="org.springframework.web.socket.server.config.xml.TestWebSocketHandler" /> |
||||
<bean id="fooHandler" class="org.springframework.web.socket.server.config.xml.FooWebSocketHandler" /> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:websocket="http://www.springframework.org/schema/websocket" |
||||
xsi:schemaLocation=" |
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> |
||||
|
||||
<websocket:handlers> |
||||
<websocket:mapping path="/foo,/bar" handler="fooHandler" /> |
||||
</websocket:handlers> |
||||
|
||||
<websocket:handlers> |
||||
<websocket:mapping path="/test" handler="testHandler" /> |
||||
</websocket:handlers> |
||||
|
||||
<bean id="testHandler" class="org.springframework.web.socket.server.config.xml.TestWebSocketHandler" /> |
||||
<bean id="fooHandler" class="org.springframework.web.socket.server.config.xml.FooWebSocketHandler" /> |
||||
|
||||
</beans> |
||||
Loading…
Reference in new issue