Add PathPatternRegistry

This commit adds the new `PathPatternRegistry`, which  holds a
sorted set of `PathPattern`s and allows for searching/adding patterns

This registry is being used in `HandlerMapping` implementations and
separates path pattern parsing/matching logic from the rest. Directly
using `PathPattern` instances should improve the performance of those
`HandlerMapping` implementations, since the parsing and generation of
pattern variants (trailing slash, suffix patterns, etc) is done only
once.

Issue: SPR-14544
This commit is contained in:
Brian Clozel
2017-02-08 14:03:22 +01:00
parent a4da313a0a
commit 18c04815a7
24 changed files with 869 additions and 581 deletions

View File

@@ -102,9 +102,9 @@ public class WebFluxConfigurationSupportTests {
assertEquals(0, mapping.getOrder());
assertTrue(mapping.useSuffixPatternMatch());
assertFalse(mapping.useSuffixPatternMatch());
assertFalse(mapping.useRegisteredSuffixPatternMatch());
assertTrue(mapping.useTrailingSlashMatch());
assertTrue(mapping.useRegisteredSuffixPatternMatch());
name = "webFluxContentTypeResolver";
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
@@ -262,7 +262,6 @@ public class WebFluxConfigurationSupportTests {
assertEquals(Ordered.LOWEST_PRECEDENCE - 1, handlerMapping.getOrder());
assertNotNull(handlerMapping.getPathHelper());
assertNotNull(handlerMapping.getPathMatcher());
SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
WebHandler webHandler = (WebHandler) urlHandlerMapping.getUrlMap().get("/images/**");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,11 +55,12 @@ public class SimpleUrlHandlerMappingTests {
Object mainController = wac.getBean("mainController");
Object otherController = wac.getBean("otherController");
testUrl("/welcome.html", mainController, handlerMapping, "/welcome.html");
// TODO: direct matches have been removed, path within mapping is indeed ""
testUrl("/welcome.html", mainController, handlerMapping, "");
testUrl("/welcome.x", otherController, handlerMapping, "welcome.x");
testUrl("/welcome/", otherController, handlerMapping, "welcome");
testUrl("/show.html", mainController, handlerMapping, "/show.html");
testUrl("/bookseats.html", mainController, handlerMapping, "/bookseats.html");
testUrl("/show.html", mainController, handlerMapping, "");
testUrl("/bookseats.html", mainController, handlerMapping, "");
}
@Test
@@ -74,10 +75,10 @@ public class SimpleUrlHandlerMappingTests {
testUrl("welcome.html", null, handlerMapping, null);
testUrl("/pathmatchingAA.html", mainController, handlerMapping, "pathmatchingAA.html");
testUrl("/pathmatchingA.html", null, handlerMapping, null);
testUrl("/administrator/pathmatching.html", mainController, handlerMapping, "/administrator/pathmatching.html");
testUrl("/administrator/pathmatching.html", mainController, handlerMapping, "");
testUrl("/administrator/test/pathmatching.html", mainController, handlerMapping, "test/pathmatching.html");
testUrl("/administratort/pathmatching.html", null, handlerMapping, null);
testUrl("/administrator/another/bla.xml", mainController, handlerMapping, "/administrator/another/bla.xml");
testUrl("/administrator/another/bla.xml", mainController, handlerMapping, "");
testUrl("/administrator/another/bla.gif", null, handlerMapping, null);
testUrl("/administrator/test/testlastbit", mainController, handlerMapping, "test/testlastbit");
testUrl("/administrator/test/testla", null, handlerMapping, null);
@@ -89,7 +90,7 @@ public class SimpleUrlHandlerMappingTests {
testUrl("/XpathXXmatching.html", null, handlerMapping, null);
testUrl("/XXpathmatching.html", null, handlerMapping, null);
testUrl("/show12.html", mainController, handlerMapping, "show12.html");
testUrl("/show123.html", mainController, handlerMapping, "/show123.html");
testUrl("/show123.html", mainController, handlerMapping, "");
testUrl("/show1.html", mainController, handlerMapping, "show1.html");
testUrl("/reallyGood-test-is-this.jpeg", mainController, handlerMapping, "reallyGood-test-is-this.jpeg");
testUrl("/reallyGood-tst-is-this.jpeg", null, handlerMapping, null);

View File

@@ -34,6 +34,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse
import org.springframework.util.FileCopyUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.util.patterns.PathPatternParser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -57,7 +58,8 @@ public class AppCacheManifestTransformerTests {
ClassPathResource allowedLocation = new ClassPathResource("test/", getClass());
ResourceWebHandler resourceHandler = new ResourceWebHandler();
ResourceUrlProvider resourceUrlProvider = new ResourceUrlProvider();
resourceUrlProvider.setHandlerMap(Collections.singletonMap("/static/**", resourceHandler));
PathPatternParser parser = new PathPatternParser();
resourceUrlProvider.setHandlerMap(Collections.singletonMap(parser.parse("/static/**"), resourceHandler));
VersionResourceResolver versionResolver = new VersionResourceResolver();
versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));

View File

@@ -39,6 +39,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.util.patterns.PathPatternParser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
@@ -58,7 +59,8 @@ public class CssLinkResourceTransformerTests {
ResourceWebHandler resourceHandler = new ResourceWebHandler();
ResourceUrlProvider resourceUrlProvider = new ResourceUrlProvider();
resourceUrlProvider.setHandlerMap(Collections.singletonMap("/static/**", resourceHandler));
PathPatternParser parser = new PathPatternParser();
resourceUrlProvider.setHandlerMap(Collections.singletonMap(parser.parse("/static/**"), resourceHandler));
VersionResourceResolver versionResolver = new VersionResourceResolver();
versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));

View File

@@ -30,6 +30,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.util.patterns.PathPatternParser;
import static org.junit.Assert.assertEquals;
@@ -69,7 +70,8 @@ public class ResourceTransformerSupportTests {
handler.setLocations(Collections.singletonList(new ClassPathResource("test/", getClass())));
handler.setResourceResolvers(resolvers);
ResourceUrlProvider urlProvider = new ResourceUrlProvider();
urlProvider.setHandlerMap(Collections.singletonMap("/resources/**", handler));
PathPatternParser parser = new PathPatternParser();
urlProvider.setHandlerMap(Collections.singletonMap(parser.parse("/resources/**"), handler));
return urlProvider;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,8 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.DefaultWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;
import org.springframework.web.util.patterns.PathPattern;
import org.springframework.web.util.patterns.PathPatternParser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -55,7 +57,7 @@ public class ResourceUrlProviderTests {
private final ResourceWebHandler handler = new ResourceWebHandler();
private final Map<String, ResourceWebHandler> handlerMap = new HashMap<>();
private final Map<PathPattern, ResourceWebHandler> handlerMap = new HashMap<>();
private final ResourceUrlProvider urlProvider = new ResourceUrlProvider();
@@ -66,7 +68,8 @@ public class ResourceUrlProviderTests {
this.locations.add(new ClassPathResource("testalternatepath/", getClass()));
this.handler.setLocations(locations);
this.handler.afterPropertiesSet();
this.handlerMap.put("/resources/**", this.handler);
PathPattern staticResources = new PathPatternParser().parse("/resources/**");
this.handlerMap.put(staticResources, this.handler);
this.urlProvider.setHandlerMap(this.handlerMap);
}
@@ -122,7 +125,8 @@ public class ResourceUrlProviderTests {
resolvers.add(new PathResourceResolver());
otherHandler.setResourceResolvers(resolvers);
this.handlerMap.put("/resources/*.css", otherHandler);
PathPattern staticResources = new PathPatternParser().parse("/resources/*.css");
this.handlerMap.put(staticResources, otherHandler);
this.urlProvider.setHandlerMap(this.handlerMap);
String url = this.urlProvider.getForLookupPath("/resources/foo.css").blockMillis(5000);
@@ -137,7 +141,8 @@ public class ResourceUrlProviderTests {
context.refresh();
ResourceUrlProvider urlProviderBean = context.getBean(ResourceUrlProvider.class);
assertThat(urlProviderBean.getHandlerMap(), Matchers.hasKey("/resources/**"));
assertThat(urlProviderBean.getHandlerMap(),
Matchers.hasKey(new PathPatternParser().parse("/resources/**")));
assertFalse(urlProviderBean.isAutodetect());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,10 @@ package org.springframework.web.reactive.result.condition;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -27,6 +29,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.util.patterns.PathPattern;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -42,13 +45,14 @@ public class PatternsRequestConditionTests {
@Test
public void prependSlash() {
PatternsRequestCondition c = new PatternsRequestCondition("foo");
assertEquals("/foo", c.getPatterns().iterator().next());
assertEquals("/foo", c.getPatterns().iterator().next().getPatternString());
}
@Test
public void prependNonEmptyPatternsOnly() {
PatternsRequestCondition c = new PatternsRequestCondition("");
assertEquals("Do not prepend empty patterns (SPR-8255)", "", c.getPatterns().iterator().next());
assertEquals("Do not prepend empty patterns (SPR-8255)", "",
c.getPatterns().iterator().next().getPatternString());
}
@Test
@@ -113,13 +117,13 @@ public class PatternsRequestConditionTests {
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("/{foo}.*", match.getPatterns().iterator().next());
assertEquals("/{foo}", match.getPatterns().iterator().next().getPatternString());
condition = new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, false, false, null);
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, true, false, null);
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("/{foo}", match.getPatterns().iterator().next());
assertEquals("/foo.*", match.getPatterns().iterator().next().getPatternString());
}
// SPR-8410
@@ -128,28 +132,30 @@ public class PatternsRequestConditionTests {
public void matchSuffixPatternUsingFileExtensions() throws Exception {
String[] patterns = new String[] {"/jobs/{jobName}"};
Set<String> extensions = Collections.singleton("json");
PatternsRequestCondition condition = new PatternsRequestCondition(patterns, null, null, true, false, extensions);
PatternsRequestCondition condition = new PatternsRequestCondition(patterns, null, true, false, extensions);
ServerWebExchange exchange = createExchange("/jobs/my.job");
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("/jobs/{jobName}", match.getPatterns().iterator().next());
assertEquals("/jobs/{jobName}", match.getPatterns().iterator().next().getPatternString());
exchange = createExchange("/jobs/my.job.json");
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("/jobs/{jobName}.json", match.getPatterns().iterator().next());
Iterator<PathPattern> matchedPatterns = match.getPatterns().iterator();
assertEquals("/jobs/{jobName}", matchedPatterns.next().getPatternString());
assertEquals("/jobs/{jobName}.json", matchedPatterns.next().getPatternString());
}
@Test
public void matchSuffixPatternUsingFileExtensions2() throws Exception {
PatternsRequestCondition condition1 = new PatternsRequestCondition(
new String[] {"/prefix"}, null, null, true, false, Collections.singleton("json"));
new String[] {"/prefix"}, null, true, false, Collections.singleton("json"));
PatternsRequestCondition condition2 = new PatternsRequestCondition(
new String[] {"/suffix"}, null, null, true, false, null);
new String[] {"/suffix"}, null, true, false, null);
PatternsRequestCondition combined = condition1.combine(condition2);
@@ -166,20 +172,20 @@ public class PatternsRequestConditionTests {
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("Should match by default", "/foo/", match.getPatterns().iterator().next());
assertNull("Should not match by default", match);
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, true, null);
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, false, true, null);
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("Trailing slash should be insensitive to useSuffixPatternMatch settings (SPR-6164, SPR-5636)",
"/foo/", match.getPatterns().iterator().next());
"/foo/", match.getPatterns().iterator().next().getPatternString());
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, false, null);
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, true, true, null);
match = condition.getMatchingCondition(exchange);
assertNull(match);
assertNotNull(match);
assertEquals("/foo/", match.getPatterns().iterator().next().getPatternString());
}
@Test
@@ -216,8 +222,8 @@ public class PatternsRequestConditionTests {
PatternsRequestCondition match1 = c1.getMatchingCondition(exchange);
PatternsRequestCondition match2 = c2.getMatchingCondition(exchange);
assertNotNull(match1);
assertEquals(1, match1.compareTo(match2, exchange));
assertNull(match1);
assertEquals("/*.html", match2.getPatterns().iterator().next().getPatternString());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,9 @@ package org.springframework.web.reactive.result.method;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.junit.Before;
import org.junit.Test;
@@ -33,14 +32,15 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.MockWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;
import org.springframework.web.util.ParsingPathMatcher;
import org.springframework.web.util.patterns.PathPattern;
import org.springframework.web.util.patterns.PathPatternParser;
import org.springframework.web.util.patterns.PatternComparatorConsideringPath;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -52,7 +52,9 @@ import static org.junit.Assert.assertNull;
*/
public class HandlerMethodMappingTests {
private AbstractHandlerMethodMapping<String> mapping;
private PathPatternParser patternParser = new PathPatternParser();
private AbstractHandlerMethodMapping<PathPattern> mapping;
private MyHandler handler;
@@ -72,14 +74,14 @@ public class HandlerMethodMappingTests {
@Test(expected = IllegalStateException.class)
public void registerDuplicates() {
this.mapping.registerMapping("foo", this.handler, this.method1);
this.mapping.registerMapping("foo", this.handler, this.method2);
this.mapping.registerMapping(this.patternParser.parse("/foo"), this.handler, this.method1);
this.mapping.registerMapping(this.patternParser.parse("/foo"), this.handler, this.method2);
}
@Test
public void directMatch() throws Exception {
String key = "foo";
this.mapping.registerMapping(key, this.handler, this.method1);
this.mapping.registerMapping(this.patternParser.parse(key), this.handler, this.method1);
Mono<Object> result = this.mapping.getHandler(createExchange(HttpMethod.GET, key));
assertEquals(this.method1, ((HandlerMethod) result.block()).getMethod());
@@ -87,8 +89,8 @@ public class HandlerMethodMappingTests {
@Test
public void patternMatch() throws Exception {
this.mapping.registerMapping("/fo*", this.handler, this.method1);
this.mapping.registerMapping("/f*", this.handler, this.method2);
this.mapping.registerMapping(this.patternParser.parse("/fo*"), this.handler, this.method1);
this.mapping.registerMapping(this.patternParser.parse("/f*"), this.handler, this.method2);
Mono<Object> result = this.mapping.getHandler(createExchange(HttpMethod.GET, "/foo"));
assertEquals(this.method1, ((HandlerMethod) result.block()).getMethod());
@@ -96,8 +98,8 @@ public class HandlerMethodMappingTests {
@Test
public void ambiguousMatch() throws Exception {
this.mapping.registerMapping("/f?o", this.handler, this.method1);
this.mapping.registerMapping("/fo?", this.handler, this.method2);
this.mapping.registerMapping(this.patternParser.parse("/f?o"), this.handler, this.method1);
this.mapping.registerMapping(this.patternParser.parse("/fo?"), this.handler, this.method2);
Mono<Object> result = this.mapping.getHandler(createExchange(HttpMethod.GET, "/foo"));
StepVerifier.create(result).expectError(IllegalStateException.class).verify();
@@ -105,47 +107,41 @@ public class HandlerMethodMappingTests {
@Test
public void registerMapping() throws Exception {
String key1 = "/foo";
String key2 = "/foo*";
PathPattern key1 = this.patternParser.parse("/foo");
PathPattern key2 = this.patternParser.parse("/foo*");
this.mapping.registerMapping(key1, this.handler, this.method1);
this.mapping.registerMapping(key2, this.handler, this.method2);
List directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1);
assertNotNull(directUrlMatches);
assertEquals(1, directUrlMatches.size());
assertEquals(key1, directUrlMatches.get(0));
HandlerMethod match = this.mapping.getMappingRegistry().getMappings().get(key1);
assertNotNull(match);
}
@Test
public void registerMappingWithSameMethodAndTwoHandlerInstances() throws Exception {
String key1 = "foo";
String key2 = "bar";
PathPattern key1 = this.patternParser.parse("/foo");
PathPattern key2 = this.patternParser.parse("/bar");
MyHandler handler1 = new MyHandler();
MyHandler handler2 = new MyHandler();
this.mapping.registerMapping(key1, handler1, this.method1);
this.mapping.registerMapping(key2, handler2, this.method1);
List directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1);
assertNotNull(directUrlMatches);
assertEquals(1, directUrlMatches.size());
assertEquals(key1, directUrlMatches.get(0));
HandlerMethod match = this.mapping.getMappingRegistry().getMappings().get(key1);
assertNotNull(match);
}
@Test
public void unregisterMapping() throws Exception {
String key = "foo";
this.mapping.registerMapping(key, this.handler, this.method1);
this.mapping.registerMapping(this.patternParser.parse(key), this.handler, this.method1);
Mono<Object> result = this.mapping.getHandler(createExchange(HttpMethod.GET, key));
assertNotNull(result.block());
this.mapping.unregisterMapping(key);
this.mapping.unregisterMapping(this.patternParser.parse(key));
result = this.mapping.getHandler(createExchange(HttpMethod.GET, key));
assertNull(result.block());
assertNull(this.mapping.getMappingRegistry().getMappingsByUrl(key));
assertNull(this.mapping.getMappingRegistry().getMappings().get(key));
}
@@ -156,9 +152,9 @@ public class HandlerMethodMappingTests {
}
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<PathPattern> {
private PathMatcher pathMatcher = new ParsingPathMatcher();
private PathPatternParser patternParser = new PathPatternParser();
@Override
protected boolean isHandler(Class<?> beanType) {
@@ -166,26 +162,28 @@ public class HandlerMethodMappingTests {
}
@Override
protected String getMappingForMethod(Method method, Class<?> handlerType) {
protected PathPattern getMappingForMethod(Method method, Class<?> handlerType) {
String methodName = method.getName();
return methodName.startsWith("handler") ? methodName : null;
return methodName.startsWith("handler") ? this.patternParser.parse(methodName) : null;
}
@Override
protected Set<String> getMappingPathPatterns(String key) {
return (this.pathMatcher.isPattern(key) ? Collections.emptySet() : Collections.singleton(key));
protected SortedSet<PathPattern> getMappingPathPatterns(PathPattern key) {
TreeSet<PathPattern> patterns = new TreeSet<>();
patterns.add(key);
return patterns;
}
@Override
protected String getMatchingMapping(String pattern, ServerWebExchange exchange) {
protected PathPattern getMatchingMapping(PathPattern pattern, ServerWebExchange exchange) {
String lookupPath = exchange.getRequest().getURI().getPath();
return (this.pathMatcher.match(pattern, lookupPath) ? pattern : null);
return (pattern.matches(lookupPath) ? pattern : null);
}
@Override
protected Comparator<String> getMappingComparator(ServerWebExchange exchange) {
protected Comparator<PathPattern> getMappingComparator(ServerWebExchange exchange) {
String lookupPath = exchange.getRequest().getURI().getPath();
return this.pathMatcher.getPatternComparator(lookupPath);
return new PatternComparatorConsideringPath(lookupPath);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,9 +24,13 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -59,6 +63,7 @@ import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.patterns.PathPattern;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
@@ -81,7 +86,6 @@ public class RequestMappingInfoHandlerMappingTests {
private ServerHttpRequest request;
@Before
public void setUp() throws Exception {
this.handlerMapping = new TestRequestMappingInfoHandlerMapping();
@@ -93,9 +97,11 @@ public class RequestMappingInfoHandlerMappingTests {
public void getMappingPathPatterns() throws Exception {
String[] patterns = {"/foo/*", "/foo", "/bar/*", "/bar"};
RequestMappingInfo info = paths(patterns).build();
Set<String> actual = this.handlerMapping.getMappingPathPatterns(info);
Set<PathPattern> actual = this.handlerMapping.getMappingPathPatterns(info);
assertEquals(new HashSet<>(Arrays.asList(patterns)), actual);
assertThat(actual.stream().map(PathPattern::getPatternString).collect(Collectors.toList()),
Matchers.containsInAnyOrder(new String[]{"/foo/*", "/foo", "/bar/*", "/bar",
"/foo/*/", "/foo/", "/bar/*/", "/bar/"}));
}
@Test
@@ -123,6 +129,9 @@ public class RequestMappingInfoHandlerMappingTests {
}
@Test
@Ignore
// TODO: for "" patterns, should we generate the "/" variant (and break SPR-8255)
// or handle matching in a different way? Here, setTrailingSlashMatch is set to false for tests
public void getHandlerEmptyPathMatch() throws Exception {
String[] patterns = new String[] {""};
Method expected = resolveMethod(new TestController(), patterns, null, null);
@@ -274,7 +283,9 @@ public class RequestMappingInfoHandlerMappingTests {
ServerWebExchange exchange = createExchange();
this.handlerMapping.handleMatch(key, "/1/2", exchange);
assertEquals("/{path1}/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE));
PathPattern pattern = (PathPattern) exchange.getAttributes()
.get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
assertEquals("/{path1}/2", pattern.getPatternString());
}
@Test
@@ -285,7 +296,9 @@ public class RequestMappingInfoHandlerMappingTests {
this.handlerMapping.handleMatch(key, "/1/2", exchange);
assertEquals("/1/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE));
PathPattern pattern = (PathPattern) exchange.getAttributes()
.get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
assertEquals("/1/2", pattern.getPatternString());
}
@Test
@@ -534,7 +547,6 @@ public class RequestMappingInfoHandlerMappingTests {
if (annot != null) {
BuilderConfiguration options = new BuilderConfiguration();
options.setPathHelper(getPathHelper());
options.setPathMatcher(getPathMatcher());
options.setSuffixPatternMatch(true);
options.setTrailingSlashMatch(true);
return paths(annot.value()).methods(annot.method())

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.stream.Collectors;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
@@ -42,12 +45,15 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.reactive.accept.MappingContentTypeResolver;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.util.patterns.PathPattern;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -71,12 +77,14 @@ public class RequestMappingHandlerMappingTests {
@Test
public void useRegisteredSuffixPatternMatch() {
assertTrue(this.handlerMapping.useSuffixPatternMatch());
assertTrue(this.handlerMapping.useRegisteredSuffixPatternMatch());
assertFalse(this.handlerMapping.useSuffixPatternMatch());
assertFalse(this.handlerMapping.useRegisteredSuffixPatternMatch());
MappingContentTypeResolver contentTypeResolver = mock(MappingContentTypeResolver.class);
when(contentTypeResolver.getKeys()).thenReturn(Collections.singleton("json"));
this.handlerMapping.setUseSuffixPatternMatch(true);
this.handlerMapping.setUseRegisteredSuffixPatternMatch(true);
this.handlerMapping.setContentTypeResolver(contentTypeResolver);
this.handlerMapping.afterPropertiesSet();
@@ -111,15 +119,8 @@ public class RequestMappingHandlerMappingTests {
@Test
public void useSuffixPatternMatch() {
assertTrue(this.handlerMapping.useSuffixPatternMatch());
assertTrue(this.handlerMapping.useRegisteredSuffixPatternMatch());
this.handlerMapping.setUseSuffixPatternMatch(false);
assertFalse(this.handlerMapping.useSuffixPatternMatch());
this.handlerMapping.setUseRegisteredSuffixPatternMatch(false);
assertFalse("'false' registeredSuffixPatternMatch shouldn't impact suffixPatternMatch",
this.handlerMapping.useSuffixPatternMatch());
assertFalse(this.handlerMapping.useRegisteredSuffixPatternMatch());
this.handlerMapping.setUseRegisteredSuffixPatternMatch(true);
assertTrue("'true' registeredSuffixPatternMatch should enable suffixPatternMatch",
@@ -197,9 +198,10 @@ public class RequestMappingHandlerMappingTests {
assertNotNull(info);
Set<String> paths = info.getPatternsCondition().getPatterns();
assertEquals(1, paths.size());
assertEquals(path, paths.iterator().next());
Set<String> paths = info.getPatternsCondition().getPatterns()
.stream().map(p -> p.getPatternString()).collect(Collectors.toSet());
assertEquals(2, paths.size());
assertThat(paths, Matchers.containsInAnyOrder(path, path + "/"));
Set<RequestMethod> methods = info.getMethodsCondition().getMethods();
assertEquals(1, methods.size());