Remove PathPatternRegistry
This commit is contained in:
parent
e7b77cb2b6
commit
fac35ebec2
|
|
@ -17,6 +17,8 @@
|
|||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
|
@ -25,6 +27,7 @@ import org.springframework.beans.BeansException;
|
|||
import org.springframework.http.server.reactive.PathContainer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
|
|
@ -51,8 +54,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
|
||||
private boolean lazyInitHandlers = false;
|
||||
|
||||
@Nullable
|
||||
private PathPatternRegistry<Object> patternRegistry;
|
||||
private final Map<PathPattern, Object> handlerMap = new LinkedHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -70,12 +72,12 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the registered handlers as an unmodifiable Map, with the registered path
|
||||
* pattern as key and the handler object (or handler bean name in case of a lazy-init handler)
|
||||
* as value.
|
||||
* Return a read-only view of registered path patterns and handlers which may
|
||||
* may be an actual handler instance or the bean name of lazily initialized
|
||||
* handler.
|
||||
*/
|
||||
public final Map<PathPattern, Object> getHandlerMap() {
|
||||
return (this.patternRegistry != null ? this.patternRegistry.getPatternsMap() : Collections.emptyMap());
|
||||
return Collections.unmodifiableMap(this.handlerMap);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -111,25 +113,26 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
* @see org.springframework.web.util.pattern.PathPattern
|
||||
*/
|
||||
@Nullable
|
||||
protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange) throws Exception {
|
||||
if (this.patternRegistry != null) {
|
||||
PathMatchResult<Object> bestMatch = this.patternRegistry.findFirstMatch(lookupPath);
|
||||
if (bestMatch != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Matching patterns for request [" + lookupPath + "] are " + bestMatch);
|
||||
}
|
||||
PathContainer pathWithinMapping = bestMatch.getPattern().extractPathWithinPattern(lookupPath);
|
||||
Object handler = bestMatch.getHandler();
|
||||
return handleMatch(handler, bestMatch.getPattern(), pathWithinMapping, exchange);
|
||||
}
|
||||
}
|
||||
protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange)
|
||||
throws Exception {
|
||||
|
||||
// No handler found...
|
||||
return null;
|
||||
return this.handlerMap.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(lookupPath))
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.findFirst()
|
||||
.map(entry -> {
|
||||
PathPattern pattern = entry.getKey();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Matching pattern for request [" + lookupPath + "] is " + pattern);
|
||||
}
|
||||
PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath);
|
||||
return handleMatch(entry.getValue(), pattern, pathWithinMapping, exchange);
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping,
|
||||
ServerWebExchange exchange) throws Exception {
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
// Bean name or resolved handler?
|
||||
if (handler instanceof String) {
|
||||
|
|
@ -152,10 +155,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
* for example to enforce specific preconditions expressed in URL mappings.
|
||||
* @param handler the handler object to validate
|
||||
* @param exchange current exchange
|
||||
* @throws Exception if validation failed
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void validateHandler(Object handler, ServerWebExchange exchange) throws Exception {
|
||||
protected void validateHandler(Object handler, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,7 +167,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
* @throws BeansException if the handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
|
||||
protected void registerHandler(String[] urlPaths, String beanName)
|
||||
throws BeansException, IllegalStateException {
|
||||
|
||||
Assert.notNull(urlPaths, "URL path array must not be null");
|
||||
for (String urlPath : urlPaths) {
|
||||
registerHandler(urlPath, beanName);
|
||||
|
|
@ -180,11 +184,26 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
* @throws BeansException if the handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
|
||||
protected void registerHandler(String urlPath, Object handler)
|
||||
throws BeansException, IllegalStateException {
|
||||
|
||||
Assert.notNull(urlPath, "URL path must not be null");
|
||||
Assert.notNull(handler, "Handler object must not be null");
|
||||
Object resolvedHandler = handler;
|
||||
|
||||
// Parse path pattern
|
||||
urlPath = prependLeadingSlash(urlPath);
|
||||
PathPattern pattern = getPathPatternParser().parse(urlPath);
|
||||
if (this.handlerMap.containsKey(pattern)) {
|
||||
Object existingHandler = this.handlerMap.get(pattern);
|
||||
if (existingHandler != null) {
|
||||
if (existingHandler != resolvedHandler) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot map " + getHandlerDescription(handler) + " to [" + urlPath + "]: " +
|
||||
"there is already " + getHandlerDescription(existingHandler) + " mapped.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Eagerly resolve handler if referencing singleton via name.
|
||||
if (!this.lazyInitHandlers && handler instanceof String) {
|
||||
|
|
@ -193,32 +212,26 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||
resolvedHandler = obtainApplicationContext().getBean(handlerName);
|
||||
}
|
||||
}
|
||||
if (this.patternRegistry == null) {
|
||||
this.patternRegistry = new PathPatternRegistry<>(getPathPatternParser());
|
||||
}
|
||||
|
||||
Map<PathPattern, Object> patternsMap = this.patternRegistry.getPatternsMap();
|
||||
if (patternsMap.containsKey(urlPath)) {
|
||||
Object mappedHandler = patternsMap.get(urlPath);
|
||||
if (mappedHandler != null) {
|
||||
if (mappedHandler != resolvedHandler) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
|
||||
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.patternRegistry.register(urlPath, resolvedHandler);
|
||||
}
|
||||
|
||||
// Register resolved handler
|
||||
this.handlerMap.put(pattern, resolvedHandler);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
|
||||
}
|
||||
}
|
||||
|
||||
private static String prependLeadingSlash(String pattern) {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
return "/" + pattern;
|
||||
}
|
||||
else {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
private String getHandlerDescription(Object handler) {
|
||||
return "handler " + (handler instanceof String ? "'" + handler + "'" : "of type [" + handler.getClass() + "]");
|
||||
return "handler " + (handler instanceof String ?
|
||||
"'" + handler + "'" : "of type [" + handler.getClass() + "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
/**
|
||||
* Result of path match performed through a {@link PathPatternRegistry}.
|
||||
* Each result contains the matched pattern and handler of type {@code T}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
* @see PathPatternRegistry
|
||||
*/
|
||||
public class PathMatchResult<T> implements Comparable<PathMatchResult<T>> {
|
||||
|
||||
private final PathPattern pattern;
|
||||
|
||||
private final T handler;
|
||||
|
||||
|
||||
public PathMatchResult(PathPattern pattern, T handler) {
|
||||
Assert.notNull(pattern, "PathPattern must not be null");
|
||||
Assert.notNull(handler, "Handler must not be null");
|
||||
this.pattern = pattern;
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@link PathPattern} that matched the incoming request.
|
||||
*/
|
||||
public PathPattern getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request handler associated with the {@link PathPattern}.
|
||||
*/
|
||||
public T getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull PathMatchResult<T> other) {
|
||||
int index = this.pattern.compareTo(other.pattern);
|
||||
return (index != 0 ? index : this.getPatternString().compareTo(other.getPatternString()));
|
||||
}
|
||||
|
||||
private String getPatternString() {
|
||||
return this.pattern.getPatternString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathMatchResult{pattern=" + this.pattern + ", handler=" + this.handler + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.http.server.reactive.PathContainer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Registry that holds {@code PathPattern}s instances
|
||||
* and allows matching against them with a lookup path.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PathPatternRegistry<T> {
|
||||
|
||||
private final PathPatternParser pathPatternParser;
|
||||
|
||||
private final Map<PathPattern, T> patternsMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code PathPatternRegistry} with
|
||||
* a default instance of {@link PathPatternParser}.
|
||||
*/
|
||||
public PathPatternRegistry() {
|
||||
this(new PathPatternParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code PathPatternRegistry} using
|
||||
* the provided instance of {@link PathPatternParser}.
|
||||
* @param patternParser the {@link PathPatternParser} to use
|
||||
*/
|
||||
public PathPatternRegistry(PathPatternParser patternParser) {
|
||||
this(patternParser, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code PathPatternRegistry} using
|
||||
* the provided instance of {@link PathPatternParser}
|
||||
* and the given map of {@link PathPattern}.
|
||||
* @param patternParser the {@link PathPatternParser} to use
|
||||
* @param patternsMap the map of {@link PathPattern} to use
|
||||
*/
|
||||
public PathPatternRegistry(PathPatternParser patternParser, Map<PathPattern, T> patternsMap) {
|
||||
this.pathPatternParser = patternParser;
|
||||
this.patternsMap = new HashMap<>(patternsMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a (read-only) map of all patterns and associated values.
|
||||
*/
|
||||
public Map<PathPattern, T> getPatternsMap() {
|
||||
return Collections.unmodifiableMap(this.patternsMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given {@code rawPattern} and adds it to this registry.
|
||||
* @param rawPattern raw path pattern to parse and register
|
||||
* @param handler the associated handler object
|
||||
*/
|
||||
public void register(String rawPattern, T handler) {
|
||||
String fixedPattern = prependLeadingSlash(rawPattern);
|
||||
PathPattern newPattern = this.pathPatternParser.parse(fixedPattern);
|
||||
this.patternsMap.put(newPattern, handler);
|
||||
}
|
||||
|
||||
private static String prependLeadingSlash(String pattern) {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
return "/" + pattern;
|
||||
}
|
||||
else {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return patterns matching the given {@code lookupPath}.
|
||||
* <p>The returned set sorted with the most specific
|
||||
* patterns first, according to the given {@code lookupPath}.
|
||||
* @param lookupPath the URL lookup path to be matched against
|
||||
*/
|
||||
public SortedSet<PathMatchResult<T>> findMatches(PathContainer lookupPath) {
|
||||
return this.patternsMap.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(lookupPath))
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.map(entry -> new PathMatchResult<>(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return, if any, the most specific {@code PathPattern} matching the given {@code lookupPath}.
|
||||
* @param lookupPath the URL lookup path to be matched against
|
||||
*/
|
||||
@Nullable
|
||||
public PathMatchResult<T> findFirstMatch(PathContainer lookupPath) {
|
||||
return this.patternsMap.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(lookupPath))
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.findFirst()
|
||||
.map(entry -> new PathMatchResult<>(entry.getKey(), entry.getValue()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all {@link PathPattern}s from this registry
|
||||
*/
|
||||
public void clear() {
|
||||
this.patternsMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,13 +18,14 @@ package org.springframework.web.reactive.resource;
|
|||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -34,8 +35,8 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||
import org.springframework.http.server.reactive.PathContainer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.handler.PathMatchResult;
|
||||
import org.springframework.web.reactive.handler.PathPatternRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
|
@ -54,41 +55,33 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
|||
*/
|
||||
public class ResourceUrlProvider implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
private static final Log logger = LogFactory.getLog(ResourceUrlProvider.class);
|
||||
|
||||
private PathPatternRegistry<ResourceWebHandler> patternRegistry = new PathPatternRegistry<>();
|
||||
|
||||
private final PathPatternParser patternParser;
|
||||
|
||||
private final Map<PathPattern, ResourceWebHandler> handlerMap = new LinkedHashMap<>();
|
||||
|
||||
private boolean autodetect = true;
|
||||
|
||||
|
||||
/**
|
||||
* Configure a {@code PathPatternParser} to use when comparing target lookup path
|
||||
* against resource mappings.
|
||||
*/
|
||||
public void setPathPatternParser(PathPatternParser patternParser) {
|
||||
this.patternRegistry = new PathPatternRegistry<>(patternParser);
|
||||
public ResourceUrlProvider() {
|
||||
this(new PathPatternParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually configure the resource mappings.
|
||||
* <p><strong>Note:</strong> by default resource mappings are auto-detected
|
||||
* from the Spring {@code ApplicationContext}. However if this property is
|
||||
* used, the auto-detection is turned off.
|
||||
*/
|
||||
public void setHandlerMap(@Nullable Map<String, ResourceWebHandler> handlerMap) {
|
||||
if (handlerMap != null) {
|
||||
this.patternRegistry.clear();
|
||||
handlerMap.forEach(this.patternRegistry::register);
|
||||
this.autodetect = false;
|
||||
}
|
||||
public ResourceUrlProvider(PathPatternParser patternParser) {
|
||||
Assert.notNull(patternParser, "'patternParser' is required.");
|
||||
this.patternParser = patternParser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the resource mappings, either manually configured or auto-detected
|
||||
* when the Spring {@code ApplicationContext} is refreshed.
|
||||
* Return a read-only view of the resource handler mappings either manually
|
||||
* configured or auto-detected when the Spring {@code ApplicationContext}
|
||||
* is refreshed.
|
||||
*/
|
||||
public Map<PathPattern, ResourceWebHandler> getHandlerMap() {
|
||||
return this.patternRegistry.getPatternsMap();
|
||||
return Collections.unmodifiableMap(this.handlerMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,12 +92,13 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
|||
return this.autodetect;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (isAutodetect()) {
|
||||
this.patternRegistry.clear();
|
||||
this.handlerMap.clear();
|
||||
detectResourceHandlers(event.getApplicationContext());
|
||||
if (!this.patternRegistry.getPatternsMap().isEmpty()) {
|
||||
if (!this.handlerMap.isEmpty()) {
|
||||
this.autodetect = false;
|
||||
}
|
||||
else if(logger.isDebugEnabled()) {
|
||||
|
|
@ -113,17 +107,15 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected void detectResourceHandlers(ApplicationContext appContext) {
|
||||
private void detectResourceHandlers(ApplicationContext context) {
|
||||
logger.debug("Looking for resource handler mappings");
|
||||
|
||||
Map<String, SimpleUrlHandlerMapping> map = appContext.getBeansOfType(SimpleUrlHandlerMapping.class);
|
||||
Map<String, SimpleUrlHandlerMapping> map = context.getBeansOfType(SimpleUrlHandlerMapping.class);
|
||||
List<SimpleUrlHandlerMapping> handlerMappings = new ArrayList<>(map.values());
|
||||
AnnotationAwareOrderComparator.sort(handlerMappings);
|
||||
|
||||
for (SimpleUrlHandlerMapping hm : handlerMappings) {
|
||||
for (PathPattern pattern : hm.getHandlerMap().keySet()) {
|
||||
Object handler = hm.getHandlerMap().get(pattern);
|
||||
handlerMappings.forEach(mapping -> {
|
||||
mapping.getHandlerMap().forEach((pattern, handler) -> {
|
||||
if (handler instanceof ResourceWebHandler) {
|
||||
ResourceWebHandler resourceHandler = (ResourceWebHandler) handler;
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -131,12 +123,41 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
|||
"locations=" + resourceHandler.getLocations() + ", " +
|
||||
"resolvers=" + resourceHandler.getResourceResolvers());
|
||||
}
|
||||
this.patternRegistry.register(pattern.getPatternString(), resourceHandler);
|
||||
this.handlerMap.put(pattern, resourceHandler);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually configure the resource mappings.
|
||||
* <p><strong>Note:</strong> by default resource mappings are auto-detected
|
||||
* from the Spring {@code ApplicationContext}. However if this property is
|
||||
* used, the auto-detection is turned off.
|
||||
*/
|
||||
public void registerHandlers(@Nullable Map<String, ResourceWebHandler> handlerMap) {
|
||||
if (handlerMap == null) {
|
||||
return;
|
||||
}
|
||||
this.handlerMap.clear();
|
||||
handlerMap.forEach((rawPattern, resourceWebHandler) -> {
|
||||
rawPattern = prependLeadingSlash(rawPattern);
|
||||
PathPattern pattern = this.patternParser.parse(rawPattern);
|
||||
this.handlerMap.put(pattern, resourceWebHandler);
|
||||
});
|
||||
this.autodetect = false;
|
||||
}
|
||||
|
||||
private static String prependLeadingSlash(String pattern) {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
return "/" + pattern;
|
||||
}
|
||||
else {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A variation on {@link #getForLookupPath(PathContainer)} that accepts a
|
||||
* full request URL path and returns the full request URL path to expose
|
||||
|
|
@ -187,19 +208,19 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
|||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting resource URL for lookup path \"" + lookupPath + "\"");
|
||||
}
|
||||
|
||||
Set<PathMatchResult<ResourceWebHandler>> matches = this.patternRegistry.findMatches(lookupPath);
|
||||
|
||||
return Flux.fromIterable(matches).next()
|
||||
.flatMap(result -> {
|
||||
PathContainer path = result.getPattern().extractPathWithinPattern(lookupPath);
|
||||
return this.handlerMap.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(lookupPath))
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.findFirst()
|
||||
.map(entry -> {
|
||||
PathContainer path = entry.getKey().extractPathWithinPattern(lookupPath);
|
||||
int endIndex = lookupPath.elements().size() - path.elements().size();
|
||||
PathContainer mapping = PathContainer.subPath(lookupPath, 0, endIndex);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking ResourceResolverChain for URL pattern " +
|
||||
"\"" + result.getPattern() + "\"");
|
||||
"\"" + entry.getKey() + "\"");
|
||||
}
|
||||
ResourceWebHandler handler = result.getHandler();
|
||||
ResourceWebHandler handler = entry.getValue();
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
ResourceResolverChain chain = new DefaultResourceResolverChain(resolvers);
|
||||
return chain.resolveUrlPath(path.value(), handler.getLocations())
|
||||
|
|
@ -209,7 +230,9 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
|||
}
|
||||
return mapping.value() + resolvedPath;
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
.orElse(Mono.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.http.server.reactive.PathContainer;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
import org.springframework.web.util.pattern.PatternParseException;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link PathPatternRegistry}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class PathPatternRegistryTests {
|
||||
|
||||
private final PathPatternRegistry<Object> registry = new PathPatternRegistry();
|
||||
|
||||
private final PathPatternParser parser = new PathPatternParser();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldPrependPatternsWithSlash() {
|
||||
this.registry.register("foo/bar", new Object());
|
||||
assertThat(this.registry.getPatternsMap().keySet(), contains(pattern("/foo/bar")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotRegisterInvalidPatterns() {
|
||||
this.thrown.expect(PatternParseException.class);
|
||||
this.thrown.expectMessage(Matchers.containsString("Expected close capture character after variable name"));
|
||||
this.registry.register("/{invalid", new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerPatternsWithSameSpecificity() {
|
||||
PathPattern fooOne = this.parser.parse("/fo?");
|
||||
PathPattern fooTwo = this.parser.parse("/f?o");
|
||||
assertThat(fooOne.compareTo(fooTwo), is(0));
|
||||
|
||||
this.registry.register("/fo?", new Object());
|
||||
this.registry.register("/f?o", new Object());
|
||||
|
||||
PathContainer path = PathContainer.parse("/foo", StandardCharsets.UTF_8);
|
||||
Set<PathMatchResult<Object>> matches = this.registry.findMatches(path);
|
||||
assertThat(toPatterns(matches), contains(pattern("/f?o"), pattern("/fo?")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNoMatch() {
|
||||
this.registry.register("/foo/{bar}", new Object());
|
||||
PathContainer path = PathContainer.parse("/other", StandardCharsets.UTF_8);
|
||||
assertThat(this.registry.findMatches(path), hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderMatchesBySpecificity() {
|
||||
this.registry.register("/foo/{*baz}", new Object());
|
||||
this.registry.register("/foo/bar/baz", new Object());
|
||||
this.registry.register("/foo/bar/{baz}", new Object());
|
||||
PathContainer path = PathContainer.parse("/foo/bar/baz", StandardCharsets.UTF_8);
|
||||
Set<PathMatchResult<Object>> matches = this.registry.findMatches(path);
|
||||
assertThat(toPatterns(matches), contains(pattern("/foo/bar/baz"), pattern("/foo/bar/{baz}"),
|
||||
pattern("/foo/{*baz}")));
|
||||
}
|
||||
|
||||
|
||||
private List<PathPattern> toPatterns(Collection<PathMatchResult<Object>> results) {
|
||||
return results.stream().map(PathMatchResult::getPattern).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static PathPatternMatcher pattern(String pattern) {
|
||||
return new PathPatternMatcher(pattern);
|
||||
}
|
||||
|
||||
private static class PathPatternMatcher extends BaseMatcher<PathPattern> {
|
||||
|
||||
private final String pattern;
|
||||
|
||||
public PathPatternMatcher(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
if(item != null && item instanceof PathPattern) {
|
||||
return ((PathPattern) item).getPatternString().equals(pattern);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ public class AppCacheManifestTransformerTests {
|
|||
ClassPathResource allowedLocation = new ClassPathResource("test/", getClass());
|
||||
ResourceWebHandler resourceHandler = new ResourceWebHandler();
|
||||
ResourceUrlProvider resourceUrlProvider = new ResourceUrlProvider();
|
||||
resourceUrlProvider.setHandlerMap(Collections.singletonMap("/static/**", resourceHandler));
|
||||
resourceUrlProvider.registerHandlers(Collections.singletonMap("/static/**", resourceHandler));
|
||||
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class CssLinkResourceTransformerTests {
|
|||
ResourceWebHandler resourceHandler = new ResourceWebHandler();
|
||||
|
||||
ResourceUrlProvider resourceUrlProvider = new ResourceUrlProvider();
|
||||
resourceUrlProvider.setHandlerMap(Collections.singletonMap("/static/**", resourceHandler));
|
||||
resourceUrlProvider.registerHandlers(Collections.singletonMap("/static/**", resourceHandler));
|
||||
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ public class ResourceTransformerSupportTests {
|
|||
handler.setLocations(Collections.singletonList(new ClassPathResource("test/", getClass())));
|
||||
handler.setResourceResolvers(resolvers);
|
||||
ResourceUrlProvider urlProvider = new ResourceUrlProvider();
|
||||
urlProvider.setHandlerMap(Collections.singletonMap("/resources/**", handler));
|
||||
urlProvider.registerHandlers(Collections.singletonMap("/resources/**", handler));
|
||||
return urlProvider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public class ResourceUrlProviderTests {
|
|||
this.handler.setLocations(locations);
|
||||
this.handler.afterPropertiesSet();
|
||||
this.handlerMap.put("/resources/**", this.handler);
|
||||
this.urlProvider.setHandlerMap(this.handlerMap);
|
||||
this.urlProvider.registerHandlers(this.handlerMap);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ public class ResourceUrlProviderTests {
|
|||
otherHandler.setResourceResolvers(resolvers);
|
||||
|
||||
this.handlerMap.put("/resources/*.css", otherHandler);
|
||||
this.urlProvider.setHandlerMap(this.handlerMap);
|
||||
this.urlProvider.registerHandlers(this.handlerMap);
|
||||
|
||||
PathContainer path = PathContainer.parse("/resources/foo.css", StandardCharsets.UTF_8);
|
||||
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
|
||||
|
|
|
|||
Loading…
Reference in New Issue