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
This commit is contained in:
committed by
Rossen Stoyanchev
parent
b676c41805
commit
928a466b5d
@@ -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 {
|
||||
|
||||
// /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 {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -606,4 +606,10 @@ public class AntPathMatcherTests {
|
||||
assertTrue(pathMatcher.stringMatcherCache.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noExtensionHandlingWithDotSeparator() {
|
||||
pathMatcher.setPathSeparator(".");
|
||||
assertEquals("/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* @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
|
||||
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
|
||||
else {
|
||||
result.add("");
|
||||
}
|
||||
return new DestinationPatternsMessageCondition(result, this.pathMatcher);
|
||||
return new DestinationPatternsMessageCondition(result, this.pathMatcher, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
* @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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
private final SubscribableChannel brokerChannel;
|
||||
|
||||
private SubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
|
||||
private SubscriptionRegistry subscriptionRegistry;
|
||||
|
||||
private MessageHeaderInitializer headerInitializer;
|
||||
|
||||
@@ -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 {
|
||||
this.clientInboundChannel = clientInboundChannel;
|
||||
this.clientOutboundChannel = clientOutboundChannel;
|
||||
this.brokerChannel = brokerChannel;
|
||||
DefaultSubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
|
||||
if(pathMatcher != null) {
|
||||
subscriptionRegistry.setPathMatcher(pathMatcher);
|
||||
}
|
||||
this.subscriptionRegistry = subscriptionRegistry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
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 {
|
||||
return this.destinationPrefixes;
|
||||
}
|
||||
|
||||
protected abstract AbstractBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel);
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
private ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
|
||||
|
||||
private String defaultSeparator;
|
||||
|
||||
public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
|
||||
Assert.notNull(clientInboundChannel);
|
||||
@@ -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 {
|
||||
protected ChannelRegistration getBrokerChannelRegistration() {
|
||||
return this.brokerChannelRegistration;
|
||||
}
|
||||
|
||||
protected String getDefaultSeparator() {
|
||||
return this.defaultSeparator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel) {
|
||||
protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel, PathMatcher pathMatcher) {
|
||||
return new SimpleBrokerMessageHandler(getClientInboundChannel(),
|
||||
getClientOutboundChannel(), brokerChannel, getDestinationPrefixes());
|
||||
getClientOutboundChannel(), brokerChannel, getDestinationPrefixes(), pathMatcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
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 {
|
||||
@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 {
|
||||
@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 {
|
||||
|
||||
@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 {
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
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.*;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomMatchingSimpleBrokerConfig extends SimpleBrokerConfig {
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.defaultSeparator(".").enableSimpleBroker("/topic", "/queue");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TestChannel extends ExecutorSubscribableChannel {
|
||||
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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 {
|
||||
|
||||
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,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,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>
|
||||
|
||||
|
||||
@@ -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,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,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">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user