Update support for using "." as path separator
Issue: SPR-11660
This commit is contained in:
		
							parent
							
								
									928a466b5d
								
							
						
					
					
						commit
						ab2526a586
					
				| 
						 | 
					@ -68,23 +68,27 @@ public class AntPathMatcher implements PathMatcher {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
 | 
						final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private PathSeparatorPatternCache pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Create a new AntPathMatcher with the default Ant path separator "/".
 | 
						 * Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public AntPathMatcher() {
 | 
						public AntPathMatcher() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Create a new AntPathMatcher.
 | 
						 * A convenience alternative constructor to use with a custom path separator.
 | 
				
			||||||
	 * @param pathSeparator the path separator to use
 | 
						 * @param pathSeparator the path separator to use, must not be {@code null}.
 | 
				
			||||||
	 * @since 4.1
 | 
						 * @since 4.1
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public AntPathMatcher(String pathSeparator) {
 | 
						public AntPathMatcher(String pathSeparator) {
 | 
				
			||||||
		if(pathSeparator != null) {
 | 
							Assert.notNull(pathSeparator, "'pathSeparator' is required");
 | 
				
			||||||
		this.pathSeparator = pathSeparator;
 | 
							this.pathSeparator = pathSeparator;
 | 
				
			||||||
 | 
							this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Set the path separator to use for pattern parsing.
 | 
						 * Set the path separator to use for pattern parsing.
 | 
				
			||||||
| 
						 | 
					@ -92,6 +96,7 @@ public class AntPathMatcher implements PathMatcher {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void setPathSeparator(String pathSeparator) {
 | 
						public void setPathSeparator(String pathSeparator) {
 | 
				
			||||||
		this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
 | 
							this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
 | 
				
			||||||
 | 
							this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -447,20 +452,20 @@ public class AntPathMatcher implements PathMatcher {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// /hotels/* + /booking -> /hotels/booking
 | 
							// /hotels/* + /booking -> /hotels/booking
 | 
				
			||||||
		// /hotels/* + booking -> /hotels/booking
 | 
							// /hotels/* + booking -> /hotels/booking
 | 
				
			||||||
		if (pattern1.endsWith(this.pathSeparator + "*")) {
 | 
							if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) {
 | 
				
			||||||
			return separatorConcat(pattern1.substring(0, pattern1.length() - 2), pattern2);
 | 
								return concat(pattern1.substring(0, pattern1.length() - 2), pattern2);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// /hotels/** + /booking -> /hotels/**/booking
 | 
							// /hotels/** + /booking -> /hotels/**/booking
 | 
				
			||||||
		// /hotels/** + booking -> /hotels/**/booking
 | 
							// /hotels/** + booking -> /hotels/**/booking
 | 
				
			||||||
		if (pattern1.endsWith(this.pathSeparator + "**")) {
 | 
							if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) {
 | 
				
			||||||
			return separatorConcat(pattern1, pattern2);
 | 
								return concat(pattern1, pattern2);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int starDotPos1 = pattern1.indexOf("*.");
 | 
							int starDotPos1 = pattern1.indexOf("*.");
 | 
				
			||||||
		if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
 | 
							if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
 | 
				
			||||||
			// simply concatenate the two patterns
 | 
								// simply concatenate the two patterns
 | 
				
			||||||
			return separatorConcat(pattern1, pattern2);
 | 
								return concat(pattern1, pattern2);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		String extension1 = pattern1.substring(starDotPos1 + 1);
 | 
							String extension1 = pattern1.substring(starDotPos1 + 1);
 | 
				
			||||||
		int dotPos2 = pattern2.indexOf('.');
 | 
							int dotPos2 = pattern2.indexOf('.');
 | 
				
			||||||
| 
						 | 
					@ -470,7 +475,7 @@ public class AntPathMatcher implements PathMatcher {
 | 
				
			||||||
		return fileName2 + extension;
 | 
							return fileName2 + extension;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private String separatorConcat(String path1, String path2) {
 | 
						private String concat(String path1, String path2) {
 | 
				
			||||||
		if (path1.endsWith(this.pathSeparator) || path2.startsWith(this.pathSeparator)) {
 | 
							if (path1.endsWith(this.pathSeparator) || path2.startsWith(this.pathSeparator)) {
 | 
				
			||||||
			return path1 + path2;
 | 
								return path1 + path2;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -763,4 +768,29 @@ public class AntPathMatcher implements PathMatcher {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * A simple cache for patterns that depend on the configured path separator.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private static class PathSeparatorPatternCache {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private final String endsOnWildCard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private final String endsOnDoubleWildCard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private PathSeparatorPatternCache(String pathSeparator) {
 | 
				
			||||||
 | 
								this.endsOnWildCard = pathSeparator + "*";
 | 
				
			||||||
 | 
								this.endsOnDoubleWildCard = pathSeparator + "**";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public String getEndsOnWildCard() {
 | 
				
			||||||
 | 
								return this.endsOnWildCard;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public String getEndsOnDoubleWildCard() {
 | 
				
			||||||
 | 
								return this.endsOnDoubleWildCard;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -607,9 +607,10 @@ public class AntPathMatcherTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void noExtensionHandlingWithDotSeparator() {
 | 
						public void testExtensionMappingWithDotPathSeparator() {
 | 
				
			||||||
		pathMatcher.setPathSeparator(".");
 | 
							pathMatcher.setPathSeparator(".");
 | 
				
			||||||
		assertEquals("/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*"));
 | 
							assertEquals("Extension mapping should be disabled with \".\" as path separator",
 | 
				
			||||||
 | 
									"/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ import java.util.Arrays;
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.Comparator;
 | 
					import java.util.Comparator;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Iterator;
 | 
					import java.util.Iterator;
 | 
				
			||||||
import java.util.LinkedHashSet;
 | 
					import java.util.LinkedHashSet;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
| 
						 | 
					@ -55,52 +56,39 @@ public final class DestinationPatternsMessageCondition
 | 
				
			||||||
	 * @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
 | 
						 * @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public DestinationPatternsMessageCondition(String... patterns) {
 | 
						public DestinationPatternsMessageCondition(String... patterns) {
 | 
				
			||||||
		this(patterns, null, true);
 | 
							this(patterns, null);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Additional constructor with a customized path matcher.
 | 
						 * Alternative constructor accepting a custom PathMatcher.
 | 
				
			||||||
	 * @param patterns the URL patterns to use; if 0, the condition will match to every request.
 | 
						 * @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 pathMatcher the PathMatcher to use.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public DestinationPatternsMessageCondition(String[] patterns, PathMatcher pathMatcher) {
 | 
						public DestinationPatternsMessageCondition(String[] patterns, PathMatcher pathMatcher) {
 | 
				
			||||||
		this(asList(patterns), pathMatcher, true);
 | 
							this(asList(patterns), pathMatcher);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						private DestinationPatternsMessageCondition(Collection<String> patterns, PathMatcher pathMatcher) {
 | 
				
			||||||
	 * 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();
 | 
							this.pathMatcher = (pathMatcher != null) ? pathMatcher : new AntPathMatcher();
 | 
				
			||||||
 | 
							this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns, this.pathMatcher));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static List<String> asList(String... patterns) {
 | 
						private static List<String> asList(String... patterns) {
 | 
				
			||||||
		return patterns != null ? Arrays.asList(patterns) : Collections.<String>emptyList();
 | 
							return patterns != null ? Arrays.asList(patterns) : Collections.<String>emptyList();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static Set<String> initializePatterns(Collection<String> patterns,
 | 
						private static Set<String> prependLeadingSlash(Collection<String> patterns, PathMatcher pathMatcher) {
 | 
				
			||||||
			boolean prependLeadingSlash) {
 | 
					 | 
				
			||||||
		if (patterns == null) {
 | 
							if (patterns == null) {
 | 
				
			||||||
			return Collections.emptySet();
 | 
								return Collections.emptySet();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							boolean slashSeparator = pathMatcher.combine("a", "a").equals("a/a");
 | 
				
			||||||
		Set<String> result = new LinkedHashSet<String>(patterns.size());
 | 
							Set<String> result = new LinkedHashSet<String>(patterns.size());
 | 
				
			||||||
		for (String pattern : patterns) {
 | 
							for (String pattern : patterns) {
 | 
				
			||||||
			if (StringUtils.hasLength(pattern) && !pattern.startsWith("/") && prependLeadingSlash) {
 | 
								if (slashSeparator) {
 | 
				
			||||||
 | 
									if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
 | 
				
			||||||
					pattern = "/" + pattern;
 | 
										pattern = "/" + pattern;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			result.add(pattern);
 | 
								result.add(pattern);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
| 
						 | 
					@ -149,7 +137,7 @@ public final class DestinationPatternsMessageCondition
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
			result.add("");
 | 
								result.add("");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return new DestinationPatternsMessageCondition(result, this.pathMatcher, false);
 | 
							return new DestinationPatternsMessageCondition(result, this.pathMatcher);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -184,7 +172,7 @@ public final class DestinationPatternsMessageCondition
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Collections.sort(matches, this.pathMatcher.getPatternComparator(destination));
 | 
							Collections.sort(matches, this.pathMatcher.getPatternComparator(destination));
 | 
				
			||||||
		return new DestinationPatternsMessageCondition(matches, this.pathMatcher, false);
 | 
							return new DestinationPatternsMessageCondition(matches, this.pathMatcher);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,27 +90,27 @@ public abstract class AbstractMethodMessageHandler<T>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Configure one or more prefixes to match to the destinations of handled messages.
 | 
						 * When this property is configured only messages to destinations matching
 | 
				
			||||||
	 * Messages whose destination does not start with one of the configured prefixes
 | 
						 * one of the configured prefixes are eligible for handling. When there is a
 | 
				
			||||||
	 * are ignored. When a destination matches one of the configured prefixes, the
 | 
						 * match the prefix is removed and only the remaining part of the destination
 | 
				
			||||||
	 * matching part is removed from destination before performing a lookup for a matching
 | 
						 * is used for method-mapping purposes.
 | 
				
			||||||
	 * message handling method. Prefixes without a trailing slash will have one appended
 | 
						 *
 | 
				
			||||||
	 * automatically.
 | 
						 * <p>By default no prefixes are configured in which case all messages are
 | 
				
			||||||
	 * <p>By default the list of prefixes is empty in which case all destinations match.
 | 
						 * eligible for handling.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void setDestinationPrefixes(Collection<String> prefixes) {
 | 
						public void setDestinationPrefixes(Collection<String> prefixes) {
 | 
				
			||||||
		this.destinationPrefixes.clear();
 | 
							this.destinationPrefixes.clear();
 | 
				
			||||||
		if (prefixes != null) {
 | 
							if (prefixes != null) {
 | 
				
			||||||
			for (String prefix : prefixes) {
 | 
								for (String prefix : prefixes) {
 | 
				
			||||||
				prefix = prefix.trim();
 | 
									prefix = prefix.trim();
 | 
				
			||||||
				if (!prefix.endsWith("/")) {
 | 
					 | 
				
			||||||
					prefix += "/";
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.destinationPrefixes.add(prefix);
 | 
									this.destinationPrefixes.add(prefix);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Return the configured destination prefixes.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Collection<String> getDestinationPrefixes() {
 | 
						public Collection<String> getDestinationPrefixes() {
 | 
				
			||||||
		return this.destinationPrefixes;
 | 
							return this.destinationPrefixes;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -346,11 +346,11 @@ public abstract class AbstractMethodMessageHandler<T>
 | 
				
			||||||
	protected abstract String getDestination(Message<?> message);
 | 
						protected abstract String getDestination(Message<?> message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Find if the given destination matches any of the configured allowed destination
 | 
						 * Check whether the given destination (of an incoming message) matches to
 | 
				
			||||||
	 * prefixes and if a match is found return the destination with the prefix removed.
 | 
						 * one of the configured destination prefixes and if so return the remaining
 | 
				
			||||||
	 * <p>If no destination prefixes are configured, the destination is returned as is.
 | 
						 * portion of the destination after the matched prefix.
 | 
				
			||||||
	 * @return the destination to use to find matching message handling methods
 | 
						 * <p>If there are no matching prefixes, return {@code null}.
 | 
				
			||||||
	 * or {@code null} if the destination does not match
 | 
						 * <p>If there are no destination prefixes, return the destination as is.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected String getLookupDestination(String destination) {
 | 
						protected String getLookupDestination(String destination) {
 | 
				
			||||||
		if (destination == null) {
 | 
							if (destination == null) {
 | 
				
			||||||
| 
						 | 
					@ -361,7 +361,7 @@ public abstract class AbstractMethodMessageHandler<T>
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		for (String prefix : this.destinationPrefixes) {
 | 
							for (String prefix : this.destinationPrefixes) {
 | 
				
			||||||
			if (destination.startsWith(prefix)) {
 | 
								if (destination.startsWith(prefix)) {
 | 
				
			||||||
				return destination.substring(prefix.length() - 1);
 | 
									return destination.substring(prefix.length());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,7 +93,9 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private ConversionService conversionService = new DefaultFormattingConversionService();
 | 
						private ConversionService conversionService = new DefaultFormattingConversionService();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private PathMatcher pathMatcher;
 | 
						private PathMatcher pathMatcher = new AntPathMatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private boolean slashPathSeparator = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Validator validator;
 | 
						private Validator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,21 +115,6 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public SimpAnnotationMethodMessageHandler(SubscribableChannel clientInboundChannel,
 | 
						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(clientInboundChannel, "clientInboundChannel must not be null");
 | 
				
			||||||
		Assert.notNull(clientOutboundChannel, "clientOutboundChannel must not be null");
 | 
							Assert.notNull(clientOutboundChannel, "clientOutboundChannel must not be null");
 | 
				
			||||||
| 
						 | 
					@ -136,7 +123,6 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
		this.clientInboundChannel = clientInboundChannel;
 | 
							this.clientInboundChannel = clientInboundChannel;
 | 
				
			||||||
		this.clientMessagingTemplate = new SimpMessagingTemplate(clientOutboundChannel);
 | 
							this.clientMessagingTemplate = new SimpMessagingTemplate(clientOutboundChannel);
 | 
				
			||||||
		this.brokerTemplate = brokerTemplate;
 | 
							this.brokerTemplate = brokerTemplate;
 | 
				
			||||||
		this.pathMatcher = new AntPathMatcher(pathSeparator);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Collection<MessageConverter> converters = new ArrayList<MessageConverter>();
 | 
							Collection<MessageConverter> converters = new ArrayList<MessageConverter>();
 | 
				
			||||||
		converters.add(new StringMessageConverter());
 | 
							converters.add(new StringMessageConverter());
 | 
				
			||||||
| 
						 | 
					@ -144,6 +130,36 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
		this.messageConverter = new CompositeMessageConverter(converters);
 | 
							this.messageConverter = new CompositeMessageConverter(converters);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * {@inheritDoc}
 | 
				
			||||||
 | 
						 * <p>Destination prefixes are expected to be slash-separated Strings and
 | 
				
			||||||
 | 
						 * therefore a slash is automatically appended where missing to ensure a
 | 
				
			||||||
 | 
						 * proper prefix-based match (i.e. matching complete segments).
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * <p>Note however that the remaining portion of a destination after the
 | 
				
			||||||
 | 
						 * prefix may use a different separator (e.g. commonly "." in messaging)
 | 
				
			||||||
 | 
						 * depending on the configured {@code PathMatcher}.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void setDestinationPrefixes(Collection<String> prefixes) {
 | 
				
			||||||
 | 
							super.setDestinationPrefixes(appendSlashes(prefixes));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static Collection<String> appendSlashes(Collection<String> prefixes) {
 | 
				
			||||||
 | 
							if (CollectionUtils.isEmpty(prefixes)) {
 | 
				
			||||||
 | 
								return prefixes;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Collection<String> result = new ArrayList<String>(prefixes.size());
 | 
				
			||||||
 | 
							for (String prefix : prefixes) {
 | 
				
			||||||
 | 
								if (!prefix.endsWith("/")) {
 | 
				
			||||||
 | 
									prefix = prefix + "/";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								result.add(prefix);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Configure a {@link MessageConverter} to use to convert the payload of a message
 | 
						 * Configure a {@link MessageConverter} to use to convert the payload of a message
 | 
				
			||||||
	 * from serialize form with a specific MIME type to an Object matching the target
 | 
						 * from serialize form with a specific MIME type to an Object matching the target
 | 
				
			||||||
| 
						 | 
					@ -189,6 +205,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
	public void setPathMatcher(PathMatcher pathMatcher) {
 | 
						public void setPathMatcher(PathMatcher pathMatcher) {
 | 
				
			||||||
		Assert.notNull(pathMatcher, "PathMatcher must not be null");
 | 
							Assert.notNull(pathMatcher, "PathMatcher must not be null");
 | 
				
			||||||
		this.pathMatcher = pathMatcher;
 | 
							this.pathMatcher = pathMatcher;
 | 
				
			||||||
 | 
							this.slashPathSeparator = this.pathMatcher.combine("a", "a").equals("a/a");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -333,31 +350,31 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
		MessageMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
 | 
							MessageMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
 | 
				
			||||||
		MessageMapping messageAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
 | 
							MessageMapping messageAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
 | 
				
			||||||
		if (messageAnnot != null) {
 | 
							if (messageAnnot != null) {
 | 
				
			||||||
			SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot, typeAnnotation == null);
 | 
								SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot);
 | 
				
			||||||
			if (typeAnnotation != null) {
 | 
								if (typeAnnotation != null) {
 | 
				
			||||||
				result = createMessageMappingCondition(typeAnnotation, false).combine(result);
 | 
									result = createMessageMappingCondition(typeAnnotation).combine(result);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return result;
 | 
								return result;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		SubscribeMapping subsribeAnnotation = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
 | 
							SubscribeMapping subsribeAnnotation = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
 | 
				
			||||||
		if (subsribeAnnotation != null) {
 | 
							if (subsribeAnnotation != null) {
 | 
				
			||||||
			SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnotation, typeAnnotation == null);
 | 
								SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnotation);
 | 
				
			||||||
			if (typeAnnotation != null) {
 | 
								if (typeAnnotation != null) {
 | 
				
			||||||
				result = createMessageMappingCondition(typeAnnotation, false).combine(result);
 | 
									result = createMessageMappingCondition(typeAnnotation).combine(result);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return result;
 | 
								return result;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private SimpMessageMappingInfo createMessageMappingCondition(MessageMapping annotation, boolean prependLeadingSlash) {
 | 
						private SimpMessageMappingInfo createMessageMappingCondition(MessageMapping annotation) {
 | 
				
			||||||
		return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE,
 | 
							return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE,
 | 
				
			||||||
				new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher, prependLeadingSlash));
 | 
									new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation, boolean prependLeadingSlash) {
 | 
						private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation) {
 | 
				
			||||||
		return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.SUBSCRIBE,
 | 
							return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.SUBSCRIBE,
 | 
				
			||||||
				new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher, prependLeadingSlash));
 | 
									new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					@ -376,6 +393,27 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
 | 
				
			||||||
		return SimpMessageHeaderAccessor.getDestination(message.getHeaders());
 | 
							return SimpMessageHeaderAccessor.getDestination(message.getHeaders());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						protected String getLookupDestination(String destination) {
 | 
				
			||||||
 | 
							if (destination == null) {
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (CollectionUtils.isEmpty(getDestinationPrefixes())) {
 | 
				
			||||||
 | 
								return destination;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (String prefix : getDestinationPrefixes()) {
 | 
				
			||||||
 | 
								if (destination.startsWith(prefix)) {
 | 
				
			||||||
 | 
									if (this.slashPathSeparator) {
 | 
				
			||||||
 | 
										return destination.substring(prefix.length() - 1);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										return destination.substring(prefix.length());
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	protected SimpMessageMappingInfo getMatchingMapping(SimpMessageMappingInfo mapping, Message<?> message) {
 | 
						protected SimpMessageMappingInfo getMatchingMapping(SimpMessageMappingInfo mapping, Message<?> message) {
 | 
				
			||||||
		return mapping.getMatchingCondition(message);
 | 
							return mapping.getMatchingCondition(message);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,8 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private SubscriptionRegistry subscriptionRegistry;
 | 
						private SubscriptionRegistry subscriptionRegistry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private PathMatcher pathMatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private MessageHeaderInitializer headerInitializer;
 | 
						private MessageHeaderInitializer headerInitializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,38 +60,21 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
 | 
				
			||||||
	 * Create a SimpleBrokerMessageHandler instance with the given message channels
 | 
						 * Create a SimpleBrokerMessageHandler instance with the given message channels
 | 
				
			||||||
	 * and destination prefixes.
 | 
						 * and destination prefixes.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @param clientInboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
 | 
						 * @param inChannel 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 outChannel 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 brokerChannel the channel for the application to send messages to the broker
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public SimpleBrokerMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel,
 | 
						public SimpleBrokerMessageHandler(SubscribableChannel inChannel, MessageChannel outChannel,
 | 
				
			||||||
			SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) {
 | 
								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);
 | 
							super(destinationPrefixes);
 | 
				
			||||||
		Assert.notNull(clientInboundChannel, "'clientInboundChannel' must not be null");
 | 
							Assert.notNull(inChannel, "'clientInboundChannel' must not be null");
 | 
				
			||||||
		Assert.notNull(clientOutboundChannel, "'clientOutboundChannel' must not be null");
 | 
							Assert.notNull(outChannel, "'clientOutboundChannel' must not be null");
 | 
				
			||||||
		Assert.notNull(brokerChannel, "'brokerChannel' must not be null");
 | 
							Assert.notNull(brokerChannel, "'brokerChannel' must not be null");
 | 
				
			||||||
		this.clientInboundChannel = clientInboundChannel;
 | 
							this.clientInboundChannel = inChannel;
 | 
				
			||||||
		this.clientOutboundChannel = clientOutboundChannel;
 | 
							this.clientOutboundChannel = outChannel;
 | 
				
			||||||
		this.brokerChannel = brokerChannel;
 | 
							this.brokerChannel = brokerChannel;
 | 
				
			||||||
		DefaultSubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
 | 
							DefaultSubscriptionRegistry subscriptionRegistry = new DefaultSubscriptionRegistry();
 | 
				
			||||||
		if(pathMatcher != null) {
 | 
					 | 
				
			||||||
			subscriptionRegistry.setPathMatcher(pathMatcher);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		this.subscriptionRegistry = subscriptionRegistry;
 | 
							this.subscriptionRegistry = subscriptionRegistry;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,15 +91,41 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
 | 
				
			||||||
		return this.brokerChannel;
 | 
							return this.brokerChannel;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Configure a custom SubscriptionRegistry to use for storing subscriptions.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * <p><strong>Note</strong> that when a custom PathMatcher is configured via
 | 
				
			||||||
 | 
						 * {@link #setPathMatcher}, if the custom registry is not an instance of
 | 
				
			||||||
 | 
						 * {@link DefaultSubscriptionRegistry}, the provided PathMatcher is not used
 | 
				
			||||||
 | 
						 * and must be configured directly on the custom registry.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public void setSubscriptionRegistry(SubscriptionRegistry subscriptionRegistry) {
 | 
						public void setSubscriptionRegistry(SubscriptionRegistry subscriptionRegistry) {
 | 
				
			||||||
		Assert.notNull(subscriptionRegistry, "SubscriptionRegistry must not be null");
 | 
							Assert.notNull(subscriptionRegistry, "SubscriptionRegistry must not be null");
 | 
				
			||||||
		this.subscriptionRegistry = subscriptionRegistry;
 | 
							this.subscriptionRegistry = subscriptionRegistry;
 | 
				
			||||||
 | 
							initPathMatcherToUse();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void initPathMatcherToUse() {
 | 
				
			||||||
 | 
							if (this.pathMatcher != null) {
 | 
				
			||||||
 | 
								if (this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) {
 | 
				
			||||||
 | 
									((DefaultSubscriptionRegistry) this.subscriptionRegistry).setPathMatcher(this.pathMatcher);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public SubscriptionRegistry getSubscriptionRegistry() {
 | 
						public SubscriptionRegistry getSubscriptionRegistry() {
 | 
				
			||||||
		return this.subscriptionRegistry;
 | 
							return this.subscriptionRegistry;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * When configured, the given PathMatcher is passed down to the
 | 
				
			||||||
 | 
						 * SubscriptionRegistry to use for matching destination to subscriptions.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public void setPathMatcher(PathMatcher pathMatcher) {
 | 
				
			||||||
 | 
							this.pathMatcher = pathMatcher;
 | 
				
			||||||
 | 
							initPathMatcherToUse();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Configure a {@link MessageHeaderInitializer} to apply to the headers of all
 | 
						 * Configure a {@link MessageHeaderInitializer} to apply to the headers of all
 | 
				
			||||||
	 * messages sent to the client outbound channel.
 | 
						 * messages sent to the client outbound channel.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright 2002-2014 the original author or authors.
 | 
					 * Copyright 2002-2013 the original author or authors.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.messaging.MessageChannel;
 | 
					import org.springframework.messaging.MessageChannel;
 | 
				
			||||||
import org.springframework.messaging.SubscribableChannel;
 | 
					import org.springframework.messaging.SubscribableChannel;
 | 
				
			||||||
 | 
					import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler;
 | 
				
			||||||
import org.springframework.util.Assert;
 | 
					import org.springframework.util.Assert;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -66,4 +67,6 @@ public abstract class AbstractBrokerRegistration {
 | 
				
			||||||
		return this.destinationPrefixes;
 | 
							return this.destinationPrefixes;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected abstract AbstractBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,9 +39,9 @@ import org.springframework.messaging.simp.user.UserSessionRegistry;
 | 
				
			||||||
import org.springframework.messaging.support.AbstractSubscribableChannel;
 | 
					import org.springframework.messaging.support.AbstractSubscribableChannel;
 | 
				
			||||||
import org.springframework.messaging.support.ExecutorSubscribableChannel;
 | 
					import org.springframework.messaging.support.ExecutorSubscribableChannel;
 | 
				
			||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
					import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
				
			||||||
import org.springframework.util.AntPathMatcher;
 | 
					 | 
				
			||||||
import org.springframework.util.ClassUtils;
 | 
					import org.springframework.util.ClassUtils;
 | 
				
			||||||
import org.springframework.util.MimeTypeUtils;
 | 
					import org.springframework.util.MimeTypeUtils;
 | 
				
			||||||
 | 
					import org.springframework.util.PathMatcher;
 | 
				
			||||||
import org.springframework.validation.Errors;
 | 
					import org.springframework.validation.Errors;
 | 
				
			||||||
import org.springframework.validation.Validator;
 | 
					import org.springframework.validation.Validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,17 +206,17 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Bean
 | 
						@Bean
 | 
				
			||||||
	public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
 | 
						public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
 | 
				
			||||||
 | 
					 | 
				
			||||||
		String defaultSeparator = this.getBrokerRegistry().getDefaultSeparator();
 | 
					 | 
				
			||||||
		SimpAnnotationMethodMessageHandler handler = new SimpAnnotationMethodMessageHandler(
 | 
							SimpAnnotationMethodMessageHandler handler = new SimpAnnotationMethodMessageHandler(
 | 
				
			||||||
				clientInboundChannel(), clientOutboundChannel(), brokerMessagingTemplate(), defaultSeparator);
 | 
									clientInboundChannel(), clientOutboundChannel(), brokerMessagingTemplate());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
 | 
							handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
 | 
				
			||||||
		handler.setMessageConverter(brokerMessageConverter());
 | 
							handler.setMessageConverter(brokerMessageConverter());
 | 
				
			||||||
		handler.setValidator(simpValidator());
 | 
							handler.setValidator(simpValidator());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		AntPathMatcher pathMatcher = new AntPathMatcher(defaultSeparator);
 | 
							PathMatcher pathMatcher = this.getBrokerRegistry().getPathMatcher();
 | 
				
			||||||
 | 
							if (pathMatcher != null) {
 | 
				
			||||||
			handler.setPathMatcher(pathMatcher);
 | 
								handler.setPathMatcher(pathMatcher);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return handler;
 | 
							return handler;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,9 +234,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Bean
 | 
						@Bean
 | 
				
			||||||
	public UserDestinationMessageHandler userDestinationMessageHandler() {
 | 
						public UserDestinationMessageHandler userDestinationMessageHandler() {
 | 
				
			||||||
		UserDestinationMessageHandler handler = new UserDestinationMessageHandler(
 | 
							return new UserDestinationMessageHandler(clientInboundChannel(), brokerChannel(), userDestinationResolver());
 | 
				
			||||||
				clientInboundChannel(), brokerChannel(), userDestinationResolver());
 | 
					 | 
				
			||||||
		return handler;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Bean
 | 
						@Bean
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
 | 
				
			||||||
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
 | 
					import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
 | 
				
			||||||
import org.springframework.util.AntPathMatcher;
 | 
					import org.springframework.util.AntPathMatcher;
 | 
				
			||||||
import org.springframework.util.Assert;
 | 
					import org.springframework.util.Assert;
 | 
				
			||||||
 | 
					import org.springframework.util.PathMatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A registry for configuring message broker options.
 | 
					 * A registry for configuring message broker options.
 | 
				
			||||||
| 
						 | 
					@ -47,9 +48,10 @@ public class MessageBrokerRegistry {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private String userDestinationPrefix;
 | 
						private String userDestinationPrefix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private PathMatcher pathMatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
 | 
						private ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private String defaultSeparator;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
 | 
						public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
 | 
				
			||||||
		Assert.notNull(clientInboundChannel);
 | 
							Assert.notNull(clientInboundChannel);
 | 
				
			||||||
| 
						 | 
					@ -111,6 +113,31 @@ public class MessageBrokerRegistry {
 | 
				
			||||||
		return this;
 | 
							return this;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Configure the PathMatcher to use to match the destinations of incoming
 | 
				
			||||||
 | 
						 * messages to {@code @MessageMapping} and {@code @SubscribeMapping} methods.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * <p>By default {@link org.springframework.util.AntPathMatcher} is configured.
 | 
				
			||||||
 | 
						 * However applications may provide an {@code AntPathMatcher} instance
 | 
				
			||||||
 | 
						 * customized to use "." (commonly used in messaging) instead of "/" as path
 | 
				
			||||||
 | 
						 * separator or provide a completely different PathMatcher implementation.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * <p>Note that the configured PathMatcher is only used for matching the
 | 
				
			||||||
 | 
						 * portion of the destination after the configured prefix. For example given
 | 
				
			||||||
 | 
						 * application destination prefix "/app" and destination "/app/price.stock.**",
 | 
				
			||||||
 | 
						 * the message might be mapped to a controller with "price" and "stock.**"
 | 
				
			||||||
 | 
						 * as its type and method-level mappings respectively.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * <p>When the simple broker is enabled, the PathMatcher configured here is
 | 
				
			||||||
 | 
						 * also used to match message destinations when brokering messages.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since 4.1
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public MessageBrokerRegistry setPathMatcher(PathMatcher pathMatcher) {
 | 
				
			||||||
 | 
							this.pathMatcher = pathMatcher;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Customize the channel used to send messages from the application to the message
 | 
						 * Customize the channel used to send messages from the application to the message
 | 
				
			||||||
	 * broker. By default messages from the application to the message broker are sent
 | 
						 * broker. By default messages from the application to the message broker are sent
 | 
				
			||||||
| 
						 | 
					@ -122,24 +149,14 @@ public class MessageBrokerRegistry {
 | 
				
			||||||
		return this.brokerChannelRegistration;
 | 
							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) {
 | 
						protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
 | 
				
			||||||
		if ((this.simpleBrokerRegistration == null) && (this.brokerRelayRegistration == null)) {
 | 
							if ((this.simpleBrokerRegistration == null) && (this.brokerRelayRegistration == null)) {
 | 
				
			||||||
			enableSimpleBroker();
 | 
								enableSimpleBroker();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (this.simpleBrokerRegistration != null) {
 | 
							if (this.simpleBrokerRegistration != null) {
 | 
				
			||||||
			AntPathMatcher pathMatcher = new AntPathMatcher(this.defaultSeparator);
 | 
								SimpleBrokerMessageHandler handler = this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
 | 
				
			||||||
			return this.simpleBrokerRegistration.getMessageHandler(brokerChannel, pathMatcher);
 | 
								handler.setPathMatcher(this.pathMatcher);
 | 
				
			||||||
 | 
								return handler;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -160,12 +177,12 @@ public class MessageBrokerRegistry {
 | 
				
			||||||
		return this.userDestinationPrefix;
 | 
							return this.userDestinationPrefix;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected PathMatcher getPathMatcher() {
 | 
				
			||||||
 | 
							return this.pathMatcher;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected ChannelRegistration getBrokerChannelRegistration() {
 | 
						protected ChannelRegistration getBrokerChannelRegistration() {
 | 
				
			||||||
		return this.brokerChannelRegistration;
 | 
							return this.brokerChannelRegistration;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected String getDefaultSeparator() {
 | 
					 | 
				
			||||||
		return this.defaultSeparator;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,28 +19,25 @@ package org.springframework.messaging.simp.config;
 | 
				
			||||||
import org.springframework.messaging.MessageChannel;
 | 
					import org.springframework.messaging.MessageChannel;
 | 
				
			||||||
import org.springframework.messaging.SubscribableChannel;
 | 
					import org.springframework.messaging.SubscribableChannel;
 | 
				
			||||||
import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
 | 
					import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
 | 
				
			||||||
import org.springframework.util.PathMatcher;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Registration class for configuring a {@link SimpleBrokerMessageHandler}.
 | 
					 * Registration class for configuring a {@link SimpleBrokerMessageHandler}.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Rossen Stoyanchev
 | 
					 * @author Rossen Stoyanchev
 | 
				
			||||||
 * @author Sebastien Deleuze
 | 
					 | 
				
			||||||
 * @since 4.0
 | 
					 * @since 4.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
 | 
					public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public SimpleBrokerRegistration(SubscribableChannel clientInboundChannel,
 | 
						public SimpleBrokerRegistration(SubscribableChannel inChannel, MessageChannel outChannel, String[] prefixes) {
 | 
				
			||||||
			MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
 | 
							super(inChannel, outChannel, prefixes);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		super(clientInboundChannel, clientOutboundChannel, destinationPrefixes);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel, PathMatcher pathMatcher) {
 | 
						@Override
 | 
				
			||||||
 | 
						protected SimpleBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel) {
 | 
				
			||||||
		return new SimpleBrokerMessageHandler(getClientInboundChannel(),
 | 
							return new SimpleBrokerMessageHandler(getClientInboundChannel(),
 | 
				
			||||||
				getClientOutboundChannel(), brokerChannel, getDestinationPrefixes(), pathMatcher);
 | 
									getClientOutboundChannel(), brokerChannel, getDestinationPrefixes());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright 2002-2014 the original author or authors.
 | 
					 * Copyright 2002-2012 the original author or authors.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -18,42 +18,34 @@ package org.springframework.messaging.handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					 | 
				
			||||||
import org.junit.runners.Parameterized;
 | 
					 | 
				
			||||||
import org.springframework.messaging.Message;
 | 
					import org.springframework.messaging.Message;
 | 
				
			||||||
import org.springframework.messaging.support.MessageBuilder;
 | 
					import org.springframework.messaging.support.MessageBuilder;
 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.junit.runners.Parameterized.Parameters;
 | 
					 | 
				
			||||||
import org.junit.runners.Parameterized.Parameter;
 | 
					 | 
				
			||||||
import org.springframework.util.AntPathMatcher;
 | 
					import org.springframework.util.AntPathMatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import static org.junit.Assert.*;
 | 
					import static org.junit.Assert.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Unit tests for {@link DestinationPatternsMessageCondition}.
 | 
					 * Unit tests for {@link DestinationPatternsMessageCondition}.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Rossen Stoyanchev
 | 
					 * @author Rossen Stoyanchev
 | 
				
			||||||
 * @author Sebastien Deleuze
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@RunWith(Parameterized.class)
 | 
					 | 
				
			||||||
public class DestinationPatternsMessageConditionTests {
 | 
					public class DestinationPatternsMessageConditionTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Parameter(0)
 | 
					 | 
				
			||||||
	public String pathSeparator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Parameters
 | 
					 | 
				
			||||||
	public static Iterable<Object[]> arguments() {
 | 
					 | 
				
			||||||
		return Arrays.asList(new Object[][]{{"/"}, {"."}});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void prependSlash() {
 | 
						public void prependSlash() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c = condition("foo");
 | 
							DestinationPatternsMessageCondition c = condition("foo");
 | 
				
			||||||
		assertEquals("/foo", c.getPatterns().iterator().next());
 | 
							assertEquals("/foo", c.getPatterns().iterator().next());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void prependSlashWithCustomPathSeparator() {
 | 
				
			||||||
 | 
							DestinationPatternsMessageCondition c =
 | 
				
			||||||
 | 
									new DestinationPatternsMessageCondition(new String[] {"foo"}, new AntPathMatcher("."));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertEquals("Pre-pending should be disabled when not using '/' as path separator",
 | 
				
			||||||
 | 
									"foo", c.getPatterns().iterator().next());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SPR-8255
 | 
						// SPR-8255
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					@ -65,21 +57,20 @@ public class DestinationPatternsMessageConditionTests {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void combineEmptySets() {
 | 
						public void combineEmptySets() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c1 = condition();
 | 
							DestinationPatternsMessageCondition c1 = condition();
 | 
				
			||||||
		DestinationPatternsMessageCondition c2 = suffixCondition();
 | 
							DestinationPatternsMessageCondition c2 = condition();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(condition(""), c1.combine(c2));
 | 
							assertEquals(condition(""), c1.combine(c2));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void combineOnePatternWithEmptySet() {
 | 
						public void combineOnePatternWithEmptySet() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c1 = condition("/type1",
 | 
							DestinationPatternsMessageCondition c1 = condition("/type1", "/type2");
 | 
				
			||||||
				pathSeparator + "type2");
 | 
							DestinationPatternsMessageCondition c2 = condition();
 | 
				
			||||||
		DestinationPatternsMessageCondition c2 = suffixCondition();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(condition("/type1", pathSeparator + "type2"), c1.combine(c2));
 | 
							assertEquals(condition("/type1", "/type2"), c1.combine(c2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		c1 = condition();
 | 
							c1 = condition();
 | 
				
			||||||
		c2 = suffixCondition("/method1", "/method2");
 | 
							c2 = condition("/method1", "/method2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(condition("/method1", "/method2"), c1.combine(c2));
 | 
							assertEquals(condition("/method1", "/method2"), c1.combine(c2));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -87,12 +78,10 @@ public class DestinationPatternsMessageConditionTests {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void combineMultiplePatterns() {
 | 
						public void combineMultiplePatterns() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c1 = condition("/t1", "/t2");
 | 
							DestinationPatternsMessageCondition c1 = condition("/t1", "/t2");
 | 
				
			||||||
		DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "m1",
 | 
							DestinationPatternsMessageCondition c2 = condition("/m1", "/m2");
 | 
				
			||||||
				pathSeparator + "m2");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(
 | 
							assertEquals(new DestinationPatternsMessageCondition(
 | 
				
			||||||
				condition("/t1" + pathSeparator + "m1", "/t1" + pathSeparator + "m2",
 | 
									"/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.combine(c2));
 | 
				
			||||||
						"/t2" + pathSeparator + "m1", "/t2" + pathSeparator + "m2"), c1.combine(c2));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					@ -105,40 +94,35 @@ public class DestinationPatternsMessageConditionTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void matchPattern() {
 | 
						public void matchPattern() {
 | 
				
			||||||
		DestinationPatternsMessageCondition condition = condition(
 | 
							DestinationPatternsMessageCondition condition = condition("/foo/*");
 | 
				
			||||||
				"/foo" + pathSeparator + "*");
 | 
							DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo/bar"));
 | 
				
			||||||
		DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo" + pathSeparator + "bar"));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertNotNull(match);
 | 
							assertNotNull(match);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void matchSortPatterns() {
 | 
						public void matchSortPatterns() {
 | 
				
			||||||
		DestinationPatternsMessageCondition condition = suffixCondition(
 | 
							DestinationPatternsMessageCondition condition = condition("/**", "/foo/bar", "/foo/*");
 | 
				
			||||||
				pathSeparator + "**", pathSeparator + "foo" + pathSeparator + "bar",
 | 
							DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo("/foo/bar"));
 | 
				
			||||||
				pathSeparator + "foo" + pathSeparator + "*");
 | 
							DestinationPatternsMessageCondition expected = condition("/foo/bar", "/foo/*", "/**");
 | 
				
			||||||
		DestinationPatternsMessageCondition match = condition.getMatchingCondition(messageTo(pathSeparator + "foo" + pathSeparator + "bar"));
 | 
					 | 
				
			||||||
		DestinationPatternsMessageCondition expected = suffixCondition(
 | 
					 | 
				
			||||||
				pathSeparator + "foo" + pathSeparator + "bar",
 | 
					 | 
				
			||||||
				pathSeparator + "foo" + pathSeparator + "*", pathSeparator + "**");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(expected, match);
 | 
							assertEquals(expected, match);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void compareEqualPatterns() {
 | 
						public void compareEqualPatterns() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c1 = suffixCondition(pathSeparator + "foo*");
 | 
							DestinationPatternsMessageCondition c1 = condition("/foo*");
 | 
				
			||||||
		DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "foo*");
 | 
							DestinationPatternsMessageCondition c2 = condition("/foo*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(0, c1.compareTo(c2, messageTo(pathSeparator + "foo")));
 | 
							assertEquals(0, c1.compareTo(c2, messageTo("/foo")));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void comparePatternSpecificity() {
 | 
						public void comparePatternSpecificity() {
 | 
				
			||||||
		DestinationPatternsMessageCondition c1 = suffixCondition(pathSeparator + "fo*");
 | 
							DestinationPatternsMessageCondition c1 = condition("/fo*");
 | 
				
			||||||
		DestinationPatternsMessageCondition c2 = suffixCondition(pathSeparator + "foo");
 | 
							DestinationPatternsMessageCondition c2 = condition("/foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals(1, c1.compareTo(c2, messageTo(pathSeparator + "foo")));
 | 
							assertEquals(1, c1.compareTo(c2, messageTo("/foo")));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					@ -156,11 +140,7 @@ public class DestinationPatternsMessageConditionTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private DestinationPatternsMessageCondition condition(String... patterns) {
 | 
						private DestinationPatternsMessageCondition condition(String... patterns) {
 | 
				
			||||||
		return new DestinationPatternsMessageCondition(patterns, new AntPathMatcher(this.pathSeparator));
 | 
							return new DestinationPatternsMessageCondition(patterns);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private DestinationPatternsMessageCondition suffixCondition(String... patterns) {
 | 
					 | 
				
			||||||
		return new DestinationPatternsMessageCondition(patterns, new AntPathMatcher(this.pathSeparator), false);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Message<?> messageTo(String destination) {
 | 
						private Message<?> messageTo(String destination) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.springframework.messaging.simp.annotation.support;
 | 
					package org.springframework.messaging.simp.annotation.support;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.LinkedHashMap;
 | 
					import java.util.LinkedHashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.concurrent.ConcurrentHashMap;
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
| 
						 | 
					@ -38,6 +40,7 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
 | 
				
			||||||
import org.springframework.messaging.simp.annotation.SubscribeMapping;
 | 
					import org.springframework.messaging.simp.annotation.SubscribeMapping;
 | 
				
			||||||
import org.springframework.messaging.support.MessageBuilder;
 | 
					import org.springframework.messaging.support.MessageBuilder;
 | 
				
			||||||
import org.springframework.stereotype.Controller;
 | 
					import org.springframework.stereotype.Controller;
 | 
				
			||||||
 | 
					import org.springframework.util.AntPathMatcher;
 | 
				
			||||||
import org.springframework.validation.Errors;
 | 
					import org.springframework.validation.Errors;
 | 
				
			||||||
import org.springframework.validation.Validator;
 | 
					import org.springframework.validation.Validator;
 | 
				
			||||||
import org.springframework.validation.annotation.Validated;
 | 
					import org.springframework.validation.annotation.Validated;
 | 
				
			||||||
| 
						 | 
					@ -148,7 +151,7 @@ public class SimpAnnotationMethodMessageHandlerTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void simpScope() {
 | 
						public void simpScope() {
 | 
				
			||||||
		ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
 | 
							Map<String, Object> map = new ConcurrentHashMap<>();
 | 
				
			||||||
		map.put("name", "value");
 | 
							map.put("name", "value");
 | 
				
			||||||
		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
 | 
							SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
 | 
				
			||||||
		headers.setSessionId("session1");
 | 
							headers.setSessionId("session1");
 | 
				
			||||||
| 
						 | 
					@ -160,6 +163,33 @@ public class SimpAnnotationMethodMessageHandlerTests {
 | 
				
			||||||
		assertEquals("scope", this.testController.method);
 | 
							assertEquals("scope", this.testController.method);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void dotPathSeparator() {
 | 
				
			||||||
 | 
							DotPathSeparatorController controller = new DotPathSeparatorController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.messageHandler.setPathMatcher(new AntPathMatcher("."));
 | 
				
			||||||
 | 
							this.messageHandler.registerHandler(controller);
 | 
				
			||||||
 | 
							this.messageHandler.setDestinationPrefixes(Arrays.asList("/app1", "/app2/"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
 | 
				
			||||||
 | 
							headers.setSessionId("session1");
 | 
				
			||||||
 | 
							headers.setSessionAttributes(new HashMap<>());
 | 
				
			||||||
 | 
							headers.setDestination("/app1/pre.foo");
 | 
				
			||||||
 | 
							Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
 | 
				
			||||||
 | 
							this.messageHandler.handleMessage(message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertEquals("handleFoo", controller.method);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							headers = SimpMessageHeaderAccessor.create();
 | 
				
			||||||
 | 
							headers.setSessionId("session1");
 | 
				
			||||||
 | 
							headers.setSessionAttributes(new HashMap<>());
 | 
				
			||||||
 | 
							headers.setDestination("/app2/pre.foo");
 | 
				
			||||||
 | 
							message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
 | 
				
			||||||
 | 
							this.messageHandler.handleMessage(message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertEquals("handleFoo", controller.method);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static class TestSimpAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {
 | 
						private static class TestSimpAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -232,6 +262,20 @@ public class SimpAnnotationMethodMessageHandlerTests {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Controller
 | 
				
			||||||
 | 
						@MessageMapping("pre")
 | 
				
			||||||
 | 
						private static class DotPathSeparatorController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private String method;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@MessageMapping("foo")
 | 
				
			||||||
 | 
							public void handleFoo() {
 | 
				
			||||||
 | 
								this.method = "handleFoo";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static class StringTestValidator implements Validator {
 | 
						private static class StringTestValidator implements Validator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private final String invalidValue;
 | 
							private final String invalidValue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ public class MessageBrokerConfigurationTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private AnnotationConfigApplicationContext customChannelContext;
 | 
						private AnnotationConfigApplicationContext customChannelContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private AnnotationConfigApplicationContext customMatchingContext;
 | 
						private AnnotationConfigApplicationContext customPathMatcherContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Before
 | 
						@Before
 | 
				
			||||||
| 
						 | 
					@ -101,9 +101,9 @@ public class MessageBrokerConfigurationTests {
 | 
				
			||||||
		this.customChannelContext.register(CustomChannelConfig.class);
 | 
							this.customChannelContext.register(CustomChannelConfig.class);
 | 
				
			||||||
		this.customChannelContext.refresh();
 | 
							this.customChannelContext.refresh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.customMatchingContext = new AnnotationConfigApplicationContext();
 | 
							this.customPathMatcherContext = new AnnotationConfigApplicationContext();
 | 
				
			||||||
		this.customMatchingContext.register(CustomMatchingSimpleBrokerConfig.class);
 | 
							this.customPathMatcherContext.register(CustomPathMatcherConfig.class);
 | 
				
			||||||
		this.customMatchingContext.refresh();
 | 
							this.customPathMatcherContext.refresh();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -407,17 +407,13 @@ public class MessageBrokerConfigurationTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void customMatching() {
 | 
						public void customPathMatcher() {
 | 
				
			||||||
		SimpleBrokerMessageHandler brokerHandler = this.customMatchingContext.getBean(SimpleBrokerMessageHandler.class);
 | 
							SimpleBrokerMessageHandler broker = this.customPathMatcherContext.getBean(SimpleBrokerMessageHandler.class);
 | 
				
			||||||
		DefaultSubscriptionRegistry subscriptionRegistry = (DefaultSubscriptionRegistry)brokerHandler.getSubscriptionRegistry();
 | 
							DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry();
 | 
				
			||||||
		AntPathMatcher pathMatcher = (AntPathMatcher)subscriptionRegistry.getPathMatcher();
 | 
							assertEquals("a.a", registry.getPathMatcher().combine("a", "a"));
 | 
				
			||||||
		DirectFieldAccessor accessor = new DirectFieldAccessor(pathMatcher);
 | 
					 | 
				
			||||||
		assertEquals(".", accessor.getPropertyValue("pathSeparator"));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		SimpAnnotationMethodMessageHandler messageHandler = customMatchingContext.getBean(SimpAnnotationMethodMessageHandler.class);
 | 
							SimpAnnotationMethodMessageHandler handler = this.customPathMatcherContext.getBean(SimpAnnotationMethodMessageHandler.class);
 | 
				
			||||||
		pathMatcher = (AntPathMatcher)messageHandler.getPathMatcher();
 | 
							assertEquals("a.a", handler.getPathMatcher().combine("a", "a"));
 | 
				
			||||||
		accessor = new DirectFieldAccessor(pathMatcher);
 | 
					 | 
				
			||||||
		assertEquals(".", accessor.getPropertyValue("pathSeparator"));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -504,11 +500,11 @@ public class MessageBrokerConfigurationTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Configuration
 | 
						@Configuration
 | 
				
			||||||
	static class CustomMatchingSimpleBrokerConfig extends SimpleBrokerConfig {
 | 
						static class CustomPathMatcherConfig extends SimpleBrokerConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
							public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
				
			||||||
			registry.defaultSeparator(".").enableSimpleBroker("/topic", "/queue");
 | 
								registry.setPathMatcher(new AntPathMatcher(".")).enableSimpleBroker("/topic", "/queue");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -330,11 +330,11 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
 | 
				
			||||||
		if (simpleBrokerElem != null) {
 | 
							if (simpleBrokerElem != null) {
 | 
				
			||||||
			String prefix = simpleBrokerElem.getAttribute("prefix");
 | 
								String prefix = simpleBrokerElem.getAttribute("prefix");
 | 
				
			||||||
			cavs.addIndexedArgumentValue(3, Arrays.asList(StringUtils.tokenizeToStringArray(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);
 | 
								brokerDef = new RootBeanDefinition(SimpleBrokerMessageHandler.class, cavs, null);
 | 
				
			||||||
 | 
								if (messageBrokerElement.hasAttribute("path-matcher")) {
 | 
				
			||||||
 | 
									brokerDef.getPropertyValues().add("pathMatcher",
 | 
				
			||||||
 | 
											new RuntimeBeanReference(messageBrokerElement.getAttribute("path-matcher")));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else if (brokerRelayElem != null) {
 | 
							else if (brokerRelayElem != null) {
 | 
				
			||||||
			String prefix = brokerRelayElem.getAttribute("prefix");
 | 
								String prefix = brokerRelayElem.getAttribute("prefix");
 | 
				
			||||||
| 
						 | 
					@ -454,15 +454,13 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
 | 
				
			||||||
		mpvs.add("destinationPrefixes",Arrays.asList(StringUtils.tokenizeToStringArray(appDestPrefix, ",")));
 | 
							mpvs.add("destinationPrefixes",Arrays.asList(StringUtils.tokenizeToStringArray(appDestPrefix, ",")));
 | 
				
			||||||
		mpvs.add("messageConverter", brokerMessageConverterRef);
 | 
							mpvs.add("messageConverter", brokerMessageConverterRef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		RootBeanDefinition annotationMethodMessageHandlerDef =
 | 
							RootBeanDefinition beanDef = new RootBeanDefinition(SimpAnnotationMethodMessageHandler.class, cavs, mpvs);
 | 
				
			||||||
				new RootBeanDefinition(SimpAnnotationMethodMessageHandler.class, cavs, mpvs);
 | 
							if (messageBrokerElement.hasAttribute("path-matcher")) {
 | 
				
			||||||
 | 
								beanDef.getPropertyValues().add("pathMatcher",
 | 
				
			||||||
		String defaultSeparator = messageBrokerElement.getAttribute("default-separator");
 | 
										new RuntimeBeanReference(messageBrokerElement.getAttribute("path-matcher")));
 | 
				
			||||||
		if (!defaultSeparator.isEmpty()) {
 | 
					 | 
				
			||||||
			annotationMethodMessageHandlerDef.getPropertyValues().add("pathMatcher", new AntPathMatcher(defaultSeparator));
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		registerBeanDef(annotationMethodMessageHandlerDef, parserCxt, source);
 | 
							registerBeanDef(beanDef, parserCxt, source);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private RuntimeBeanReference registerUserDestinationResolver(Element messageBrokerElement,
 | 
						private RuntimeBeanReference registerUserDestinationResolver(Element messageBrokerElement,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
					<?xml version="1.0" encoding="UTF-8" ?>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!--
 | 
					<!--
 | 
				
			||||||
  ~ 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");
 | 
					  ~ Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
  ~ you may not use this file except in compliance with the License.
 | 
					  ~ you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -19,12 +19,13 @@
 | 
				
			||||||
<xsd:schema xmlns="http://www.springframework.org/schema/websocket"
 | 
					<xsd:schema xmlns="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 | 
					            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 | 
				
			||||||
            xmlns:beans="http://www.springframework.org/schema/beans"
 | 
					            xmlns:beans="http://www.springframework.org/schema/beans"
 | 
				
			||||||
 | 
								xmlns:tool="http://www.springframework.org/schema/tool"
 | 
				
			||||||
            targetNamespace="http://www.springframework.org/schema/websocket"
 | 
					            targetNamespace="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
            elementFormDefault="qualified"
 | 
					            elementFormDefault="qualified"
 | 
				
			||||||
            attributeFormDefault="unqualified">
 | 
					            attributeFormDefault="unqualified">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <xsd:import namespace="http://www.springframework.org/schema/beans"
 | 
					    <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"/>
 | 
				
			||||||
                schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"/>
 | 
						<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.1.xsd" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <xsd:complexType name="mapping">
 | 
					    <xsd:complexType name="mapping">
 | 
				
			||||||
        <xsd:annotation>
 | 
					        <xsd:annotation>
 | 
				
			||||||
| 
						 | 
					@ -687,14 +688,31 @@
 | 
				
			||||||
                    ]]></xsd:documentation>
 | 
					                    ]]></xsd:documentation>
 | 
				
			||||||
                </xsd:annotation>
 | 
					                </xsd:annotation>
 | 
				
			||||||
            </xsd:attribute>
 | 
					            </xsd:attribute>
 | 
				
			||||||
			<xsd:attribute name="default-separator" type="xsd:string">
 | 
								<xsd:attribute name="path-matcher" type="xsd:string">
 | 
				
			||||||
				<xsd:annotation>
 | 
									<xsd:annotation>
 | 
				
			||||||
					<xsd:documentation><![CDATA[
 | 
										<xsd:documentation><![CDATA[
 | 
				
			||||||
	Customize the default separator used for destination patterns matching/combining.
 | 
						A reference to the PathMatcher to use to match the destinations of incoming
 | 
				
			||||||
	It could be used to configure "." as the default separator, since it is used in most
 | 
						messages to @MessageMapping and @SubscribeMapping methods.
 | 
				
			||||||
	STOMP broker relay, enabling destination patterns like "/topic/PRICE.STOCK.**".
 | 
					
 | 
				
			||||||
	The default separator is "/".
 | 
						By default AntPathMatcher is configured.
 | 
				
			||||||
 | 
						However applications may provide an AntPathMatcher instance
 | 
				
			||||||
 | 
						customized to use "." (commonly used in messaging) instead of "/" as path
 | 
				
			||||||
 | 
						separator or provide a completely different PathMatcher implementation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Note that the configured PathMatcher is only used for matching the
 | 
				
			||||||
 | 
						portion of the destination after the configured prefix. For example given
 | 
				
			||||||
 | 
						application destination prefix "/app" and destination "/app/price.stock.**",
 | 
				
			||||||
 | 
						the message might be mapped to a controller with "price" and "stock.**"
 | 
				
			||||||
 | 
						as its type and method-level mappings respectively.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						When the simple broker is enabled, the PathMatcher configured here is
 | 
				
			||||||
 | 
						also used to match message destinations when brokering messages.
 | 
				
			||||||
                    ]]></xsd:documentation>
 | 
					                    ]]></xsd:documentation>
 | 
				
			||||||
 | 
										<xsd:appinfo>
 | 
				
			||||||
 | 
											<tool:annotation kind="ref">
 | 
				
			||||||
 | 
												<tool:expected-type type="java:org.springframework.util.PathMatcher" />
 | 
				
			||||||
 | 
											</tool:annotation>
 | 
				
			||||||
 | 
										</xsd:appinfo>
 | 
				
			||||||
				</xsd:annotation>
 | 
									</xsd:annotation>
 | 
				
			||||||
			</xsd:attribute>
 | 
								</xsd:attribute>
 | 
				
			||||||
            <xsd:attribute name="order" type="xsd:token">
 | 
					            <xsd:attribute name="order" type="xsd:token">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
					       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       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
 | 
					       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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker>
 | 
					    <websocket:message-broker>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
					       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       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
 | 
					       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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker>
 | 
					    <websocket:message-broker>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
       xsi:schemaLocation="
 | 
					       xsi:schemaLocation="
 | 
				
			||||||
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 | 
					        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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker>
 | 
					    <websocket:message-broker>
 | 
				
			||||||
        <websocket:stomp-endpoint path="/foo"/>
 | 
					        <websocket:stomp-endpoint path="/foo"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
					       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       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
 | 
					       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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal">
 | 
					    <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal">
 | 
				
			||||||
        <websocket:stomp-endpoint path="/foo,/bar">
 | 
					        <websocket:stomp-endpoint path="/foo,/bar">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
					       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       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
 | 
					       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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker order="2">
 | 
					    <websocket:message-broker order="2">
 | 
				
			||||||
        <websocket:stomp-endpoint path="/foo">
 | 
					        <websocket:stomp-endpoint path="/foo">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,11 @@
 | 
				
			||||||
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
					       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       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
 | 
					       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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal" default-separator=".">
 | 
					    <websocket:message-broker application-destination-prefix="/app"
 | 
				
			||||||
 | 
												  user-destination-prefix="/personal"
 | 
				
			||||||
 | 
												  path-matcher="pathMatcher">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- message-size=128*1024, send-buffer-size=1024*1024 -->
 | 
					        <!-- message-size=128*1024, send-buffer-size=1024*1024 -->
 | 
				
			||||||
        <websocket:transport message-size="131072" send-timeout="25000" send-buffer-size="1048576" />
 | 
					        <websocket:transport message-size="131072" send-timeout="25000" send-buffer-size="1048576" />
 | 
				
			||||||
| 
						 | 
					@ -22,6 +24,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </websocket:message-broker>
 | 
					    </websocket:message-broker>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher">
 | 
				
			||||||
 | 
							<property name="pathSeparator" value="." />
 | 
				
			||||||
 | 
						</bean>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <bean id="myHandler" class="org.springframework.web.socket.config.TestHandshakeHandler"/>
 | 
					    <bean id="myHandler" class="org.springframework.web.socket.config.TestHandshakeHandler"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</beans>
 | 
					</beans>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
       xsi:schemaLocation="
 | 
					       xsi:schemaLocation="
 | 
				
			||||||
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 | 
					        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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:handlers order="2">
 | 
					    <websocket:handlers order="2">
 | 
				
			||||||
        <websocket:mapping path="/foo" handler="fooHandler"/>
 | 
					        <websocket:mapping path="/foo" handler="fooHandler"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
       xsi:schemaLocation="
 | 
					       xsi:schemaLocation="
 | 
				
			||||||
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 | 
					        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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:handlers >
 | 
					    <websocket:handlers >
 | 
				
			||||||
        <websocket:mapping path="/test" handler="testHandler"/>
 | 
					        <websocket:mapping path="/test" handler="testHandler"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
       xsi:schemaLocation="
 | 
					       xsi:schemaLocation="
 | 
				
			||||||
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 | 
					        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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:handlers>
 | 
					    <websocket:handlers>
 | 
				
			||||||
        <websocket:mapping path="/test" handler="testHandler"/>
 | 
					        <websocket:mapping path="/test" handler="testHandler"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
					       xmlns:websocket="http://www.springframework.org/schema/websocket"
 | 
				
			||||||
       xsi:schemaLocation="
 | 
					       xsi:schemaLocation="
 | 
				
			||||||
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 | 
					        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.1.xsd">
 | 
							http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <websocket:handlers>
 | 
					    <websocket:handlers>
 | 
				
			||||||
        <websocket:mapping path="/foo,/bar" handler="fooHandler"/>
 | 
					        <websocket:mapping path="/foo,/bar" handler="fooHandler"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37864,8 +37864,8 @@ options. The endpoint is available for clients to connect to at URL path `/app/p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
        public void configureMessageBroker(MessageBrokerRegistry config) {
 | 
					        public void configureMessageBroker(MessageBrokerRegistry config) {
 | 
				
			||||||
            config.setApplicationDestinationPrefixes("/app")
 | 
					            config.setApplicationDestinationPrefixes("/app");
 | 
				
			||||||
                .enableSimpleBroker("/queue", "/topic");
 | 
					            config.enableSimpleBroker("/queue", "/topic");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
| 
						 | 
					@ -38001,7 +38001,7 @@ Below is a simple example to illustrate the flow of messages:
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
							public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
				
			||||||
			registry.setApplicationDestinationPrefixes("/app");
 | 
								registry.setApplicationDestinationPrefixes("/app");
 | 
				
			||||||
			registry.enableSimpleBroker("/topic/");
 | 
								registry.enableSimpleBroker("/topic");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -38039,17 +38039,28 @@ kinds of arguments and return values supported.
 | 
				
			||||||
[[websocket-stomp-handle-annotations]]
 | 
					[[websocket-stomp-handle-annotations]]
 | 
				
			||||||
==== Annotation Message Handling
 | 
					==== Annotation Message Handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `@MessageMapping` annotation is supported on methods of `@Controller`
 | 
					The `@MessageMapping` annotation is supported on methods of `@Controller` classes.
 | 
				
			||||||
as well as on `@RestController`-annotated classes.
 | 
					It can be used for mapping methods to message destinations and can also be combined
 | 
				
			||||||
It can be used for mapping methods to path-like message destinations. It is also
 | 
					with the type-level `@MessageMapping` for expressing shared mappings across all
 | 
				
			||||||
possible to combine with a type-level `@MessageMapping` for expressing shared
 | 
					annotated methods within a controller.
 | 
				
			||||||
mappings across all annotated methods within a controller.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Destination mappings can contain Ant-style patterns (e.g. "/foo*", "/foo/**")
 | 
					By default destination mappings are treated as Ant-style, slash-separated, path
 | 
				
			||||||
and template variables (e.g. "/foo/{id}"), which can then be accessed via
 | 
					patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables,
 | 
				
			||||||
`@DestinationVariable` method arguments. This should be familiar to Spring MVC
 | 
					e.g. "/foo/{id}" that can then be referenced via `@DestinationVariable`-annotated
 | 
				
			||||||
users, in fact the same `AntPathMatcher` is used for matching destinations based
 | 
					method arguments.
 | 
				
			||||||
on patterns and for extracting template variables.
 | 
					
 | 
				
			||||||
 | 
					[NOTE]
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					Although Ant-style, slash-separated, path patterns should feel familiar to web
 | 
				
			||||||
 | 
					developers, in message brokers and in messaging it is common to use "." as the
 | 
				
			||||||
 | 
					separator, for example in the names of destinations such as topics, queues,
 | 
				
			||||||
 | 
					exchanges, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Applications can switch to using "." (dot) instead of "/" (slash) as the separator
 | 
				
			||||||
 | 
					for destinations mapped to `@MessageMapping` methods simply by configuring an `AntPathMatcher`
 | 
				
			||||||
 | 
					with a customized path separator property. This can be done easily through
 | 
				
			||||||
 | 
					the provided Java config and XML namespace.
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The following method arguments are supported for `@MessageMapping` methods:
 | 
					The following method arguments are supported for `@MessageMapping` methods:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38137,6 +38148,21 @@ stores them in memory, and broadcasts messages to connected clients with matchin
 | 
				
			||||||
destinations. The broker supports path-like destinations, including subscriptions
 | 
					destinations. The broker supports path-like destinations, including subscriptions
 | 
				
			||||||
to Ant-style destination patterns.
 | 
					to Ant-style destination patterns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[NOTE]
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					Although Ant-style, slash-separated, path patterns should feel familiar to web
 | 
				
			||||||
 | 
					developers, in message brokers and in messaging it is common to use "." as the
 | 
				
			||||||
 | 
					separator, for example in the names of destinations such as topics, queues,
 | 
				
			||||||
 | 
					exchanges, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Applications can switch to using "." (dot) instead of "/" (slash) as the separator
 | 
				
			||||||
 | 
					for destinations handled by the broker simply by configuring an `AntPathMatcher`
 | 
				
			||||||
 | 
					with a customized path separator property. This can be done easily through
 | 
				
			||||||
 | 
					the provided Java config and XML namespace.
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[websocket-stomp-handle-broker-relay]]
 | 
					[[websocket-stomp-handle-broker-relay]]
 | 
				
			||||||
==== Full-Featured Broker
 | 
					==== Full-Featured Broker
 | 
				
			||||||
| 
						 | 
					@ -38168,7 +38194,7 @@ Below is example configuration that enables a full-featured broker:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
							public void configureMessageBroker(MessageBrokerRegistry registry) {
 | 
				
			||||||
			registry.enableStompBrokerRelay("/topic/", "/queue/");
 | 
								registry.enableStompBrokerRelay("/topic", "/queue");
 | 
				
			||||||
			registry.setApplicationDestinationPrefixes("/app");
 | 
								registry.setApplicationDestinationPrefixes("/app");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue