Browse Source

Allow to customize separator for messaging destinations

In order to be able to use separators like "." (used by default
by most broker relays) instead of "/" for destination patterns
handling, the PathMatcher used in spring-messaging can now
be customized easily thanks to XML websocket namespace
or JavaConfig.

AntPathMatcher has been updated in order to use the configured path
separator instead of an hardcoded "/" for path concatenation.
Extension handling is now disabled when the "." separator is configured.

Issue: SPR-11660
pull/592/head
Sebastien Deleuze 12 years ago committed by Rossen Stoyanchev
parent
commit
928a466b5d
  1. 35
      spring-core/src/main/java/org/springframework/util/AntPathMatcher.java
  2. 6
      spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java
  3. 41
      spring-messaging/src/main/java/org/springframework/messaging/handler/DestinationPatternsMessageCondition.java
  4. 37
      spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java
  5. 22
      spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java
  6. 5
      spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractBrokerRegistration.java
  7. 7
      spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
  8. 22
      spring-messaging/src/main/java/org/springframework/messaging/simp/config/MessageBrokerRegistry.java
  9. 9
      spring-messaging/src/main/java/org/springframework/messaging/simp/config/SimpleBrokerRegistration.java
  10. 72
      spring-messaging/src/test/java/org/springframework/messaging/handler/DestinationPatternsMessageConditionTests.java
  11. 33
      spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
  12. 14
      spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
  13. 3
      spring-websocket/src/main/resources/META-INF/spring.schemas
  14. 710
      spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd
  15. 8
      spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java
  16. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-converters-defaults-off.xml
  17. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-converters.xml
  18. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels-default-executor.xml
  19. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml
  20. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-relay.xml
  21. 4
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-simple.xml
  22. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-attributes.xml
  23. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-sockjs-attributes.xml
  24. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-sockjs.xml
  25. 2
      spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers.xml

35
spring-core/src/main/java/org/springframework/util/AntPathMatcher.java

@ -68,6 +68,23 @@ public class AntPathMatcher implements PathMatcher { @@ -68,6 +68,23 @@ public class AntPathMatcher implements PathMatcher {
final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
/**
* Create a new AntPathMatcher with the default Ant path separator "/".
*/
public AntPathMatcher() {
}
/**
* Create a new AntPathMatcher.
* @param pathSeparator the path separator to use
* @since 4.1
*/
public AntPathMatcher(String pathSeparator) {
if(pathSeparator != null) {
this.pathSeparator = pathSeparator;
}
}
/**
* Set the path separator to use for pattern parsing.
@ -430,20 +447,20 @@ public class AntPathMatcher implements PathMatcher { @@ -430,20 +447,20 @@ public class AntPathMatcher implements PathMatcher {
// /hotels/* + /booking -> /hotels/booking
// /hotels/* + booking -> /hotels/booking
if (pattern1.endsWith("/*")) {
return slashConcat(pattern1.substring(0, pattern1.length() - 2), pattern2);
if (pattern1.endsWith(this.pathSeparator + "*")) {
return separatorConcat(pattern1.substring(0, pattern1.length() - 2), pattern2);
}
// /hotels/** + /booking -> /hotels/**/booking
// /hotels/** + booking -> /hotels/**/booking
if (pattern1.endsWith("/**")) {
return slashConcat(pattern1, pattern2);
if (pattern1.endsWith(this.pathSeparator + "**")) {
return separatorConcat(pattern1, pattern2);
}
int starDotPos1 = pattern1.indexOf("*.");
if (pattern1ContainsUriVar || starDotPos1 == -1) {
if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
// simply concatenate the two patterns
return slashConcat(pattern1, pattern2);
return separatorConcat(pattern1, pattern2);
}
String extension1 = pattern1.substring(starDotPos1 + 1);
int dotPos2 = pattern2.indexOf('.');
@ -453,11 +470,11 @@ public class AntPathMatcher implements PathMatcher { @@ -453,11 +470,11 @@ public class AntPathMatcher implements PathMatcher {
return fileName2 + extension;
}
private String slashConcat(String path1, String path2) {
if (path1.endsWith("/") || path2.startsWith("/")) {
private String separatorConcat(String path1, String path2) {
if (path1.endsWith(this.pathSeparator) || path2.startsWith(this.pathSeparator)) {
return path1 + path2;
}
return path1 + "/" + path2;
return path1 + this.pathSeparator + path2;
}
/**

6
spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java

@ -606,4 +606,10 @@ public class AntPathMatcherTests { @@ -606,4 +606,10 @@ public class AntPathMatcherTests {
assertTrue(pathMatcher.stringMatcherCache.isEmpty());
}
@Test
public void noExtensionHandlingWithDotSeparator() {
pathMatcher.setPathSeparator(".");
assertEquals("/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*"));
}
}

41
spring-messaging/src/main/java/org/springframework/messaging/handler/DestinationPatternsMessageCondition.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -55,21 +55,35 @@ public final class DestinationPatternsMessageCondition @@ -55,21 +55,35 @@ public final class DestinationPatternsMessageCondition
* @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
*/
public DestinationPatternsMessageCondition(String... patterns) {
this(patterns, null);
this(patterns, null, true);
}
/**
* Additional constructor with flags for using suffix pattern (.*) and
* trailing slash matches.
* Additional constructor with a customized path matcher.
* @param patterns the URL patterns to use; if 0, the condition will match to every request.
* @param pathMatcher for path matching with patterns
* @param pathMatcher the customized path matcher to use with patterns
*/
public DestinationPatternsMessageCondition(String[] patterns,PathMatcher pathMatcher) {
this(asList(patterns), pathMatcher);
public DestinationPatternsMessageCondition(String[] patterns, PathMatcher pathMatcher) {
this(asList(patterns), pathMatcher, true);
}
private DestinationPatternsMessageCondition(Collection<String> patterns, PathMatcher pathMatcher) {
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
/**
* Additional constructor with a customized path matcher and a flag specifying if
* the destination patterns should be prepended with "/".
* @param patterns the URL patterns to use; if 0, the condition will match to every request.
* @param pathMatcher the customized path matcher to use with patterns
* @param prependLeadingSlash to specify whether each pattern that is not empty and does not
* start with "/" will be prepended with "/" or not
* @since 4.1
*/
public DestinationPatternsMessageCondition(String[] patterns, PathMatcher pathMatcher,
boolean prependLeadingSlash) {
this(asList(patterns), pathMatcher, prependLeadingSlash);
}
private DestinationPatternsMessageCondition(Collection<String> patterns,
PathMatcher pathMatcher, boolean prependLeadingSlash) {
this.patterns = Collections.unmodifiableSet(initializePatterns(patterns, prependLeadingSlash));
this.pathMatcher = (pathMatcher != null) ? pathMatcher : new AntPathMatcher();
}
@ -77,13 +91,14 @@ public final class DestinationPatternsMessageCondition @@ -77,13 +91,14 @@ public final class DestinationPatternsMessageCondition
return patterns != null ? Arrays.asList(patterns) : Collections.<String>emptyList();
}
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
private static Set<String> initializePatterns(Collection<String> patterns,
boolean prependLeadingSlash) {
if (patterns == null) {
return Collections.emptySet();
}
Set<String> result = new LinkedHashSet<String>(patterns.size());
for (String pattern : patterns) {
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/") && prependLeadingSlash) {
pattern = "/" + pattern;
}
result.add(pattern);
@ -134,7 +149,7 @@ public final class DestinationPatternsMessageCondition @@ -134,7 +149,7 @@ public final class DestinationPatternsMessageCondition
else {
result.add("");
}
return new DestinationPatternsMessageCondition(result, this.pathMatcher);
return new DestinationPatternsMessageCondition(result, this.pathMatcher, false);
}
/**
@ -169,7 +184,7 @@ public final class DestinationPatternsMessageCondition @@ -169,7 +184,7 @@ public final class DestinationPatternsMessageCondition
}
Collections.sort(matches, this.pathMatcher.getPatternComparator(destination));
return new DestinationPatternsMessageCondition(matches, this.pathMatcher);
return new DestinationPatternsMessageCondition(matches, this.pathMatcher, false);
}
/**

37
spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java

@ -52,7 +52,6 @@ import org.springframework.messaging.handler.invocation.AbstractExceptionHandler @@ -52,7 +52,6 @@ import org.springframework.messaging.handler.invocation.AbstractExceptionHandler
import org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.SimpAttributes;
import org.springframework.messaging.simp.SimpAttributesContextHolder;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageMappingInfo;
@ -94,7 +93,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -94,7 +93,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
private ConversionService conversionService = new DefaultFormattingConversionService();
private PathMatcher pathMatcher = new AntPathMatcher();
private PathMatcher pathMatcher;
private Validator validator;
@ -113,7 +112,22 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -113,7 +112,22 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
* @param brokerTemplate a messaging template to send application messages to the broker
*/
public SimpAnnotationMethodMessageHandler(SubscribableChannel clientInboundChannel,
MessageChannel clientOutboundChannel, SimpMessageSendingOperations brokerTemplate) {
MessageChannel clientOutboundChannel, SimpMessageSendingOperations brokerTemplate) {
this(clientInboundChannel, clientOutboundChannel, brokerTemplate, null);
}
/**
* Create an instance of SimpAnnotationMethodMessageHandler with the given
* message channels and broker messaging template.
* @param clientInboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param clientOutboundChannel the channel for messages to clients (e.g. WebSocket clients)
* @param brokerTemplate a messaging template to send application messages to the broker
* @param pathSeparator the path separator to use with the destination patterns
* @since 4.1
*/
public SimpAnnotationMethodMessageHandler(SubscribableChannel clientInboundChannel,
MessageChannel clientOutboundChannel, SimpMessageSendingOperations brokerTemplate,
String pathSeparator) {
Assert.notNull(clientInboundChannel, "clientInboundChannel must not be null");
Assert.notNull(clientOutboundChannel, "clientOutboundChannel must not be null");
@ -122,6 +136,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -122,6 +136,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
this.clientInboundChannel = clientInboundChannel;
this.clientMessagingTemplate = new SimpMessagingTemplate(clientOutboundChannel);
this.brokerTemplate = brokerTemplate;
this.pathMatcher = new AntPathMatcher(pathSeparator);
Collection<MessageConverter> converters = new ArrayList<MessageConverter>();
converters.add(new StringMessageConverter());
@ -318,31 +333,31 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -318,31 +333,31 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
MessageMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
MessageMapping messageAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
if (messageAnnot != null) {
SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot);
SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot, typeAnnotation == null);
if (typeAnnotation != null) {
result = createMessageMappingCondition(typeAnnotation).combine(result);
result = createMessageMappingCondition(typeAnnotation, false).combine(result);
}
return result;
}
SubscribeMapping subsribeAnnotation = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
if (subsribeAnnotation != null) {
SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnotation);
SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnotation, typeAnnotation == null);
if (typeAnnotation != null) {
result = createMessageMappingCondition(typeAnnotation).combine(result);
result = createMessageMappingCondition(typeAnnotation, false).combine(result);
}
return result;
}
return null;
}
private SimpMessageMappingInfo createMessageMappingCondition(MessageMapping annotation) {
private SimpMessageMappingInfo createMessageMappingCondition(MessageMapping annotation, boolean prependLeadingSlash) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE,
new DestinationPatternsMessageCondition(annotation.value()));
new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher, prependLeadingSlash));
}
private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation) {
private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation, boolean prependLeadingSlash) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.SUBSCRIBE,
new DestinationPatternsMessageCondition(annotation.value()));
new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher, prependLeadingSlash));
}
@Override

22
spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java

@ -29,6 +29,7 @@ import org.springframework.messaging.support.MessageHeaderAccessor; @@ -29,6 +29,7 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.messaging.support.MessageHeaderInitializer;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.PathMatcher;
/**
* A "simple" message broker that recognizes the message types defined in
@ -48,7 +49,7 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler { @@ -48,7 +49,7 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
private final SubscribableChannel brokerChannel;
private SubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
private SubscriptionRegistry subscriptionRegistry;
private MessageHeaderInitializer headerInitializer;
@ -63,6 +64,20 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler { @@ -63,6 +64,20 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
*/
public SimpleBrokerMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel,
SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) {
this(clientInboundChannel, clientOutboundChannel, brokerChannel, destinationPrefixes, null);
}
/**
* Additional constructor with a customized path matcher.
*
* @param clientInboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param clientOutboundChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker
* @param pathMatcher the path matcher to use
* @since 4.1
*/
public SimpleBrokerMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel,
SubscribableChannel brokerChannel, Collection<String> destinationPrefixes, PathMatcher pathMatcher) {
super(destinationPrefixes);
Assert.notNull(clientInboundChannel, "'clientInboundChannel' must not be null");
@ -71,6 +86,11 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler { @@ -71,6 +86,11 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
this.brokerChannel = brokerChannel;
DefaultSubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
if(pathMatcher != null) {
subscriptionRegistry.setPathMatcher(pathMatcher);
}
this.subscriptionRegistry = subscriptionRegistry;
}

5
spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractBrokerRegistration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -23,7 +23,6 @@ import java.util.List; @@ -23,7 +23,6 @@ import java.util.List;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler;
import org.springframework.util.Assert;
/**
@ -67,6 +66,4 @@ public abstract class AbstractBrokerRegistration { @@ -67,6 +66,4 @@ public abstract class AbstractBrokerRegistration {
return this.destinationPrefixes;
}
protected abstract AbstractBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel);
}

7
spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java

@ -39,6 +39,7 @@ import org.springframework.messaging.simp.user.UserSessionRegistry; @@ -39,6 +39,7 @@ import org.springframework.messaging.simp.user.UserSessionRegistry;
import org.springframework.messaging.support.AbstractSubscribableChannel;
import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.Errors;
@ -206,12 +207,16 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC @@ -206,12 +207,16 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
@Bean
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
String defaultSeparator = this.getBrokerRegistry().getDefaultSeparator();
SimpAnnotationMethodMessageHandler handler = new SimpAnnotationMethodMessageHandler(
clientInboundChannel(), clientOutboundChannel(), brokerMessagingTemplate());
clientInboundChannel(), clientOutboundChannel(), brokerMessagingTemplate(), defaultSeparator);
handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
handler.setMessageConverter(brokerMessageConverter());
handler.setValidator(simpValidator());
AntPathMatcher pathMatcher = new AntPathMatcher(defaultSeparator);
handler.setPathMatcher(pathMatcher);
return handler;
}

22
spring-messaging/src/main/java/org/springframework/messaging/simp/config/MessageBrokerRegistry.java

@ -23,12 +23,14 @@ import org.springframework.messaging.MessageChannel; @@ -23,12 +23,14 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
/**
* A registry for configuring message broker options.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public class MessageBrokerRegistry {
@ -47,6 +49,7 @@ public class MessageBrokerRegistry { @@ -47,6 +49,7 @@ public class MessageBrokerRegistry {
private ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
private String defaultSeparator;
public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
Assert.notNull(clientInboundChannel);
@ -119,12 +122,24 @@ public class MessageBrokerRegistry { @@ -119,12 +122,24 @@ public class MessageBrokerRegistry {
return this.brokerChannelRegistration;
}
/**
* Customize the default separator used for destination patterns matching/combining.
* It can be used to configure "." as the default separator, since it is used in most
* STOMP broker relay, enabling destination patterns like "/topic/PRICE.STOCK.**".
* <p>The default separator is "/".
*/
public MessageBrokerRegistry defaultSeparator(String defaultSeparator) {
this.defaultSeparator = defaultSeparator;
return this;
}
protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
if ((this.simpleBrokerRegistration == null) && (this.brokerRelayRegistration == null)) {
enableSimpleBroker();
}
if (this.simpleBrokerRegistration != null) {
return this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
AntPathMatcher pathMatcher = new AntPathMatcher(this.defaultSeparator);
return this.simpleBrokerRegistration.getMessageHandler(brokerChannel, pathMatcher);
}
return null;
}
@ -148,4 +163,9 @@ public class MessageBrokerRegistry { @@ -148,4 +163,9 @@ public class MessageBrokerRegistry {
protected ChannelRegistration getBrokerChannelRegistration() {
return this.brokerChannelRegistration;
}
protected String getDefaultSeparator() {
return this.defaultSeparator;
}
}

9
spring-messaging/src/main/java/org/springframework/messaging/simp/config/SimpleBrokerRegistration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -19,11 +19,13 @@ package org.springframework.messaging.simp.config; @@ -19,11 +19,13 @@ package org.springframework.messaging.simp.config;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
import org.springframework.util.PathMatcher;
/**
* Registration class for configuring a {@link SimpleBrokerMessageHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
@ -36,10 +38,9 @@ public class SimpleBrokerRegistration extends AbstractBrokerRegistration { @@ -36,10 +38,9 @@ public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
}
@Override
protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel) {
protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel, PathMatcher pathMatcher) {
return new SimpleBrokerMessageHandler(getClientInboundChannel(),
getClientOutboundChannel(), brokerChannel, getDestinationPrefixes());
getClientOutboundChannel(), brokerChannel, getDestinationPrefixes(), pathMatcher);
}
}

72
spring-messaging/src/test/java/org/springframework/messaging/handler/DestinationPatternsMessageConditionTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@ -18,18 +18,36 @@ package org.springframework.messaging.handler; @@ -18,18 +18,36 @@ package org.springframework.messaging.handler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized.Parameter;
import org.springframework.util.AntPathMatcher;
import java.util.Arrays;
import static org.junit.Assert.*;
/**
* Unit tests for {@link DestinationPatternsMessageCondition}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
@RunWith(Parameterized.class)
public class DestinationPatternsMessageConditionTests {
@Parameter(0)
public String pathSeparator;
@Parameters
public static Iterable<Object[]> arguments() {
return Arrays.asList(new Object[][]{{"/"}, {"."}});
}
@Test
public void prependSlash() {
DestinationPatternsMessageCondition c = condition("foo");
@ -47,20 +65,21 @@ public class DestinationPatternsMessageConditionTests { @@ -47,20 +65,21 @@ public class DestinationPatternsMessageConditionTests {
@Test
public void combineEmptySets() {
DestinationPatternsMessageCondition c1 = condition();
DestinationPatternsMessageCondition c2 = condition();
DestinationPatternsMessageCondition c2 = suffixCondition();
assertEquals(condition(""), c1.combine(c2));
}
@Test
public void combineOnePatternWithEmptySet() {
DestinationPatternsMessageCondition c1 = condition("/type1", "/type2");
DestinationPatternsMessageCondition c2 = condition();
DestinationPatternsMessageCondition c1 = condition("/type1",
pathSeparator + "type2");
DestinationPatternsMessageCondition c2 = suffixCondition();
assertEquals(condition("/type1", "/type2"), c1.combine(c2));
assertEquals(condition("/type1", pathSeparator + "type2"), c1.combine(c2));
c1 = condition();
c2 = condition("/method1", "/method2");
c2 = suffixCondition("/method1", "/method2");
assertEquals(condition("/method1", "/method2"), c1.combine(c2));
}
@ -68,10 +87,12 @@ public class DestinationPatternsMessageConditionTests { @@ -68,10 +87,12 @@ public class DestinationPatternsMessageConditionTests {
@Test
public void combineMultiplePatterns() {
DestinationPatternsMessageCondition c1 = condition("/t1", "/t2");
DestinationPatternsMessageCondition c2 = condition("/m1", "/m2");
DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "m1",
pathSeparator + "m2");
assertEquals(new DestinationPatternsMessageCondition(
"/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.combine(c2));
assertEquals(
condition("/t1" + pathSeparator + "m1", "/t1" + pathSeparator + "m2",
"/t2" + pathSeparator + "m1", "/t2" + pathSeparator + "m2"), c1.combine(c2));
}
@Test
@ -84,35 +105,40 @@ public class DestinationPatternsMessageConditionTests { @@ -84,35 +105,40 @@ public class DestinationPatternsMessageConditionTests {
@Test
public void matchPattern() {
DestinationPatternsMessageCondition condition = condition("/foo/*");
DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo/bar"));
DestinationPatternsMessageCondition condition = condition(
"/foo" + pathSeparator + "*");
DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo" + pathSeparator + "bar"));
assertNotNull(match);
}
@Test
public void matchSortPatterns() {
DestinationPatternsMessageCondition condition = condition("/**", "/foo/bar", "/foo/*");
DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo/bar"));
DestinationPatternsMessageCondition expected = condition("/foo/bar", "/foo/*", "/**");
DestinationPatternsMessageCondition condition = suffixCondition(
pathSeparator + "**", pathSeparator + "foo" + pathSeparator + "bar",
pathSeparator + "foo" + pathSeparator + "*");
DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo(pathSeparator + "foo" + pathSeparator + "bar"));
DestinationPatternsMessageCondition expected = suffixCondition(
pathSeparator + "foo" + pathSeparator + "bar",
pathSeparator + "foo" + pathSeparator + "*", pathSeparator + "**");
assertEquals(expected, match);
}
@Test
public void compareEqualPatterns() {
DestinationPatternsMessageCondition c1 = condition("/foo*");
DestinationPatternsMessageCondition c2 = condition("/foo*");
DestinationPatternsMessageCondition c1 = suffixCondition(pathSeparator + "foo*");
DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "foo*");
assertEquals(0, c1.compareTo(c2, messageTo("/foo")));
assertEquals(0, c1.compareTo(c2, messageTo(pathSeparator + "foo")));
}
@Test
public void comparePatternSpecificity() {
DestinationPatternsMessageCondition c1 = condition("/fo*");
DestinationPatternsMessageCondition c2 = condition("/foo");
DestinationPatternsMessageCondition c1 = suffixCondition(pathSeparator + "fo*");
DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "foo");
assertEquals(1, c1.compareTo(c2, messageTo("/foo")));
assertEquals(1, c1.compareTo(c2, messageTo(pathSeparator + "foo")));
}
@Test
@ -130,7 +156,11 @@ public class DestinationPatternsMessageConditionTests { @@ -130,7 +156,11 @@ public class DestinationPatternsMessageConditionTests {
private DestinationPatternsMessageCondition condition(String... patterns) {
return new DestinationPatternsMessageCondition(patterns);
return new DestinationPatternsMessageCondition(patterns, new AntPathMatcher(this.pathSeparator));
}
private DestinationPatternsMessageCondition suffixCondition(String... patterns) {
return new DestinationPatternsMessageCondition(patterns, new AntPathMatcher(this.pathSeparator), false);
}
private Message<?> messageTo(String destination) {

33
spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java

@ -27,6 +27,7 @@ import org.junit.Before; @@ -27,6 +27,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -39,6 +40,7 @@ import org.springframework.messaging.handler.annotation.SendTo; @@ -39,6 +40,7 @@ import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry;
import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
import org.springframework.messaging.simp.user.UserDestinationMessageHandler;
import org.springframework.messaging.simp.user.UserSessionRegistry;
@ -52,6 +54,7 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel; @@ -52,6 +54,7 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@ -64,6 +67,7 @@ import static org.junit.Assert.*; @@ -64,6 +67,7 @@ import static org.junit.Assert.*;
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @author Sebastien Deleuze
*/
public class MessageBrokerConfigurationTests {
@ -75,6 +79,8 @@ public class MessageBrokerConfigurationTests { @@ -75,6 +79,8 @@ public class MessageBrokerConfigurationTests {
private AnnotationConfigApplicationContext customChannelContext;
private AnnotationConfigApplicationContext customMatchingContext;
@Before
public void setupOnce() {
@ -94,6 +100,10 @@ public class MessageBrokerConfigurationTests { @@ -94,6 +100,10 @@ public class MessageBrokerConfigurationTests {
this.customChannelContext = new AnnotationConfigApplicationContext();
this.customChannelContext.register(CustomChannelConfig.class);
this.customChannelContext.refresh();
this.customMatchingContext = new AnnotationConfigApplicationContext();
this.customMatchingContext.register(CustomMatchingSimpleBrokerConfig.class);
this.customMatchingContext.refresh();
}
@ -396,6 +406,20 @@ public class MessageBrokerConfigurationTests { @@ -396,6 +406,20 @@ public class MessageBrokerConfigurationTests {
assertThat(messageHandler.getValidator(), Matchers.notNullValue(Validator.class));
}
@Test
public void customMatching() {
SimpleBrokerMessageHandler brokerHandler = this.customMatchingContext.getBean(SimpleBrokerMessageHandler.class);
DefaultSubscriptionRegistry subscriptionRegistry = (DefaultSubscriptionRegistry)brokerHandler.getSubscriptionRegistry();
AntPathMatcher pathMatcher = (AntPathMatcher)subscriptionRegistry.getPathMatcher();
DirectFieldAccessor accessor = new DirectFieldAccessor(pathMatcher);
assertEquals(".", accessor.getPropertyValue("pathSeparator"));
SimpAnnotationMethodMessageHandler messageHandler = customMatchingContext.getBean(SimpAnnotationMethodMessageHandler.class);
pathMatcher = (AntPathMatcher)messageHandler.getPathMatcher();
accessor = new DirectFieldAccessor(pathMatcher);
assertEquals(".", accessor.getPropertyValue("pathSeparator"));
}
@Controller
static class TestController {
@ -479,6 +503,15 @@ public class MessageBrokerConfigurationTests { @@ -479,6 +503,15 @@ public class MessageBrokerConfigurationTests {
}
}
@Configuration
static class CustomMatchingSimpleBrokerConfig extends SimpleBrokerConfig {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.defaultSeparator(".").enableSimpleBroker("/topic", "/queue");
}
}
private static class TestChannel extends ExecutorSubscribableChannel {

14
spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java

@ -21,10 +21,9 @@ import java.util.Collections; @@ -21,10 +21,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.messaging.simp.SimpSessionScope;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
@ -42,6 +41,7 @@ import org.springframework.messaging.converter.DefaultContentTypeResolver; @@ -42,6 +41,7 @@ import org.springframework.messaging.converter.DefaultContentTypeResolver;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.SimpSessionScope;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.messaging.simp.user.DefaultUserDestinationResolver;
import org.springframework.messaging.simp.user.DefaultUserSessionRegistry;
@ -50,6 +50,7 @@ import org.springframework.messaging.simp.user.UserDestinationMessageHandler; @@ -50,6 +50,7 @@ import org.springframework.messaging.simp.user.UserDestinationMessageHandler;
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeTypeUtils;
@ -329,6 +330,10 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser { @@ -329,6 +330,10 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
if (simpleBrokerElem != null) {
String prefix = simpleBrokerElem.getAttribute("prefix");
cavs.addIndexedArgumentValue(3, Arrays.asList(StringUtils.tokenizeToStringArray(prefix, ",")));
String defaultSeparator = messageBrokerElement.getAttribute("default-separator");
if (!defaultSeparator.isEmpty()) {
cavs.addIndexedArgumentValue(4, new AntPathMatcher(defaultSeparator));
}
brokerDef = new RootBeanDefinition(SimpleBrokerMessageHandler.class, cavs, null);
}
else if (brokerRelayElem != null) {
@ -452,6 +457,11 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser { @@ -452,6 +457,11 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
RootBeanDefinition annotationMethodMessageHandlerDef =
new RootBeanDefinition(SimpAnnotationMethodMessageHandler.class, cavs, mpvs);
String defaultSeparator = messageBrokerElement.getAttribute("default-separator");
if (!defaultSeparator.isEmpty()) {
annotationMethodMessageHandlerDef.getPropertyValues().add("pathMatcher", new AntPathMatcher(defaultSeparator));
}
registerBeanDef(annotationMethodMessageHandlerDef, parserCxt, source);
}

3
spring-websocket/src/main/resources/META-INF/spring.schemas

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
http\://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd=org/springframework/web/socket/config/spring-websocket-4.0.xsd
http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/config/spring-websocket-4.0.xsd
http\://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd=org/springframework/web/socket/config/spring-websocket-4.1.xsd
http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/config/spring-websocket-4.1.xsd

710
spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd

@ -0,0 +1,710 @@ @@ -0,0 +1,710 @@
<?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:annotation>
<xsd:documentation><![CDATA[
An entry in the registered HandlerMapping that matches a path with a handler.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="path" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
A path that maps a particular request to a handler.
Exact path mapping URIs (such as "/myPath") are supported as well as Ant-stype path patterns (such as /myPath/**).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="handler" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.WebSocketHandler"><![CDATA[
The bean name of a WebSocketHandler to use for requests that match the path configuration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="handshake-handler">
<xsd:attribute name="ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.server.HandshakeHandler"><![CDATA[
The bean name of a HandshakeHandler to use for processing WebSocket handshake requests.
If none specified, a DefaultHandshakeHandler will be configured by default.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="handshake-interceptors">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![CDATA[
A list of HandshakeInterceptor beans definition and references.
A HandshakeInterceptor can be used to inspect the handshake request and response as well as to pass attributes to the target WebSocketHandler.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice maxOccurs="unbounded">
<xsd:element ref="beans:bean">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![CDATA[
A HandshakeInterceptor bean definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="beans:ref">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![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:annotation>
<xsd:documentation source="org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService"><![CDATA[
Configures a DefaultSockJsService for processing HTTP requests from SockJS clients.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="transport-handlers" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.socket.sockjs.transport.TransportHandler"><![CDATA[
List of TransportHandler beans to be configured for the current handlers element.
One can choose not to register the default TransportHandlers and/or override those using
custom TransportHandlers.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice maxOccurs="unbounded">
<xsd:element ref="beans:bean">
<xsd:annotation>
<xsd:documentation><![CDATA[
A 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:annotation>
<xsd:documentation><![CDATA[
Whether or not default TransportHandlers registrations should be added in addition to the ones provided within this element.
Default registrations include XhrPollingTransportHandler, XhrReceivingTransportHandler,
JsonpPollingTransportHandler, JsonpReceivingTransportHandler, XhrStreamingTransportHandler,
EventSourceTransportHandler, HtmlFileTransportHandler, and WebSocketTransportHandler.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
A unique name for the service, mainly for logging purposes.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-library-url" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
Transports with no native cross-domain communication (e.g. "eventsource",
"htmlfile") must get a simple page from the "foreign" domain in an invisible
iframe so that code in the iframe can run from a domain local to the SockJS
server. Since the iframe needs to load the SockJS javascript client library,
this property allows specifying where to load it from.
By default this is set to point to
"https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js". However it can
also be set to point to a URL served by the application.
Note that it's possible to specify a relative URL in which case the URL
must be relative to the iframe URL. For example assuming a SockJS endpoint
mapped to "/sockjs", and resulting iframe URL "/sockjs/iframe.html", then the
The relative URL must start with "../../" to traverse up to the location
above the SockJS mapping. In case of a prefix-based Servlet mapping one more
traversal may be needed.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="stream-bytes-limit" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
Minimum number of bytes that can be send over a single HTTP streaming request before it will be closed.
Defaults to 128K (i.e. 128 1024).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-cookie-needed" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
The "cookie_needed" value in the response from the SockJs "/info" endpoint.
This property indicates whether the use of a JSESSIONID cookie is required for the application to function correctly,
e.g. for load balancing or in Java Servlet containers for the use of an HTTP session.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="heartbeat-time" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
The amount of time in milliseconds when the server has not sent any messages and after which the server
should send a heartbeat frame to the client in order to keep the connection from breaking.
The default value is 25,000 (25 seconds).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="disconnect-delay" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
The amount of time in milliseconds before a client is considered disconnected after not having
a receiving connection, i.e. an active connection over which the server can send data to the client.
The default value is 5000.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-cache-size" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
The number of server-to-client messages that a session can cache while waiting for
the next HTTP polling request from the client.
The default size is 100.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="websocket-enabled" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
Some load balancers don't support websockets. Set this option to "false" to disable the WebSocket transport on the server side.
The default value is "true".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="scheduler" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
The bean name of a TaskScheduler; a new ThreadPoolTaskScheduler instance will be created if no value is provided.
This scheduler instance will be used for scheduling heart-beat messages.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="stomp-broker-relay">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
Configures a MessageHandler that handles messages by forwarding them to a STOMP broker.
This MessageHandler also opens a default "system" TCP connection to the message
broker that is used for sending messages that originate from the server application (as
opposed to from a client).
The "login", "password", "heartbeat-send-interval" and "heartbeat-receive-interval" attributes
are provided to configure this "system" connection.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="prefix" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
Comma-separated list of destination prefixes supported by the broker being configured.
Destinations that do not match the given prefix(es) are ignored.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="relay-host" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The STOMP message broker host.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="relay-port" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The STOMP message broker port.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-login" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The login to use when creating connections to the STOMP broker on behalf of connected clients.
By default this is set to "guest".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-passcode" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The passcode to use when creating connections to the STOMP broker on behalf of connected clients.
By default this is set to "guest".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="system-login" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The login for the shared "system" connection used to send messages to
the STOMP broker from within the application, i.e. messages not associated
with a specific client session (e.g. REST/HTTP request handling method).
By default this is set to "guest".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="system-passcode" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The passcode for the shared "system" connection used to send messages to
the STOMP broker from within the application, i.e. messages not associated
with a specific client session (e.g. REST/HTTP request handling method).
By default this is set to "guest".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="heartbeat-send-interval" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The interval, in milliseconds, at which the "system" connection will send heartbeats to the STOMP broker.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="heartbeat-receive-interval" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The interval, in milliseconds, at which the "system" connection expects to receive heartbeats from the STOMP broker.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
Whether or not the StompBrokerRelay should be automatically started as part of its SmartLifecycle,
i.e. at the time of an application context refresh.
Default value is "true".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="virtual-host" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
The value of the "host" header to use in STOMP CONNECT frames sent to the STOMP broker.
This may be useful for example in a cloud environment where the actual host to which
the TCP connection is established is different from the host providing the cloud-based STOMP service.
By default this property is not set.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="simple-broker">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler"><![CDATA[
Configures a SimpleBrokerMessageHandler that handles messages as a simple message broker implementation.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="prefix" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.simp.stomp.SimpleBrokerMessageHandler"><![CDATA[
Comma-separated list of destination prefixes supported by the broker being configured.
Destinations that do not match the given prefix(es) are ignored.
]]></xsd:documentation>
</xsd:annotation>
</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:annotation>
<xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
Configuration for the ThreadPoolTaskExecutor that sends messages for the message channel.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="core-pool-size" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
Set the core pool size of the ThreadPoolExecutor.
NOTE: the core pool size is effectively the max pool size when an unbounded queue-capacity is configured (the default).
This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
When this strategy is used, the max pool size is effectively ignored.
By default this is set to twice the value of Runtime.availableProcessors().
In an an application where tasks do not block frequently,
the number should be closer to or equal to the number of available CPUs/cores.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-pool-size" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
Set the max pool size of the ThreadPoolExecutor.
NOTE: when an unbounded queue-capacity is configured (the default), the max pool size is effectively ignored.
See the "Unbounded queues" strategy in java.util.concurrent.ThreadPoolExecutor for more details.
By default this is set to Integer.MAX_VALUE.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keep-alive-seconds" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
Set the time limit for which threads may remain idle before being terminated.
If there are more than the core number of threads currently in the pool, after waiting this amount of time without
processing a task, excess threads will be terminated. This overrides any value set in the constructor.
By default this is set to 60.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="queue-capacity" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
Set the queue capacity for the ThreadPoolExecutor.
NOTE: when an unbounded queue-capacity is configured (the default) the core pool size is effectively the max pool size.
This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
When this strategy is used, the max pool size is effectively ignored.
By default this is set to Integer.MAX_VALUE.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="channel-interceptors">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.messaging.support.ChannelInterceptor"><![CDATA[
List of ChannelInterceptor beans to be used with this channel.
Empty by default.
]]></xsd:documentation>
</xsd:annotation>
<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[
Configures WebSocket support by registering a SimpleUrlHandlerMapping and mapping
paths to registered WebSocketHandlers.
If a sockjs service is configured within this element, then a
SockJsHttpRequestHandler will handle
requests mapped to the given path.
Otherwise a WebSocketHttpRequestHandler
will be registered for that purpose.
See EnableWebSocket Javadoc for
information on code-based alternatives to enabling WebSocket support.
]]></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:token">
<xsd:annotation>
<xsd:documentation><![CDATA[
Order value for this SimpleUrlHandlerMapping.
Default value is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="message-broker">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configures broker-backed messaging over WebSocket using a higher-level messaging sub-protocol.
Registers a SimpleUrlHandlerMapping and maps paths to registered Controllers.
A StompSubProtocolHandler is registered to handle various versions of the STOMP protocol.
See EnableWebSocketMessageBroker javadoc for information on code-based alternatives to enabling broker-backed messaging.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="transport" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure options related to the processing of messages received from and sent to WebSocket clients.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="message-size" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure the maximum size for an incoming sub-protocol message.
For example a STOMP message may be received as multiple WebSocket messages
or multiple HTTP POST requests when SockJS fallback options are in use.
In theory a WebSocket message can be almost unlimited in size.
In practice WebSocket servers impose limits on incoming message size.
STOMP clients for example tend to split large messages around 16K
boundaries. Therefore a server must be able to buffer partial content
and decode when enough data is received. Use this property to configure
the max size of the buffer to use.
The default value is 64K (i.e. 64 * 1024).
NOTE that the current version 1.2 of the STOMP spec
does not specifically discuss how to send STOMP messages over WebSocket.
Version 2 of the spec will but in the mean time existing client libraries
have already established a practice that servers must handle.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure a time limit (in milliseconds) for the maximum amount of a time
allowed when sending messages to a WebSocket session or writing to an
HTTP response when SockJS fallback option are in use.
In general WebSocket servers expect that messages to a single WebSocket
session are sent from a single thread at a time. This is automatically
guaranteed when using {@code @EnableWebSocketMessageBroker} configuration.
If message sending is slow, or at least slower than rate of messages sending,
subsequent messages are buffered until either the {@code sendTimeLimit}
or the {@code sendBufferSizeLimit} are reached at which point the session
state is cleared and an attempt is made to close the session.
NOTE that the session time limit is checked only
on attempts to send additional messages. So if only a single message is
sent and it hangs, the session will not time out until another message is
sent or the underlying physical socket times out. So this is not a
replacement for WebSocket server or HTTP connection timeout but is rather
intended to control the extent of buffering of unsent messages.
NOTE that closing the session may not succeed in
actually closing the physical socket and may also hang. This is true
especially when using blocking IO such as the BIO connector in Tomcat
that is used by default on Tomcat 7. Therefore it is recommended to ensure
the server is using non-blocking IO such as Tomcat's NIO connector that
is used by default on Tomcat 8. If you must use blocking IO consider
customizing OS-level TCP settings, for example
{@code /proc/sys/net/ipv4/tcp_retries2} on Linux.
The default value is 10 seconds (i.e. 10 * 10000).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-buffer-size" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure the maximum amount of data to buffer when sending messages
to a WebSocket session, or an HTTP response when SockJS fallback
option are in use.
In general WebSocket servers expect that messages to a single WebSocket
session are sent from a single thread at a time. This is automatically
guaranteed when using {@code @EnableWebSocketMessageBroker} configuration.
If message sending is slow, or at least slower than rate of messages sending,
subsequent messages are buffered until either the {@code sendTimeLimit}
or the {@code sendBufferSizeLimit} are reached at which point the session
state is cleared and an attempt is made to close the session.
NOTE that closing the session may not succeed in
actually closing the physical socket and may also hang. This is true
especially when using blocking IO such as the BIO connector in Tomcat
configured by default on Tomcat 7. Therefore it is recommended to ensure
the server is using non-blocking IO such as Tomcat's NIO connector used
by default on Tomcat 8. If you must use blocking IO consider customizing
OS-level TCP settings, for example {@code /proc/sys/net/ipv4/tcp_retries2}
on Linux.
The default value is 512K (i.e. 512 * 1024).
@param sendBufferSizeLimit the maximum number of bytes to buffer when
sending messages; if the value is less than or equal to 0 then buffering
is effectively disabled.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="stomp-endpoint" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Registers STOMP over WebSocket endpoints.
]]></xsd:documentation>
</xsd:annotation>
<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:annotation>
<xsd:documentation><![CDATA[
A path that maps a particular message destination to a handler method.
Exact path mapping URIs (such as "/myPath") are supported as well as Ant-stype path patterns (such as /myPath/**).
]]></xsd:documentation>
</xsd:annotation>
</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="message-converters" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure the message converters to use when extracting the payload of messages in annotated methods
and when sending messages (e.g. through the "broker" SimpMessagingTemplate.
MessageConverter registrations provided here will take precedence over MessageConverter types registered by default.
Also see the register-defaults attribute if you want to turn off default registrations entirely.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice maxOccurs="unbounded">
<xsd:element ref="beans:bean">
<xsd:annotation>
<xsd:documentation><![CDATA[
A MessageConverter bean definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="beans:ref">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an HttpMessageConverter bean.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="register-defaults" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether or not default MessageConverter registrations should be added in addition to the ones provided within this element.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="client-inbound-channel" type="channel" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The channel for receiving messages from clients (e.g. WebSocket clients).
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="client-outbound-channel" type="channel" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The channel for sending messages to clients (e.g. WebSocket clients).
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="broker-channel" type="channel" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The channel for sending messages with translated user destinations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="application-destination-prefix" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-separated list of prefixes to match to the destinations of handled messages.
Messages whose destination does not start with one of the configured prefixes are ignored.
Prefix is removed from the destination part and then messages are delegated to
@SubscribeMapping and @MessageMapping}annotated methods.
Prefixes without a trailing slash will have one appended automatically.
By default the list of prefixes is empty in which case all destinations match.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="user-destination-prefix" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The prefix used to identify user destinations.
Any destinations that do not start with the given prefix are not be resolved.
The default value is "/user/".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-separator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Customize the default separator used for destination patterns matching/combining.
It could be used to configure "." as the default separator, since it is used in most
STOMP broker relay, enabling destination patterns like "/topic/PRICE.STOCK.**".
The default separator is "/".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:token">
<xsd:annotation>
<xsd:documentation><![CDATA[
Order value for this SimpleUrlHandlerMapping.
Default value is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>

8
spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java

@ -182,6 +182,10 @@ public class MessageBrokerBeanDefinitionParserTests { @@ -182,6 +182,10 @@ public class MessageBrokerBeanDefinitionParserTests {
}
assertNotNull(this.appContext.getBean("webSocketScopeConfigurer", CustomScopeConfigurer.class));
DirectFieldAccessor subscriptionRegistryAccessor = new DirectFieldAccessor(brokerMessageHandler.getSubscriptionRegistry());
String pathSeparator = (String)new DirectFieldAccessor(subscriptionRegistryAccessor.getPropertyValue("pathMatcher")).getPropertyValue("pathSeparator");
assertEquals(".", pathSeparator);
}
@Test
@ -285,6 +289,10 @@ public class MessageBrokerBeanDefinitionParserTests { @@ -285,6 +289,10 @@ public class MessageBrokerBeanDefinitionParserTests {
ContentTypeResolver resolver = ((MappingJackson2MessageConverter) converters.get(2)).getContentTypeResolver();
assertEquals(MimeTypeUtils.APPLICATION_JSON, ((DefaultContentTypeResolver) resolver).getDefaultMimeType());
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(annotationMethodMessageHandler);
String pathSeparator = (String)new DirectFieldAccessor(handlerAccessor.getPropertyValue("pathMatcher")).getPropertyValue("pathSeparator");
assertEquals(".", pathSeparator);
}
@Test

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-converters-defaults-off.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-converters.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels-default-executor.xml

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker>
<websocket:stomp-endpoint path="/foo"/>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal">
<websocket:stomp-endpoint path="/foo,/bar">

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-relay.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker order="2">
<websocket:stomp-endpoint path="/foo">

4
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-simple.xml

@ -2,9 +2,9 @@ @@ -2,9 +2,9 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal">
<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal" default-separator=".">
<!-- message-size=128*1024, send-buffer-size=1024*1024 -->
<websocket:transport message-size="131072" send-timeout="25000" send-buffer-size="1048576" />

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-attributes.xml

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:handlers order="2">
<websocket:mapping path="/foo" handler="fooHandler"/>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-sockjs-attributes.xml

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:handlers >
<websocket:mapping path="/test" handler="testHandler"/>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers-sockjs.xml

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:handlers>
<websocket:mapping path="/test" handler="testHandler"/>

2
spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-handlers.xml

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
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">
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">
<websocket:handlers>
<websocket:mapping path="/foo,/bar" handler="fooHandler"/>

Loading…
Cancel
Save