From 6f537ca74b8b2338614dc3a0b5467c9c7ea50b6f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 17 Jun 2011 09:39:49 +0000 Subject: [PATCH] SPR-7812 RequestCondition refactoring with the possibility for custom request conditions in mind. git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4566 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../mvc/method/RequestMappingInfo.java | 202 ++++------------ .../RequestMappingInfoHandlerMapping.java | 82 ++----- .../RequestMappingHandlerMapping.java | 25 +- ....java => AbstractNameValueExpression.java} | 23 +- .../condition/ConsumesRequestCondition.java | 228 +++++++++++------- .../condition/HeadersRequestCondition.java | 154 +++++++----- .../LogicalConjunctionRequestCondition.java | 54 ----- .../LogicalDisjunctionRequestCondition.java | 51 ---- .../method/condition/MediaTypeExpression.java | 96 ++++++++ .../condition/MediaTypesRequestCondition.java | 126 ---------- .../condition/ParamsRequestCondition.java | 138 ++++++----- .../condition/PatternsRequestCondition.java | 228 ++++++++++++++++++ .../condition/ProducesRequestCondition.java | 198 ++++++++++----- .../method/condition/RequestCondition.java | 38 ++- .../condition/RequestConditionFactory.java | 194 --------------- ...site.java => RequestConditionSupport.java} | 60 +++-- .../RequestMethodsRequestCondition.java | 164 +++++-------- .../RequestMappingInfoComparatorTests.java | 41 ++-- ...RequestMappingInfoHandlerMappingTests.java | 24 +- .../mvc/method/RequestMappingInfoTests.java | 206 +++++++--------- .../ConsumesRequestConditionTests.java | 77 +++--- .../HeadersRequestConditionTests.java | 48 ++-- .../ParamsRequestConditionTests.java | 40 +-- .../ProducesRequestConditionTests.java | 102 ++++---- .../RequestMethodsRequestConditionTests.java | 24 +- 25 files changed, 1262 insertions(+), 1361 deletions(-) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/{AbstractNameValueCondition.java => AbstractNameValueExpression.java} (75%) delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/{RequestConditionComposite.java => RequestConditionSupport.java} (52%) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java index b22ae2c2ab2..883fcd0b682 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java @@ -16,31 +16,19 @@ package org.springframework.web.servlet.mvc.method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.springframework.util.PathMatcher; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition; import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition; import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition; -import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition; /** - * Contains a set of conditions to match to a given request such as URL patterns, HTTP methods, request parameters - * and headers. - * - *

Two {@link RequestMappingInfo}s can be combined resulting in a new {@link RequestMappingInfo} with conditions - * from both. A {@link RequestMappingInfo} can also match itself to an HTTP request resulting in a new - * {@link RequestMappingInfo} with the subset of conditions relevant to the request. + * Contains request mapping conditions to be matched to a given request. * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -48,7 +36,7 @@ import org.springframework.web.servlet.mvc.method.condition.RequestMethodsReques */ public final class RequestMappingInfo { - private final Set patterns; + private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; @@ -63,24 +51,15 @@ public final class RequestMappingInfo { private int hash; /** - * Creates a new {@code RequestMappingInfo} instance with the given URL patterns and HTTP methods. - * - *

Package protected for testing purposes. + * Creates a new {@code RequestMappingInfo} instance. */ - RequestMappingInfo(Collection patterns, RequestMethod[] methods) { - this(patterns, RequestConditionFactory.parseMethods(methods), null, null, null, null); - } - - /** - * Creates a new {@code RequestMappingInfo} instance with a full set of conditions. - */ - public RequestMappingInfo(Collection patterns, - RequestMethodsRequestCondition methodsCondition, - ParamsRequestCondition paramsCondition, - HeadersRequestCondition headersCondition, - ConsumesRequestCondition consumesCondition, - ProducesRequestCondition producesCondition) { - this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns)); + public RequestMappingInfo(PatternsRequestCondition patternsCondition, + RequestMethodsRequestCondition methodsCondition, + ParamsRequestCondition paramsCondition, + HeadersRequestCondition headersCondition, + ConsumesRequestCondition consumesCondition, + ProducesRequestCondition producesCondition) { + this.patternsCondition = patternsCondition != null ? patternsCondition : new PatternsRequestCondition(); this.methodsCondition = methodsCondition != null ? methodsCondition : new RequestMethodsRequestCondition(); this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition(); this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition(); @@ -88,67 +67,52 @@ public final class RequestMappingInfo { this.producesCondition = producesCondition != null ? producesCondition : new ProducesRequestCondition(); } - private static Set prependLeadingSlash(Collection patterns) { - if (patterns == null) { - return Collections.emptySet(); - } - Set result = new LinkedHashSet(patterns.size()); - for (String pattern : patterns) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - result.add(pattern); - } - return result; - } - - private static Set asUnmodifiableSet(Collection collection) { - if (collection == null) { - return Collections.emptySet(); - } - Set result = new LinkedHashSet(collection); - return Collections.unmodifiableSet(result); + /** + * Package protected, used for testing. + */ + RequestMappingInfo(String[] patterns, RequestMethod... methods) { + this(new PatternsRequestCondition(patterns), new RequestMethodsRequestCondition(methods), null, null, null, null); } /** * Returns the patterns of this request mapping info. */ - public Set getPatterns() { - return patterns; + public PatternsRequestCondition getPatternsCondition() { + return patternsCondition; } /** - * Returns the request method conditions of this request mapping info. + * Returns the request method condition of this request mapping info. */ - public RequestMethodsRequestCondition getMethods() { + public RequestMethodsRequestCondition getMethodsCondition() { return methodsCondition; } /** - * Returns the request parameters conditions of this request mapping info. + * Returns the request parameters condition of this request mapping info. */ - public ParamsRequestCondition getParams() { + public ParamsRequestCondition getParamsCondition() { return paramsCondition; } /** - * Returns the request headers conditions of this request mapping info. + * Returns the request headers condition of this request mapping info. */ - public HeadersRequestCondition getHeaders() { + public HeadersRequestCondition getHeadersCondition() { return headersCondition; } /** - * Returns the request consumes conditions of this request mapping info. + * Returns the request consumes condition of this request mapping info. */ - public ConsumesRequestCondition getConsumes() { + public ConsumesRequestCondition getConsumesCondition() { return consumesCondition; } /** - * Returns the request produces conditions of this request mapping info. + * Returns the request produces condition of this request mapping info. */ - public ProducesRequestCondition getProduces() { + public ProducesRequestCondition getProducesCondition() { return producesCondition; } @@ -167,110 +131,44 @@ public final class RequestMappingInfo { *

  • Consumes are combined as per {@link ConsumesRequestCondition#combine(ConsumesRequestCondition)}. * * @param methodKey the key to combine with - * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns * @return a new request mapping info containing conditions from both keys */ - public RequestMappingInfo combine(RequestMappingInfo methodKey, PathMatcher pathMatcher) { - Set patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher); + public RequestMappingInfo combine(RequestMappingInfo methodKey) { + PatternsRequestCondition patterns = this.patternsCondition.combine(methodKey.patternsCondition); RequestMethodsRequestCondition methods = this.methodsCondition.combine(methodKey.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(methodKey.producesCondition); - + return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces); } - private static Set combinePatterns(Collection typePatterns, - Collection methodPatterns, - PathMatcher pathMatcher) { - Set result = new LinkedHashSet(); - if (!typePatterns.isEmpty() && !methodPatterns.isEmpty()) { - for (String pattern1 : typePatterns) { - for (String pattern2 : methodPatterns) { - result.add(pathMatcher.combine(pattern1, pattern2)); - } - } - } - else if (!typePatterns.isEmpty()) { - result.addAll(typePatterns); - } - else if (!methodPatterns.isEmpty()) { - result.addAll(methodPatterns); - } - else { - result.add(""); - } - return result; - } - /** - * Returns a new {@code RequestMappingInfo} that contains all conditions of this key that are relevant to the request. - *
      - *
    • The list of URL path patterns is trimmed to contain the patterns that match the URL with matching patterns - * sorted via {@link PathMatcher#getPatternComparator(String)}. - *
    • The list of HTTP methods is trimmed to contain only the method of the request. - *
    • Request parameter and request header conditions are included in full. - *
    • The list of consumes conditions is trimmed and sorted to match the request "Content-Type" header. - *
    - * @param lookupPath mapping lookup path within the current servlet mapping if applicable + * Returns a new {@code RequestMappingInfo} with conditions relevant to the current request. + * For example the list of URL path patterns is trimmed to contain the patterns that match the URL. * @param request the current request - * @param pathMatcher to check for matching patterns * @return a new request mapping info that contains all matching attributes, or {@code null} if not all conditions match */ - public RequestMappingInfo getMatchingRequestMapping(String lookupPath, HttpServletRequest request, PathMatcher pathMatcher) { - RequestMethodsRequestCondition matchingMethodCondition = methodsCondition.getMatchingCondition(request); - ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request); - HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request); - ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request); - ProducesRequestCondition matchingProducesCondition = producesCondition.getMatchingCondition(request); + public RequestMappingInfo getMatchingRequestMapping(HttpServletRequest request) { + RequestMethodsRequestCondition matchingMethod = methodsCondition.getMatchingCondition(request); + ParamsRequestCondition matchingParams = paramsCondition.getMatchingCondition(request); + HeadersRequestCondition matchingHeaders = headersCondition.getMatchingCondition(request); + ConsumesRequestCondition matchingConsumes = consumesCondition.getMatchingCondition(request); + ProducesRequestCondition matchingProduces = producesCondition.getMatchingCondition(request); - if (matchingMethodCondition == null || matchingParamsCondition == null || matchingHeadersCondition == null || - matchingConsumesCondition == null || matchingProducesCondition == null) { + if (matchingMethod == null || matchingParams == null || matchingHeaders == null || + matchingConsumes == null || matchingProduces == null) { return null; } - else { - List matchingPatterns = getMatchingPatterns(lookupPath, pathMatcher); - if (!matchingPatterns.isEmpty()) { - return new RequestMappingInfo(matchingPatterns, matchingMethodCondition, matchingParamsCondition, - matchingHeadersCondition, matchingConsumesCondition, matchingProducesCondition); - } - else { - return null; - } - } - } - - private List getMatchingPatterns(String lookupPath, PathMatcher pathMatcher) { - - List matchingPatterns = new ArrayList(); - for (String pattern : this.patterns) { - String matchingPattern = getMatchingPattern(pattern, lookupPath, pathMatcher); - if (matchingPattern != null) { - matchingPatterns.add(matchingPattern); - } + + PatternsRequestCondition matchingPatterns = patternsCondition.getMatchingCondition(request); + if (matchingPatterns != null) { + return new RequestMappingInfo(matchingPatterns, matchingMethod, + matchingParams, matchingHeaders, matchingConsumes, + matchingProduces); } - Collections.sort(matchingPatterns, pathMatcher.getPatternComparator(lookupPath)); - - return matchingPatterns; - } - - private String getMatchingPattern(String pattern, String lookupPath, PathMatcher pathMatcher) { - if (pattern.equals(lookupPath)) { - return pattern; - } - boolean hasSuffix = pattern.indexOf('.') != -1; - if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) { - return pattern + ".*"; - } - if (pathMatcher.match(pattern, lookupPath)) { - return pattern; - } - boolean endsWithSlash = pattern.endsWith("/"); - if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) { - return pattern +"/"; - } return null; } @@ -281,7 +179,7 @@ public final class RequestMappingInfo { } if (obj != null && obj instanceof RequestMappingInfo) { RequestMappingInfo other = (RequestMappingInfo) obj; - return (this.patterns.equals(other.patterns) && + return (this.patternsCondition.equals(other.patternsCondition) && this.methodsCondition.equals(other.methodsCondition) && this.paramsCondition.equals(other.paramsCondition) && this.headersCondition.equals(other.headersCondition) && @@ -295,7 +193,7 @@ public final class RequestMappingInfo { public int hashCode() { int result = hash; if (result == 0) { - result = patterns.hashCode(); + result = patternsCondition.hashCode(); result = 31 * result + methodsCondition.hashCode(); result = 31 * result + paramsCondition.hashCode(); result = 31 * result + headersCondition.hashCode(); @@ -309,7 +207,7 @@ public final class RequestMappingInfo { @Override public String toString() { StringBuilder builder = new StringBuilder("{"); - builder.append(patterns); + builder.append(patternsCondition); builder.append(",methods=").append(methodsCondition); builder.append(",params=").append(paramsCondition); builder.append(",headers=").append(headersCondition); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index 8a9118d4347..97fa811fff0 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -61,7 +60,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @Override protected Set getMappingPaths(RequestMappingInfo mapping) { - return mapping.getPatterns(); + return mapping.getPatternsCondition().getPatterns(); } /** @@ -73,7 +72,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping, String lookupPath, HttpServletRequest request) { - return mapping.getMatchingRequestMapping(lookupPath, request, getPathMatcher()); + return mapping.getMatchingRequestMapping(request); } /** @@ -81,7 +80,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe */ @Override protected Comparator getMappingComparator(String lookupPath, HttpServletRequest request) { - return new RequestMappingInfoComparator(lookupPath, request); + return new RequestMappingInfoComparator(request); } /** @@ -94,12 +93,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) { super.handleMatch(info, lookupPath, request); - String pattern = info.getPatterns().iterator().next(); + String pattern = info.getPatternsCondition().getPatterns().iterator().next(); Map uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(pattern, lookupPath); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); - if (!info.getProduces().isEmpty()) { - Set mediaTypes = info.getProduces().getMediaTypes(); + if (!info.getProducesCondition().isEmpty()) { + Set mediaTypes = info.getProducesCondition().getMediaTypes(); request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes); } } @@ -117,18 +116,18 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe Set consumableMediaTypes = new HashSet(); Set producibleMediaTypes = new HashSet(); for (RequestMappingInfo info : requestMappingInfos) { - for (String pattern : info.getPatterns()) { + for (String pattern : info.getPatternsCondition().getPatterns()) { if (getPathMatcher().match(pattern, lookupPath)) { - if (!info.getMethods().match(request)) { - for (RequestMethod method : info.getMethods().getMethods()) { + if (info.getMethodsCondition().getMatchingCondition(request) == null) { + for (RequestMethod method : info.getMethodsCondition().getMethods()) { allowedMethods.add(method.name()); } } - if (!info.getConsumes().match(request)) { - consumableMediaTypes.addAll(info.getConsumes().getMediaTypes()); + if (info.getConsumesCondition().getMatchingCondition(request) == null) { + consumableMediaTypes.addAll(info.getConsumesCondition().getMediaTypes()); } - if (!info.getProduces().match(request)) { - producibleMediaTypes.addAll(info.getProduces().getMediaTypes()); + if (info.getProducesCondition().getMatchingCondition(request) == null) { + producibleMediaTypes.addAll(info.getProducesCondition().getMediaTypes()); } } } @@ -152,77 +151,44 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe } /** - * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a specific - * request. For example not all {@link RequestMappingInfo} patterns may apply to the current request. Therefore an - * HttpServletRequest is required as input. - * - *

    Furthermore, the following assumptions are made about the input RequestMappings:

    • Each RequestMappingInfo - * has been fully matched to the request
    • The RequestMappingInfo contains matched patterns only
    • Patterns are - * ordered with the best matching pattern at the top
    - * - * @see RequestMappingInfoHandlerMapping#getMatchingMapping(RequestMappingInfo, String, HttpServletRequest) + * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context + * of a specific request. For example only a subset of URL patterns may apply to the current request. */ private class RequestMappingInfoComparator implements Comparator { - private Comparator patternComparator; + private final HttpServletRequest request; - private List requestAcceptHeader; - - public RequestMappingInfoComparator(String lookupPath, HttpServletRequest request) { - this.patternComparator = getPathMatcher().getPatternComparator(lookupPath); - String acceptHeader = request.getHeader("Accept"); - this.requestAcceptHeader = MediaType.parseMediaTypes(acceptHeader); - MediaType.sortByQualityValue(this.requestAcceptHeader); + public RequestMappingInfoComparator(HttpServletRequest request) { + this.request = request; } public int compare(RequestMappingInfo mapping, RequestMappingInfo otherMapping) { - int result = comparePatterns(mapping.getPatterns(), otherMapping.getPatterns()); + int result = mapping.getPatternsCondition().compareTo(otherMapping.getPatternsCondition(), request); if (result != 0) { return result; } - result = mapping.getParams().compareTo(otherMapping.getParams()); + result = mapping.getParamsCondition().compareTo(otherMapping.getParamsCondition(), request); if (result != 0) { return result; } - result = mapping.getHeaders().compareTo(otherMapping.getHeaders()); + result = mapping.getHeadersCondition().compareTo(otherMapping.getHeadersCondition(), request); if (result != 0) { return result; } - result = mapping.getConsumes().compareTo(otherMapping.getConsumes()); + result = mapping.getConsumesCondition().compareTo(otherMapping.getConsumesCondition(), request); if (result != 0) { return result; } - result = mapping.getProduces().compareTo(otherMapping.getProduces(), this.requestAcceptHeader); + result = mapping.getProducesCondition().compareTo(otherMapping.getProducesCondition(), request); if (result != 0) { return result; } - result = mapping.getMethods().compareTo(otherMapping.getMethods()); + result = mapping.getMethodsCondition().compareTo(otherMapping.getMethodsCondition(), request); if (result != 0) { return result; } return 0; } - - private int comparePatterns(Set patterns, Set otherPatterns) { - Iterator iterator = patterns.iterator(); - Iterator iteratorOther = otherPatterns.iterator(); - while (iterator.hasNext() && iteratorOther.hasNext()) { - int result = patternComparator.compare(iterator.next(), iteratorOther.next()); - if (result != 0) { - return result; - } - } - if (iterator.hasNext()) { - return -1; - } - else if (iteratorOther.hasNext()) { - return 1; - } - else { - return 0; - } - } - } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index a0102faaa70..23afb1cf64e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -17,7 +17,6 @@ package org.springframework.web.servlet.mvc.method.annotation; import java.lang.reflect.Method; -import java.util.Arrays; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; @@ -25,7 +24,12 @@ import org.springframework.util.PathMatcher; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; -import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; +import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition; /** * A sub-class of {@link RequestMappingInfoHandlerMapping} that prepares {@link RequestMappingInfo}s @@ -64,7 +68,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi RequestMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); if (typeAnnot != null) { RequestMappingInfo typeMapping = createFromRequestMapping(typeAnnot); - return typeMapping.combine(methodMapping, getPathMatcher()); + return typeMapping.combine(methodMapping); } else { return methodMapping; @@ -75,13 +79,14 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi } } - private static RequestMappingInfo createFromRequestMapping(RequestMapping annotation) { - return new RequestMappingInfo(Arrays.asList(annotation.value()), - RequestConditionFactory.parseMethods(annotation.method()), - RequestConditionFactory.parseParams(annotation.params()), - RequestConditionFactory.parseHeaders(annotation.headers()), - RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()), - RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers())); + private RequestMappingInfo createFromRequestMapping(RequestMapping annotation) { + return new RequestMappingInfo( + new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher()), + new RequestMethodsRequestCondition(annotation.method()), + new ParamsRequestCondition(annotation.params()), + new HeadersRequestCondition(annotation.headers()), + new ConsumesRequestCondition(annotation.consumes(), annotation.headers()), + new ProducesRequestCondition(annotation.produces(), annotation.headers())); } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java similarity index 75% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java index ea859b7039e..06d2e81be76 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java @@ -19,14 +19,15 @@ package org.springframework.web.servlet.mvc.method.condition; import javax.servlet.http.HttpServletRequest; /** - * A condition that supports simple "name=value" style expressions as documented in {@link - * org.springframework.web.bind.annotation.RequestMapping#params()} and {@link org.springframework.web.bind.annotation.RequestMapping#headers()}. + * Supports "name=value" style expressions as described in: + * {@link org.springframework.web.bind.annotation.RequestMapping#params()} and + * {@link org.springframework.web.bind.annotation.RequestMapping#headers()}. * * @author Rossen Stoyanchev * @author Arjen Poutsma * @since 3.1 */ -abstract class AbstractNameValueCondition implements RequestCondition { +abstract class AbstractNameValueExpression { protected final String name; @@ -34,7 +35,7 @@ abstract class AbstractNameValueCondition implements RequestCondition { protected final boolean isNegated; - AbstractNameValueCondition(String expression) { + AbstractNameValueExpression(String expression) { int separator = expression.indexOf('='); if (separator == -1) { this.isNegated = expression.startsWith("!"); @@ -65,6 +66,20 @@ abstract class AbstractNameValueCondition implements RequestCondition { protected abstract boolean matchValue(HttpServletRequest request); + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof AbstractNameValueExpression) { + AbstractNameValueExpression other = (AbstractNameValueExpression) obj; + return ((this.name.equalsIgnoreCase(other.name)) && + (this.value != null ? this.value.equals(other.value) : other.value == null) && + this.isNegated == other.isNegated); + } + return false; + } + @Override public int hashCode() { int result = name.hashCode(); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java index c2323191534..adefee10fc5 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java @@ -16,140 +16,194 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; + import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression; /** - * Represents a collection of consumes conditions, typically obtained from {@link org.springframework.web.bind.annotation.RequestMapping#consumes() - * @RequestMapping.consumes()}. - * + * A logical disjunction (' || ') request condition to match requests against consumable media type expressions. + * + *

    For details on the syntax of the expressions see {@link RequestMapping#consumes()}. If the condition is + * created with 0 consumable media type expressions, it matches to every request. + * + *

    This request condition is also capable of parsing header expressions specifically selecting 'Content-Type' + * header expressions and converting them to consumable media type expressions. + * * @author Arjen Poutsma - * @see RequestConditionFactory#parseConsumes(String...) - * @see RequestConditionFactory#parseConsumes(String[], String[]) + * @author Rossen Stoyanchev * @since 3.1 */ -public class ConsumesRequestCondition - extends MediaTypesRequestCondition - implements Comparable { +public class ConsumesRequestCondition extends RequestConditionSupport { - ConsumesRequestCondition(Collection conditions) { - super(conditions); - } - - ConsumesRequestCondition(String... consumes) { - this(parseConditions(Arrays.asList(consumes))); - } - - private static Set parseConditions(List consumes) { - Set conditions = new LinkedHashSet(consumes.size()); - for (String consume : consumes) { - conditions.add(new ConsumeRequestCondition(consume)); - } - return conditions; - } + private final List expressions; /** - * Creates a default set of consumes request conditions. + * Creates a {@link ConsumesRequestCondition} with the given consumable media type expressions. + * @param consumes the expressions to parse; if 0 the condition matches to every request */ - public ConsumesRequestCondition() { - this(Collections.emptySet()); + public ConsumesRequestCondition(String... consumes) { + this(consumes, null); } /** - * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request. - * - * @param request the request - * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match + * Creates a {@link ConsumesRequestCondition} with the given header and consumes expressions. + * In addition to consume expressions, {@code "Content-Type"} header expressions are extracted + * and treated as consumable media type expressions. + * @param consumes the consumes expressions to parse; 0 matches to all requests + * @param headers the header expression to parse; 0 matches to all requests + */ + public ConsumesRequestCondition(String[] consumes, String[] headers) { + this(parseExpressions(consumes, headers)); + } + + /** + * Private constructor. + */ + private ConsumesRequestCondition(Collection expressions) { + this.expressions = new ArrayList(expressions); + Collections.sort(this.expressions); + } + + private static Set parseExpressions(String[] consumes, String[] headers) { + Set result = new LinkedHashSet(); + if (headers != null) { + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if ("Content-Type".equalsIgnoreCase(expr.name)) { + for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { + result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated)); + } + } + } + } + if (consumes != null) { + for (String consume : consumes) { + result.add(new ConsumeMediaTypeExpression(consume)); + } + } + return result; + } + + /** + * Returns the consumable media types contained in all expressions of this condition. + */ + public Set getMediaTypes() { + Set result = new LinkedHashSet(); + for (ConsumeMediaTypeExpression expression : expressions) { + result.add(expression.getMediaType()); + } + return result; + } + + /** + * Returns true if this condition contains 0 consumable media type expressions. + */ + public boolean isEmpty() { + return expressions.isEmpty(); + } + + @Override + protected Collection getContent() { + return expressions; + } + + @Override + protected boolean isLogicalConjunction() { + return false; + } + + /** + * Returns the "other" instance as long as it contains any expressions; or "this" instance otherwise. + * In other words "other" takes precedence over "this" as long as it has expressions. + *

    Example: method-level "consumes" overrides type-level "consumes" condition. + */ + public ConsumesRequestCondition combine(ConsumesRequestCondition other) { + return !other.expressions.isEmpty() ? other : this; + } + + /** + * Checks if any of the consumable media type expressions match the given request and returns an instance that + * is guaranteed to contain matching media type expressions only. + * + * @param request the current request + * + * @return the same instance if the condition contains no expressions; + * or a new condition with matching expressions; or {@code null} if no expressions match. */ public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) { if (isEmpty()) { return this; } - Set matchingConditions = new LinkedHashSet(getConditions()); - for (Iterator iterator = matchingConditions.iterator(); iterator.hasNext();) { - ConsumeRequestCondition condition = iterator.next(); - if (!condition.match(request)) { + Set result = new LinkedHashSet(expressions); + for (Iterator iterator = result.iterator(); iterator.hasNext();) { + ConsumeMediaTypeExpression expression = iterator.next(); + if (!expression.match(request)) { iterator.remove(); } } - if (matchingConditions.isEmpty()) { - return null; + return (result.isEmpty()) ? null : new ConsumesRequestCondition(result); + } + + /** + * Returns: + *

      + *
    • 0 if the two conditions have the same number of expressions + *
    • Less than 1 if "this" has more in number or more specific consumable media type expressions + *
    • Greater than 1 if "other" has more in number or more specific consumable media type expressions + *
    + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * and each instance contains the matching consumable media type expression only or is otherwise empty. + */ + public int compareTo(ConsumesRequestCondition other, HttpServletRequest request) { + if (expressions.isEmpty() && other.expressions.isEmpty()) { + return 0; + } + else if (expressions.isEmpty()) { + return 1; + } + else if (other.expressions.isEmpty()) { + return -1; } else { - return new ConsumesRequestCondition(matchingConditions); + return expressions.get(0).compareTo(other.expressions.get(0)); } } /** - * Combines this collection of request condition with another. Returns {@code other}, unless it is empty. - * - * @param other the condition to combine with + * Parsing and request matching logic for consumable media type expressions. + * @see RequestMapping#consumes() */ - public ConsumesRequestCondition combine(ConsumesRequestCondition other) { - return !other.isEmpty() ? other : this; - } + static class ConsumeMediaTypeExpression extends MediaTypeExpression { - - public int compareTo(ConsumesRequestCondition other) { - MediaTypeRequestCondition thisMostSpecificCondition = this.getMostSpecificCondition(); - MediaTypeRequestCondition otherMostSpecificCondition = other.getMostSpecificCondition(); - if (thisMostSpecificCondition == null && otherMostSpecificCondition == null) { - return 0; - } - else if (thisMostSpecificCondition == null) { - return 1; - } - else if (otherMostSpecificCondition == null) { - return -1; - } - else { - return thisMostSpecificCondition.compareTo(otherMostSpecificCondition); - } - } - - private MediaTypeRequestCondition getMostSpecificCondition() { - if (!isEmpty()) { - return getSortedConditions().get(0); - } - else { - return null; - } - } - - - static class ConsumeRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition { - - ConsumeRequestCondition(String expression) { + ConsumeMediaTypeExpression(String expression) { super(expression); } - ConsumeRequestCondition(MediaType mediaType, boolean negated) { + ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) { super(mediaType, negated); } @Override protected boolean match(HttpServletRequest request, MediaType mediaType) { - MediaType contentType = getContentType(request); + + MediaType contentType = StringUtils.hasLength(request.getContentType()) ? + MediaType.parseMediaType(request.getContentType()) : + MediaType.APPLICATION_OCTET_STREAM ; + return mediaType.includes(contentType); } - - private MediaType getContentType(HttpServletRequest request) { - if (StringUtils.hasLength(request.getContentType())) { - return MediaType.parseMediaType(request.getContentType()); - } - else { - return MediaType.APPLICATION_OCTET_STREAM; - } - } - } + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java index bb41c829098..559e0e1ceb9 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java @@ -16,79 +16,117 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; + import javax.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.RequestMapping; + /** - * Represents a collection of header request conditions, typically obtained from {@link - * org.springframework.web.bind.annotation.RequestMapping#headers() @RequestMapping.headers()}. - * + * A logical conjunction (' && ') request condition that matches a request against a set of header expressions. + * + *

    For details on the syntax of the expressions see {@link RequestMapping#headers()}. If the condition is + * created with 0 header expressions, it will match to every request. + * + *

    Note: when parsing header expressions, {@code "Accept"} and {@code "Content-Type"} header expressions + * are filtered out. Those should be converted and used as "produces" and "consumes" conditions instead. + * See the constructors for {@link ProducesRequestCondition} and {@link ConsumesRequestCondition}. + * * @author Arjen Poutsma - * @see RequestConditionFactory#parseHeaders(String...) + * @author Rossen Stoyanchev * @since 3.1 */ -public class HeadersRequestCondition - extends LogicalConjunctionRequestCondition - implements Comparable { +public class HeadersRequestCondition extends RequestConditionSupport { - HeadersRequestCondition(Collection conditions) { - super(conditions); + private final Set expressions; + + /** + * Create a {@link HeadersRequestCondition} with the given header expressions. + * + *

    Note: {@code "Accept"} and {@code "Content-Type"} header expressions are filtered out. + * Those should be converted and used as "produces" and "consumes" conditions instead. + * See the constructors for {@link ProducesRequestCondition} and {@link ConsumesRequestCondition}. + * + * @param headers 0 or more header expressions; if 0 the condition will match to every request. + */ + public HeadersRequestCondition(String... headers) { + this(parseExpressions(headers)); } - - HeadersRequestCondition(String... headers) { - this(parseConditions(Arrays.asList(headers))); + + private HeadersRequestCondition(Collection conditions) { + this.expressions = Collections.unmodifiableSet(new LinkedHashSet(conditions)); } - - private static Set parseConditions(Collection params) { - Set conditions = new LinkedHashSet(params.size()); - for (String param : params) { - conditions.add(new HeaderRequestCondition(param)); + + private static Collection parseExpressions(String... headers) { + Set expressions = new LinkedHashSet(); + if (headers != null) { + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) { + continue; + } + expressions.add(expr); + } } - return conditions; + return expressions; + } + + @Override + protected Collection getContent() { + return expressions; + } + + @Override + protected boolean isLogicalConjunction() { + return true; } /** - * Creates an empty set of header request conditions. - */ - public HeadersRequestCondition() { - this(Collections.emptySet()); - } - - /** - * Returns a new {@code RequestCondition} that contains all conditions that match the request. - * - * @param request the request - * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match - */ - public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) { - return match(request) ? this : null; - } - - - /** - * Combines this collection of request condition with another by combining all header request conditions into a - * logical AND. - * - * @param other the condition to combine with + * Returns a new instance with the union of the header expressions from "this" and the "other" instance. */ public HeadersRequestCondition combine(HeadersRequestCondition other) { - Set conditions = new LinkedHashSet(getConditions()); - conditions.addAll(other.getConditions()); - return new HeadersRequestCondition(conditions); + Set set = new LinkedHashSet(this.expressions); + set.addAll(other.expressions); + return new HeadersRequestCondition(set); + } + + /** + * Returns "this" instance if the request matches to all header expressions; or {@code null} otherwise. + */ + public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) { + for (HeaderExpression expression : expressions) { + if (!expression.match(request)) { + return null; + } + } + return this; } - - public int compareTo(HeadersRequestCondition other) { - return other.getConditions().size() - this.getConditions().size(); + /** + * Returns: + *

      + *
    • 0 if the two conditions have the same number of header expressions + *
    • Less than 1 if "this" instance has more header expressions + *
    • Greater than 1 if the "other" instance has more header expressions + *
    + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * and each instance contains the matching header expression only or is otherwise empty. + */ + public int compareTo(HeadersRequestCondition other, HttpServletRequest request) { + return other.expressions.size() - this.expressions.size(); } - static class HeaderRequestCondition extends AbstractNameValueCondition { + /** + * Parsing and request matching logic for header expressions. + * @see RequestMapping#headers() + */ + static class HeaderExpression extends AbstractNameValueExpression { - public HeaderRequestCondition(String expression) { + public HeaderExpression(String expression) { super(expression); } @@ -107,20 +145,6 @@ public class HeadersRequestCondition return value.equals(request.getHeader(name)); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof HeaderRequestCondition) { - HeaderRequestCondition other = (HeaderRequestCondition) obj; - return ((this.name.equalsIgnoreCase(other.name)) && - (this.value != null ? this.value.equals(other.value) : other.value == null) && - this.isNegated == other.isNegated); - } - return false; - } - @Override public int hashCode() { int result = name.toLowerCase().hashCode(); @@ -128,7 +152,5 @@ public class HeadersRequestCondition result = 31 * result + (isNegated ? 1 : 0); return result; } - - - } + } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java deleted file mode 100644 index 7873d909628..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2011 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.servlet.mvc.method.condition; - -import java.util.Collection; -import java.util.Set; -import javax.servlet.http.HttpServletRequest; - -/** - * {@link RequestCondition} implementation that represents a logical AND (i.e. &&). - * - * @author Arjen Poutsma - * @since 3.1 - */ -class LogicalConjunctionRequestCondition extends RequestConditionComposite { - - LogicalConjunctionRequestCondition(Collection conditions) { - super(conditions); - } - - public boolean match(HttpServletRequest request) { - Set conditions = getConditions(); - if (isEmpty()) { - return true; - } - for (T condition : conditions) { - if (!condition.match(request)) { - return false; - } - } - return true; - } - - - @Override - protected String getToStringInfix() { - return " && "; - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java deleted file mode 100644 index e775a76c96a..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2011 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.servlet.mvc.method.condition; - -import java.util.Collection; -import javax.servlet.http.HttpServletRequest; - -/** - * {@link RequestCondition} implementation that represents a logical OR (i.e. ||). - * - * @author Arjen Poutsma - * @since 3.1 - */ -class LogicalDisjunctionRequestCondition extends RequestConditionComposite { - - LogicalDisjunctionRequestCondition(Collection conditions) { - super(conditions); - } - - public boolean match(HttpServletRequest request) { - if (isEmpty()) { - return true; - } - for (RequestCondition condition : getConditions()) { - if (condition.match(request)) { - return true; - } - } - return false; - } - - @Override - protected String getToStringInfix() { - return " || "; - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java new file mode 100644 index 00000000000..ca24662ba5d --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2011 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.servlet.mvc.method.condition; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Supports media type expressions as described in: + * {@link RequestMapping#consumes()} and {@link RequestMapping#produces()}. + * + * @author Arjen Poutsma + * @author Rossen Stoyanchev + * @since 3.1 + */ +abstract class MediaTypeExpression implements Comparable { + + private final MediaType mediaType; + + private final boolean isNegated; + + MediaTypeExpression(String expression) { + if (expression.startsWith("!")) { + isNegated = true; + expression = expression.substring(1); + } + else { + isNegated = false; + } + this.mediaType = MediaType.parseMediaType(expression); + } + + MediaTypeExpression(MediaType mediaType, boolean negated) { + this.mediaType = mediaType; + isNegated = negated; + } + + public boolean match(HttpServletRequest request) { + boolean match = match(request, this.mediaType); + return !isNegated ? match : !match; + } + + protected abstract boolean match(HttpServletRequest request, MediaType mediaType); + + MediaType getMediaType() { + return mediaType; + } + + public int compareTo(MediaTypeExpression other) { + return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && getClass().equals(obj.getClass())) { + MediaTypeExpression other = (MediaTypeExpression) obj; + return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated); + } + return false; + } + + @Override + public int hashCode() { + return mediaType.hashCode(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (isNegated) { + builder.append('!'); + } + builder.append(mediaType.toString()); + return builder.toString(); + } + +} \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java deleted file mode 100644 index df254e0741a..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2002-2011 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.servlet.mvc.method.condition; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import javax.servlet.http.HttpServletRequest; - -import org.springframework.http.MediaType; - -/** - * @author Arjen Poutsma - */ -class MediaTypesRequestCondition - extends LogicalDisjunctionRequestCondition { - - private final List sortedConditions; - - public MediaTypesRequestCondition(Collection conditions) { - super(conditions); - sortedConditions = new ArrayList(conditions); - Collections.sort(sortedConditions); - } - - protected List getSortedConditions() { - return sortedConditions; - } - - /** - * Returns all {@link MediaType}s contained in this condition. - */ - public Set getMediaTypes() { - Set result = new LinkedHashSet(); - for (MediaTypeRequestCondition condition : getConditions()) { - result.add(condition.getMediaType()); - } - return result; - } - - /** - * @author Arjen Poutsma - */ - protected abstract static class MediaTypeRequestCondition - implements RequestCondition, Comparable { - - private final MediaType mediaType; - - private final boolean isNegated; - - MediaTypeRequestCondition(MediaType mediaType, boolean negated) { - this.mediaType = mediaType; - isNegated = negated; - } - - MediaTypeRequestCondition(String expression) { - if (expression.startsWith("!")) { - isNegated = true; - expression = expression.substring(1); - } - else { - isNegated = false; - } - this.mediaType = MediaType.parseMediaType(expression); - } - - public boolean match(HttpServletRequest request) { - boolean match = match(request, this.mediaType); - return !isNegated ? match : !match; - } - - protected abstract boolean match(HttpServletRequest request, MediaType mediaType); - - MediaType getMediaType() { - return mediaType; - } - - public int compareTo(MediaTypeRequestCondition other) { - return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && getClass().equals(obj.getClass())) { - MediaTypeRequestCondition other = (MediaTypeRequestCondition) obj; - return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated); - } - return false; - } - - @Override - public int hashCode() { - return mediaType.hashCode(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (isNegated) { - builder.append('!'); - } - builder.append(mediaType.toString()); - return builder.toString(); - } - } -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java index fba088f8ed7..7bbcf7c85ef 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java @@ -16,80 +16,106 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; + import javax.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.util.WebUtils; /** - * Represents a collection of parameter request conditions, typically obtained from {@link - * org.springframework.web.bind.annotation.RequestMapping#params() @RequestMapping.params()}. - * + * A logical conjunction (' && ') request condition that matches a request against a set parameter expressions. + * + *

    For details on the syntax of the expressions see {@link RequestMapping#params()}. If the condition is + * created with 0 parameter expressions, it will match to every request. + * * @author Arjen Poutsma - * @see RequestConditionFactory#parseParams(String...) + * @author Rossen Stoyanchev * @since 3.1 */ -public class ParamsRequestCondition - extends LogicalConjunctionRequestCondition - implements Comparable { +public class ParamsRequestCondition extends RequestConditionSupport { - private ParamsRequestCondition(Collection conditions) { - super(conditions); + private final Set expressions; + + /** + * Create a {@link ParamsRequestCondition} with the given param expressions. + * + * @param params 0 or more param expressions; if 0 the condition will match to every request. + */ + public ParamsRequestCondition(String... params) { + this(parseExpressions(params)); + } + + private ParamsRequestCondition(Collection conditions) { + this.expressions = Collections.unmodifiableSet(new LinkedHashSet(conditions)); } - ParamsRequestCondition(String... params) { - this(parseConditions(Arrays.asList(params))); - } - - private static Set parseConditions(List params) { - Set conditions = new LinkedHashSet(params.size()); - for (String param : params) { - conditions.add(new ParamRequestCondition(param)); + private static Collection parseExpressions(String... params) { + Set expressions = new LinkedHashSet(); + if (params != null) { + for (String header : params) { + expressions.add(new ParamExpression(header)); + } } - return conditions; + return expressions; + } + + @Override + protected Collection getContent() { + return expressions; + } + + @Override + protected boolean isLogicalConjunction() { + return true; } /** - * Creates an empty set of parameter request conditions. - */ - public ParamsRequestCondition() { - this(Collections.emptySet()); - } - - /** - * Returns a new {@code RequestCondition} that contains all conditions that match the request. - * - * @param request the request - * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match - */ - public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) { - return match(request) ? this : null; - } - - /** - * Combines this collection of request condition with another by combining all parameter request conditions into a - * logical AND. - * - * @param other the condition to combine with + * Returns a new instance with the union of the param expressions from "this" and the "other" instance. */ public ParamsRequestCondition combine(ParamsRequestCondition other) { - Set conditions = new LinkedHashSet(getConditions()); - conditions.addAll(other.getConditions()); - return new ParamsRequestCondition(conditions); + Set set = new LinkedHashSet(this.expressions); + set.addAll(other.expressions); + return new ParamsRequestCondition(set); } - public int compareTo(ParamsRequestCondition other) { - return other.getConditions().size() - this.getConditions().size(); + /** + * Returns "this" instance if the request matches to all parameter expressions; or {@code null} otherwise. + */ + public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) { + for (ParamExpression expression : expressions) { + if (!expression.match(request)) { + return null; + } + } + return this; } - static class ParamRequestCondition extends AbstractNameValueCondition { + /** + * Returns: + *

      + *
    • 0 if the two conditions have the same number of parameter expressions + *
    • Less than 1 if "this" instance has more parameter expressions + *
    • Greater than 1 if the "other" instance has more parameter expressions + *
    + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * and each instance contains the matching parameter expressions only or is otherwise empty. + */ + public int compareTo(ParamsRequestCondition other, HttpServletRequest request) { + return other.expressions.size() - this.expressions.size(); + } - ParamRequestCondition(String expression) { + /** + * Parsing and request matching logic for parameter expressions. + * @see RequestMapping#params() + */ + static class ParamExpression extends AbstractNameValueExpression { + + ParamExpression(String expression) { super(expression); } @@ -107,20 +133,6 @@ public class ParamsRequestCondition protected boolean matchValue(HttpServletRequest request) { return value.equals(request.getParameter(name)); } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof ParamRequestCondition) { - ParamRequestCondition other = (ParamRequestCondition) obj; - return ((this.name.equals(other.name)) && - (this.value != null ? this.value.equals(other.value) : other.value == null) && - this.isNegated == other.isNegated); - } - return false; - } - } + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java new file mode 100644 index 00000000000..339128145d6 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java @@ -0,0 +1,228 @@ +/* + * Copyright 2002-2011 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.servlet.mvc.method.condition; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UrlPathHelper; + +/** + * A logical disjunction (' || ') request condition that matches a request against a set of URL path patterns. + * + *

    See Javadoc on individual methods for details on how URL patterns are matched, combined, and compared. + * + * @author Rossen Stoyanchev + * @since 3.1 + */ +public class PatternsRequestCondition extends RequestConditionSupport { + + private final Set patterns; + + private final UrlPathHelper urlPathHelper; + + private final PathMatcher pathMatcher; + + /** + * Creates a new {@link PatternsRequestCondition} with the given URL patterns. + * Each pattern that is not empty and does not start with "/" is prepended with "/". + * @param patterns 0 or more URL patterns; if 0 the condition will match to every request. + */ + public PatternsRequestCondition(String... patterns) { + this(patterns, new UrlPathHelper(), new AntPathMatcher()); + } + + /** + * Creates a new {@link PatternsRequestCondition} with the given URL patterns. + * Each pattern that is not empty and does not start with "/" is prepended with "/". + * + * @param patterns the URL patterns to use; if 0 the condition will match to every request. + * @param urlPathHelper a {@link UrlPathHelper} for determining the lookup path for a request + * @param pathMatcher a {@link PathMatcher} for pattern path matching + */ + public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher) { + this(asList(patterns), urlPathHelper, pathMatcher); + } + + private static List asList(String... patterns) { + return patterns != null ? Arrays.asList(patterns) : Collections.emptyList(); + } + + /** + * Private constructor. + */ + private PatternsRequestCondition(Collection patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher) { + this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns)); + this.urlPathHelper = urlPathHelper; + this.pathMatcher = pathMatcher; + } + + private static Set prependLeadingSlash(Collection patterns) { + if (patterns == null) { + return Collections.emptySet(); + } + Set result = new LinkedHashSet(patterns.size()); + for (String pattern : patterns) { + if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { + pattern = "/" + pattern; + } + result.add(pattern); + } + return result; + } + + public Set getPatterns() { + return patterns; + } + + @Override + protected Collection getContent() { + return patterns; + } + + @Override + protected boolean isLogicalConjunction() { + return false; + } + + /** + * Returns a new instance with URL patterns from the current instance ("this") and + * the "other" instance as follows: + *

      + *
    • If there are patterns in both instances, combine the patterns in "this" with + * the patterns in "other" using {@link PathMatcher#combine(String, String)}. + *
    • If only one instance has patterns, use them. + *
    • If neither instance has patterns, use "". + *
    + */ + public PatternsRequestCondition combine(PatternsRequestCondition other) { + Set result = new LinkedHashSet(); + if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) { + for (String pattern1 : this.patterns) { + for (String pattern2 : other.patterns) { + result.add(pathMatcher.combine(pattern1, pattern2)); + } + } + } + else if (!this.patterns.isEmpty()) { + result.addAll(this.patterns); + } + else if (!other.patterns.isEmpty()) { + result.addAll(other.patterns); + } + else { + result.add(""); + } + return new PatternsRequestCondition(result, urlPathHelper, pathMatcher); + } + + /** + * Checks if any of the patterns match the given request and returns an instance that is guaranteed + * to contain matching patterns, sorted via {@link PathMatcher#getPatternComparator(String)}. + * + *

    A matching pattern is obtained by making checks in the following order: + *

      + *
    • Direct match + *
    • A pattern match with ".*" appended assuming the pattern already doesn't contain "." + *
    • A pattern match + *
    • A pattern match with "/" appended assuming the patterns already end with "/" + *
    + * + * @param request the current request + * + * @return the same instance if the condition contains no patterns; + * or a new condition with sorted matching patterns; or {@code null} if no patterns match. + */ + public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) { + if (patterns.isEmpty()) { + return this; + } + String lookupPath = urlPathHelper.getLookupPathForRequest(request); + List matches = new ArrayList(); + for (String pattern : patterns) { + String match = getMatchingPattern(pattern, lookupPath); + if (match != null) { + matches.add(match); + } + } + Collections.sort(matches, pathMatcher.getPatternComparator(lookupPath)); + return matches.isEmpty() ? null : new PatternsRequestCondition(matches, urlPathHelper, pathMatcher); + } + + private String getMatchingPattern(String pattern, String lookupPath) { + if (pattern.equals(lookupPath)) { + return pattern; + } + boolean hasSuffix = pattern.indexOf('.') != -1; + if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) { + return pattern + ".*"; + } + if (pathMatcher.match(pattern, lookupPath)) { + return pattern; + } + boolean endsWithSlash = pattern.endsWith("/"); + if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) { + return pattern +"/"; + } + return null; + } + + /** + * Compare the two conditions and return 0 if they match equally to the request, less than one if "this" + * matches the request more, and greater than one if "other" matches the request more. Patterns are + * compared one at a time, from top to bottom via {@link PathMatcher#getPatternComparator(String)}. + * If all compared patterns match equally, but one instance has more patterns, it is a closer match. + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * to ensure they contain only patterns that match the request and are sorted with the best matches on top. + */ + public int compareTo(PatternsRequestCondition other, HttpServletRequest request) { + String lookupPath = urlPathHelper.getLookupPathForRequest(request); + Comparator patternComparator = pathMatcher.getPatternComparator(lookupPath); + + Iterator iterator = patterns.iterator(); + Iterator iteratorOther = other.patterns.iterator(); + while (iterator.hasNext() && iteratorOther.hasNext()) { + int result = patternComparator.compare(iterator.next(), iteratorOther.next()); + if (result != 0) { + return result; + } + } + if (iterator.hasNext()) { + return -1; + } + else if (iteratorOther.hasNext()) { + return 1; + } + else { + return 0; + } + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java index 37766fd2620..caade1ca522 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java @@ -16,99 +16,170 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; + import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression; /** - * Represents a collection of produces conditions, typically obtained from {@link - * org.springframework.web.bind.annotation.RequestMapping#produces() @RequestMapping.produces()}. - * + * A logical disjunction (' || ') request condition to match requests against producible media type expressions. + * + *

    For details on the syntax of the expressions see {@link RequestMapping#consumes()}. If the condition is + * created with 0 producible media type expressions, it matches to every request. + * + *

    This request condition is also capable of parsing header expressions specifically selecting 'Accept' header + * expressions and converting them to prodicuble media type expressions. + * * @author Arjen Poutsma - * @see RequestConditionFactory#parseProduces(String...) - * @see RequestConditionFactory#parseProduces(String[], String[]) + * @author Rossen Stoyanchev * @since 3.1 */ -public class ProducesRequestCondition - extends MediaTypesRequestCondition { +public class ProducesRequestCondition extends RequestConditionSupport { - ProducesRequestCondition(Collection conditions) { - super(conditions); - } - - - ProducesRequestCondition(String... consumes) { - this(parseConditions(Arrays.asList(consumes))); - } - - private static Set parseConditions(List consumes) { - Set conditions = new LinkedHashSet(consumes.size()); - for (String consume : consumes) { - conditions.add(new ProduceRequestCondition(consume)); - } - return conditions; - } + private final List expressions; /** - * Creates an empty set of consumes request conditions. + * Creates a {@link ProducesRequestCondition} with the given producible media type expressions. + * @param produces the expressions to parse; if 0 the condition matches to every request */ - public ProducesRequestCondition() { - this(Collections.emptySet()); + public ProducesRequestCondition(String... produces) { + this(produces, null); + } + + /** + * Creates a {@link ProducesRequestCondition} with the given header and produces expressions. + * In addition to produces expressions, {@code "Accept"} header expressions are extracted and treated as + * producible media type expressions. + * @param produces the produces expressions to parse + * @param headers the header expression to parse + */ + public ProducesRequestCondition(String[] produces, String[] headers) { + this(parseExpressions(produces, headers)); } /** - * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request. - * - * @param request the request - * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match + * A private constructor. + */ + private ProducesRequestCondition(Collection expressions) { + this.expressions = new ArrayList(expressions); + Collections.sort(this.expressions); + } + + private static Set parseExpressions(String[] produces, String[] headers) { + Set result = new LinkedHashSet(); + if (headers != null) { + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if ("Accept".equalsIgnoreCase(expr.name)) { + for( MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { + result.add(new ProduceMediaTypeExpression(mediaType, expr.isNegated)); + } + } + } + } + if (produces != null) { + for (String produce : produces) { + result.add(new ProduceMediaTypeExpression(produce)); + } + } + return result; + } + + /** + * Returns the producible media types contained in all expressions of this condition. + */ + public Set getMediaTypes() { + Set result = new LinkedHashSet(); + for (ProduceMediaTypeExpression expression : expressions) { + result.add(expression.getMediaType()); + } + return result; + } + + /** + * Returns true if this condition contains no producible media type expressions. + */ + public boolean isEmpty() { + return expressions.isEmpty(); + } + + @Override + protected Collection getContent() { + return expressions; + } + + @Override + protected boolean isLogicalConjunction() { + return false; + } + + /** + * Returns the "other" instance if "other" as long as it contains any expressions; or "this" instance otherwise. + * In other words "other" takes precedence over "this" as long as it contains any expressions. + *

    Example: method-level "produces" overrides type-level "produces" condition. + */ + public ProducesRequestCondition combine(ProducesRequestCondition other) { + return !other.expressions.isEmpty() ? other : this; + } + + /** + * Checks if any of the producible media type expressions match the given request and returns an instance that + * is guaranteed to contain matching media type expressions only. + * + * @param request the current request + * + * @return the same instance if the condition contains no expressions; + * or a new condition with matching expressions; or {@code null} if no expressions match. */ public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) { if (isEmpty()) { return this; } - Set matchingConditions = new LinkedHashSet(getConditions()); - for (Iterator iterator = matchingConditions.iterator(); iterator.hasNext();) { - ProduceRequestCondition condition = iterator.next(); - if (!condition.match(request)) { + Set result = new LinkedHashSet(expressions); + for (Iterator iterator = result.iterator(); iterator.hasNext();) { + ProduceMediaTypeExpression expression = iterator.next(); + if (!expression.match(request)) { iterator.remove(); } } - if (matchingConditions.isEmpty()) { - return null; - } - else { - return new ProducesRequestCondition(matchingConditions); - } + return (result.isEmpty()) ? null : new ProducesRequestCondition(result); } /** - * Combines this collection of request condition with another. Returns {@code other}, unless it has the default - * value (i.e. {@code */*}). - * - * @param other the condition to combine with + * Returns: + *

      + *
    • 0 if the two conditions have the same number of expressions + *
    • Less than 1 if "this" has more in number or more specific producible media type expressions + *
    • Greater than 1 if "other" has more in number or more specific producible media type expressions + *
    + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * and each instance contains the matching producible media type expression only or is otherwise empty. */ - public ProducesRequestCondition combine(ProducesRequestCondition other) { - return !other.isEmpty() ? other : this; - } - - public int compareTo(ProducesRequestCondition other, List acceptedMediaTypes) { + public int compareTo(ProducesRequestCondition other, HttpServletRequest request) { + String acceptHeader = request.getHeader("Accept"); + List acceptedMediaTypes = MediaType.parseMediaTypes(acceptHeader); + MediaType.sortByQualityValue(acceptedMediaTypes); + for (MediaType acceptedMediaType : acceptedMediaTypes) { int thisIndex = this.indexOfMediaType(acceptedMediaType); int otherIndex = other.indexOfMediaType(acceptedMediaType); if (thisIndex != otherIndex) { return otherIndex - thisIndex; } else if (thisIndex != -1 && otherIndex != -1) { - ProduceRequestCondition thisCondition = this.getSortedConditions().get(thisIndex); - ProduceRequestCondition otherCondition = other.getSortedConditions().get(otherIndex); - int result = thisCondition.compareTo(otherCondition); + ProduceMediaTypeExpression thisExpr = this.expressions.get(thisIndex); + ProduceMediaTypeExpression otherExpr = other.expressions.get(otherIndex); + int result = thisExpr.compareTo(otherExpr); if (result != 0) { return result; } @@ -118,29 +189,31 @@ public class ProducesRequestCondition } private int indexOfMediaType(MediaType mediaType) { - List sortedConditions = getSortedConditions(); - for (int i = 0; i < sortedConditions.size(); i++) { - ProduceRequestCondition condition = sortedConditions.get(i); - if (mediaType.includes(condition.getMediaType())) { + for (int i = 0; i < expressions.size(); i++) { + if (mediaType.includes(expressions.get(i).getMediaType())) { return i; } } return -1; } - static class ProduceRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition { + /** + * Parsing and request matching logic for producible media type expressions. + * @see RequestMapping#produces() + */ + static class ProduceMediaTypeExpression extends MediaTypeExpression { - ProduceRequestCondition(MediaType mediaType, boolean negated) { + ProduceMediaTypeExpression(MediaType mediaType, boolean negated) { super(mediaType, negated); } - ProduceRequestCondition(String expression) { + ProduceMediaTypeExpression(String expression) { super(expression); } @Override protected boolean match(HttpServletRequest request, MediaType mediaType) { - List acceptedMediaTypes = getAccept(request); + List acceptedMediaTypes = getAcceptedMediaTypes(request); for (MediaType acceptedMediaType : acceptedMediaTypes) { if (mediaType.isCompatibleWith(acceptedMediaType)) { return true; @@ -149,7 +222,7 @@ public class ProducesRequestCondition return false; } - private List getAccept(HttpServletRequest request) { + private List getAcceptedMediaTypes(HttpServletRequest request) { String acceptHeader = request.getHeader("Accept"); if (StringUtils.hasLength(acceptHeader)) { return MediaType.parseMediaTypes(acceptHeader); @@ -159,4 +232,5 @@ public class ProducesRequestCondition } } } + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java index 74619e7c9e6..b8c55727764 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java @@ -19,23 +19,39 @@ package org.springframework.web.servlet.mvc.method.condition; import javax.servlet.http.HttpServletRequest; /** - * Defines the contract for conditions that must be met given an incoming request. - * - *

    Implementations of this interface are created by the {@link RequestConditionFactory}. - * + * The contract for request conditions. + * + *

    Request conditions can be combined (e.g. type + method-level conditions), matched to a request, + * or compared to each other to determine if one matches the request better. + * * @author Rossen Stoyanchev * @author Arjen Poutsma - * @see RequestConditionFactory * @since 3.1 */ -public interface RequestCondition { +public interface RequestCondition> { /** - * Indicates whether this condition matches against the given servlet request. - * - * @param request the request - * @return {@code true} if this condition matches the request; {@code false} otherwise + * Defines the rules for combining "this" condition (i.e. the current instance) with another condition. + *

    Example: combine type- and method-level request mapping conditions. + * + * @returns a request condition instance that is the result of combining the two condition instances. */ - boolean match(HttpServletRequest request); + This combine(This other); + /** + * Checks if this condition matches the provided request and returns a potentially new request condition + * with content tailored to the current request. For example a condition with URL patterns might return + * a new condition that contains matching patterns sorted with best matching patterns on top. + * + * @return a condition instance in case of a match; or {@code null} if there is no match. + */ + This getMatchingCondition(HttpServletRequest request); + + /** + * Compares "this" condition (i.e. the current instance) with another condition in the context of a request. + *

    Note: it is assumed instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * to ensure they have content relevant to current request only. + */ + int compareTo(This other, HttpServletRequest request); + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java deleted file mode 100644 index f67aa1fba31..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2002-2011 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.servlet.mvc.method.condition; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * Factory for {@link RequestCondition} objects. - * - * @author Arjen Poutsma - * @author Rossen Stoyanchev - * @since 3.1 - */ -public abstract class RequestConditionFactory { - - private static final String CONTENT_TYPE_HEADER = "Content-Type"; - - private static final String ACCEPT_HEADER = "Accept"; - - /** - * Parses the given request methods, and returns them as a single request condition. - * - * @param methods the methods - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#method() - */ - public static RequestMethodsRequestCondition parseMethods(RequestMethod... methods) { - return methods != null ? new RequestMethodsRequestCondition(methods) : new RequestMethodsRequestCondition(); - } - - /** - * Parses the given parameters, and returns them as a single request condition. - * - * @param params the parameters - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#params() - */ - public static ParamsRequestCondition parseParams(String... params) { - return params != null ? new ParamsRequestCondition(params) : new ParamsRequestCondition(); - } - - /** - * Parses the given headers, and returns them as a single request condition. - * - * @param headers the headers - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#headers() - */ - public static HeadersRequestCondition parseHeaders(String... headers) { - if (headers == null) { - return new HeadersRequestCondition(); - } - HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers); - - // filter out Accept and Content-Type headers, they are dealt with by produces and consumes respectively - Set filteredConditions = - new LinkedHashSet(headersCondition.getConditions()); - - for (Iterator iterator = filteredConditions.iterator(); - iterator.hasNext();) { - HeadersRequestCondition.HeaderRequestCondition headerCondition = iterator.next(); - if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name) || - CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) { - iterator.remove(); - } - } - return new HeadersRequestCondition(filteredConditions); - } - - /** - * Parses the given consumes, and returns them as a single request condition. - * - * @param consumes the consumes - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#consumes() - */ - public static ConsumesRequestCondition parseConsumes(String... consumes) { - return new ConsumesRequestCondition(consumes); - } - - /** - * Parses the given consumes and {@code Content-Type} headers, and returns them as a single request condition.

    Only - * {@code Content-Type} headers will be used, all other headers will be ignored. - * - * @param consumes the consumes - * @param headers the headers - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#consumes() - */ - public static ConsumesRequestCondition parseConsumes(String[] consumes, String[] headers) { - - List allConditions = parseContentTypeHeaders(headers); - - // ignore the default consumes() value if any content-type headers have been set - boolean headersHasContentType = !allConditions.isEmpty(); - boolean consumesHasDefaultValue = consumes.length == 1 && consumes[0].equals("*/*"); - if (!headersHasContentType || !consumesHasDefaultValue) { - for (String consume : consumes) { - allConditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(consume)); - } - } - return new ConsumesRequestCondition(allConditions); - } - - private static List parseContentTypeHeaders(String[] headers) { - List conditions = - new ArrayList(); - HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers); - for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) { - List mediaTypes = MediaType.parseMediaTypes(headerCondition.value); - for (MediaType mediaType : mediaTypes) { - conditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(mediaType, - headerCondition.isNegated)); - } - } - } - return conditions; - } - - /** - * Parses the given produces, and returns them as a single request condition. - * - * @param produces the produces - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#produces() - */ - public static ProducesRequestCondition parseProduces(String... produces) { - return new ProducesRequestCondition(produces); - } - - /** - * Parses the given produces and {@code Accept} headers, and returns them as a single request condition.

    Only {@code - * Accept} headers will be used, all other headers will be ignored. - * - * @param produces the consumes - * @param headers the headers - * @return the request condition - * @see org.springframework.web.bind.annotation.RequestMapping#produces() - */ - public static ProducesRequestCondition parseProduces(String[] produces, String[] headers) { - - List allConditions = parseAcceptHeaders(headers); - - // ignore the default consumes() value if any accept headers have been set - boolean headersHasAccept = !allConditions.isEmpty(); - boolean producesHasDefaultValue = produces.length == 1 && produces[0].equals("*/*"); - if (!headersHasAccept || !producesHasDefaultValue) { - for (String produce : produces) { - allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(produce)); - } - } - return new ProducesRequestCondition(allConditions); - } - - private static List parseAcceptHeaders(String[] headers) { - List allConditions = - new ArrayList(); - HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers); - for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) { - if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name)) { - List mediaTypes = MediaType.parseMediaTypes(headerCondition.value); - for (MediaType mediaType : mediaTypes) { - allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(mediaType, - headerCondition.isNegated)); - } - } - } - return allConditions; - } - - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java similarity index 52% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java index 02914a7725f..7d2faad65aa 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java @@ -17,64 +17,62 @@ package org.springframework.web.servlet.mvc.method.condition; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; /** - * Abstract base class for {@link RequestCondition} implementations that wrap other request conditions. - * - * @author Arjen Poutsma + * Base class for {@link RequestCondition}s. + * + * @author Rossen Stoyanchev * @since 3.1 */ -abstract class RequestConditionComposite implements RequestCondition { - - private final Set conditions; - - protected RequestConditionComposite(Collection conditions) { - this.conditions = Collections.unmodifiableSet(new LinkedHashSet(conditions)); - } - - protected Set getConditions() { - return conditions; - } - - public boolean isEmpty() { - return conditions.isEmpty(); - } - +abstract class RequestConditionSupport> implements RequestCondition { + + /** + * Returns the individual expressions a request condition is composed of such as + * URL patterns, HTTP request methods, parameter expressions, etc. + */ + protected abstract Collection getContent(); + @Override public boolean equals(Object o) { if (this == o) { return true; } if (o != null && getClass().equals(o.getClass())) { - RequestConditionComposite other = (RequestConditionComposite) o; - return this.conditions.equals(other.conditions); + RequestConditionSupport other = (RequestConditionSupport) o; + return getContent().equals(other.getContent()); } return false; } @Override public int hashCode() { - return conditions.hashCode(); + return getContent().hashCode(); } @Override public String toString() { StringBuilder builder = new StringBuilder("["); - String infix = getToStringInfix(); - for (Iterator iterator = conditions.iterator(); iterator.hasNext();) { - RequestCondition condition = iterator.next(); - builder.append(condition.toString()); + for (Iterator iterator = getContent().iterator(); iterator.hasNext();) { + Object expression = iterator.next(); + builder.append(expression.toString()); if (iterator.hasNext()) { - builder.append(infix); + if (isLogicalConjunction()) { + builder.append(" && "); + } + else { + builder.append(" || "); + } } } builder.append("]"); return builder.toString(); } - protected abstract String getToStringInfix(); + /** + * Returns {@code true} if the individual expressions of the condition are combined via logical + * conjunction (" && "); or {@code false} otherwise. + */ + protected abstract boolean isLogicalConjunction(); + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java index 4a851567b8c..8be1f398b09 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java @@ -22,132 +22,102 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; + import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.RequestMethod; /** - * Represents a collection of {@link RequestMethod} conditions, typically obtained from {@link - * org.springframework.web.bind.annotation.RequestMapping#method() @RequestMapping.methods()}. - * + * A logical disjunction (' || ') request condition that matches a request against a set of {@link RequestMethod}s. + * + *

    If the condition is created with 0 HTTP request methods, it matches to every request. + * * @author Arjen Poutsma - * @see RequestConditionFactory#parseMethods(RequestMethod...) + * @author Rossen Stoyanchev * @since 3.1 */ -public class RequestMethodsRequestCondition - extends LogicalDisjunctionRequestCondition - implements Comparable { - - private RequestMethodsRequestCondition(Collection conditions) { - super(conditions); - } - - RequestMethodsRequestCondition(RequestMethod... methods) { - this(parseConditions(Arrays.asList(methods))); - } - - private static Set parseConditions(List methods) { - Set conditions = - new LinkedHashSet(methods.size()); - for (RequestMethod method : methods) { - conditions.add(new RequestMethodRequestCondition(method)); - } - return conditions; - } +public class RequestMethodsRequestCondition extends RequestConditionSupport { + private final Set methods; /** - * Creates an empty set of method request conditions. + * Create a {@link RequestMethodsRequestCondition} with the given {@link RequestMethod}s. + * @param requestMethods 0 or more HTTP request methods; if 0 the condition will match to every request. */ - public RequestMethodsRequestCondition() { - this(Collections.emptySet()); + public RequestMethodsRequestCondition(RequestMethod... requestMethods) { + this(asList(requestMethods)); } + private static List asList(RequestMethod... requestMethods) { + return requestMethods != null ? Arrays.asList(requestMethods) : Collections.emptyList(); + } + + /** + * Private constructor. + */ + private RequestMethodsRequestCondition(Collection requestMethods) { + this.methods = Collections.unmodifiableSet(new LinkedHashSet(requestMethods)); + } + /** * Returns all {@link RequestMethod}s contained in this condition. */ public Set getMethods() { - Set result = new LinkedHashSet(); - for (RequestMethodRequestCondition condition : getConditions()) { - result.add(condition.getMethod()); - } - return result; + return methods; } - public int compareTo(RequestMethodsRequestCondition other) { - return other.getConditions().size() - this.getConditions().size(); + @Override + protected Collection getContent() { + return methods; } + @Override + protected boolean isLogicalConjunction() { + return false; + } + /** - * Returns a new {@code RequestMethodsRequestCondition} that contains all conditions that match the request. - * - * @param request the request - * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match - */ - public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) { - if (isEmpty()) { - return this; - } - else { - if (match(request)) { - return new RequestMethodsRequestCondition(RequestMethod.valueOf(request.getMethod())); - } - else { - return null; - } - } - - } - - /** - * Combines this collection of request method conditions with another by combining all methods into a logical OR. - * - * @param other the condition to combine with + * Returns a new instance with a union of the HTTP request methods from "this" and the "other" instance. */ public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) { - Set conditions = - new LinkedHashSet(getConditions()); - conditions.addAll(other.getConditions()); - return new RequestMethodsRequestCondition(conditions); + Set set = new LinkedHashSet(this.methods); + set.addAll(other.methods); + return new RequestMethodsRequestCondition(set); } - static class RequestMethodRequestCondition implements RequestCondition { - - private final RequestMethod method; - - RequestMethodRequestCondition(RequestMethod method) { - this.method = method; + /** + * Checks if any of the HTTP request methods match the given request and returns an instance that + * contain the matching request method. + * @param request the current request + * @return the same instance if the condition contains no request method; + * or a new condition with the matching request method; or {@code null} if no request methods match. + */ + public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) { + if (methods.isEmpty()) { + return this; } - - RequestMethod getMethod() { - return method; - } - - public boolean match(HttpServletRequest request) { - RequestMethod method = RequestMethod.valueOf(request.getMethod()); - return this.method.equals(method); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; + RequestMethod incomingRequestMethod = RequestMethod.valueOf(request.getMethod()); + for (RequestMethod method : methods) { + if (method.equals(incomingRequestMethod)) { + return new RequestMethodsRequestCondition(method); } - if (obj != null && obj instanceof RequestMethodRequestCondition) { - RequestMethodRequestCondition other = (RequestMethodRequestCondition) obj; - return this.method.equals(other.method); - } - return false; - } - - @Override - public int hashCode() { - return method.hashCode(); - } - - @Override - public String toString() { - return method.toString(); } + return null; } + + /** + * Returns: + *

      + *
    • 0 if the two conditions contain the same number of HTTP request methods. + *
    • Less than 1 if "this" instance has an HTTP request method but "other" doesn't. + *
    • Greater than 1 "other" has an HTTP request method but "this" doesn't. + *
    + * + *

    It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)} + * and therefore each instance contains the matching HTTP request method only or is otherwise empty. + */ + public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) { + return other.methods.size() - this.methods.size(); + } + } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java index 01ae7f41a5d..90619c719b5 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java @@ -16,6 +16,11 @@ package org.springframework.web.servlet.mvc.method; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + import java.lang.reflect.Method; import java.util.Collections; import java.util.Comparator; @@ -23,17 +28,14 @@ import java.util.List; import org.junit.Before; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; +import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition; import org.springframework.web.util.UrlPathHelper; -import static java.util.Arrays.*; -import static org.junit.Assert.*; - /** * Test fixture with {@link RequestMappingHandlerMapping} testing its {@link RequestMappingInfo} comparator. * @@ -57,8 +59,8 @@ public class RequestMappingInfoComparatorTests { request.setRequestURI("/foo"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); Comparator comparator = handlerMapping.getMappingComparator(lookupPath, request); - RequestMappingInfo key1 = new RequestMappingInfo(asList("/fo*"), null); - RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo"), null); + RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/fo*"}); + RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo"}); assertEquals(1, comparator.compare(key1, key2)); } @@ -68,8 +70,8 @@ public class RequestMappingInfoComparatorTests { request.setRequestURI("/foo"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); Comparator comparator = handlerMapping.getMappingComparator(lookupPath, request); - RequestMappingInfo key1 = new RequestMappingInfo(asList("/foo*"), null); - RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo*"), null); + RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/foo*"}); + RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo*"}); assertEquals(0, comparator.compare(key1, key2)); } @@ -78,20 +80,20 @@ public class RequestMappingInfoComparatorTests { public void greaterNumberOfMatchingPatternsWins() throws Exception { request.setRequestURI("/foo.html"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key1 = new RequestMappingInfo(asList("/foo", "*.jpeg"), null); - RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo", "*.html"), null); + RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/foo", "*.jpeg"}); + RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo", "*.html"}); RequestMappingInfo match1 = handlerMapping.getMatchingMapping(key1, lookupPath, request); RequestMappingInfo match2 = handlerMapping.getMatchingMapping(key2, lookupPath, request); List matches = asList(match1, match2); Collections.sort(matches, handlerMapping.getMappingComparator(lookupPath, request)); - assertSame(match2.getPatterns(), matches.get(0).getPatterns()); + assertSame(match2.getPatternsCondition(), matches.get(0).getPatternsCondition()); } @Test public void oneMethodWinsOverNone() { Comparator comparator = handlerMapping.getMappingComparator("", request); - RequestMappingInfo key1 = new RequestMappingInfo(null, null); + RequestMappingInfo key1 = new RequestMappingInfo(null); RequestMappingInfo key2 = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET}); assertEquals(1, comparator.compare(key1, key2)); @@ -99,10 +101,11 @@ public class RequestMappingInfoComparatorTests { @Test public void methodsAndParams() { - RequestMappingInfo empty = new RequestMappingInfo(null, null); + RequestMappingInfo empty = new RequestMappingInfo(null); RequestMappingInfo oneMethod = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET}); RequestMappingInfo oneMethodOneParam = - new RequestMappingInfo(null, RequestConditionFactory.parseMethods(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null, null); + new RequestMappingInfo(null, new RequestMethodsRequestCondition(RequestMethod.GET), + new ParamsRequestCondition("foo"), null, null, null); List list = asList(empty, oneMethod, oneMethodOneParam); Collections.shuffle(list); Collections.sort(list, handlerMapping.getMappingComparator("", request)); @@ -114,9 +117,9 @@ public class RequestMappingInfoComparatorTests { @Test public void produces() { - RequestMappingInfo html = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("text/html")); - RequestMappingInfo xml = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("application/xml")); - RequestMappingInfo none = new RequestMappingInfo(null, null); + RequestMappingInfo html = new RequestMappingInfo(null, null, null, null, null, new ProducesRequestCondition("text/html")); + RequestMappingInfo xml = new RequestMappingInfo(null, null, null, null, null, new ProducesRequestCondition("application/xml")); + RequestMappingInfo none = new RequestMappingInfo(null); request.addHeader("Accept", "application/xml, text/html"); Comparator comparator = handlerMapping.getMappingComparator("", request); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index 62861a1f52e..b87f5060bd7 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -24,7 +24,6 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Map; import org.junit.Before; @@ -42,9 +41,13 @@ import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.MappedInterceptor; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; +import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition; import org.springframework.web.util.UrlPathHelper; /** @@ -129,7 +132,7 @@ public class RequestMappingInfoHandlerMappingTests { @Test public void uriTemplateVariables() { - RequestMappingInfo key = new RequestMappingInfo(Arrays.asList("/{path1}/{path2}"), null); + RequestMappingInfo key = new RequestMappingInfo(new String[] {"/{path1}/{path2}"}); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); @@ -197,12 +200,13 @@ public class RequestMappingInfoHandlerMappingTests { @Override protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); - return new RequestMappingInfo(Arrays.asList(annotation.value()), - RequestConditionFactory.parseMethods(annotation.method()), - RequestConditionFactory.parseParams(annotation.params()), - RequestConditionFactory.parseHeaders(annotation.headers()), - RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()), - RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers())); + return new RequestMappingInfo( + new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher()), + new RequestMethodsRequestCondition(annotation.method()), + new ParamsRequestCondition(annotation.params()), + new HeadersRequestCondition(annotation.headers()), + new ConsumesRequestCondition(annotation.consumes(), annotation.headers()), + new ProducesRequestCondition(annotation.produces(), annotation.headers())); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java index f03017af057..d566c887229 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java @@ -16,20 +16,19 @@ package org.springframework.web.servlet.mvc.method; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; -import org.springframework.web.util.UrlPathHelper; - -import static java.util.Arrays.*; -import static java.util.Collections.*; -import static org.junit.Assert.*; -import static org.springframework.web.bind.annotation.RequestMethod.*; +import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition; +import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition; /** * Test fixture for {@link RequestMappingInfo} tests. @@ -41,8 +40,8 @@ public class RequestMappingInfoTests { @Test public void equals() { - RequestMappingInfo key1 = new RequestMappingInfo(singleton("/foo"), methods(GET)); - RequestMappingInfo key2 = new RequestMappingInfo(singleton("/foo"), methods(GET)); + RequestMappingInfo key1 = new RequestMappingInfo(new String[] {"/foo"}, GET); + RequestMappingInfo key2 = new RequestMappingInfo(new String[] {"/foo"}, GET); assertEquals(key1, key2); assertEquals(key1.hashCode(), key2.hashCode()); @@ -50,8 +49,8 @@ public class RequestMappingInfoTests { @Test public void equalsPrependSlash() { - RequestMappingInfo key1 = new RequestMappingInfo(singleton("/foo"), methods(GET)); - RequestMappingInfo key2 = new RequestMappingInfo(singleton("foo"), methods(GET)); + RequestMappingInfo key1 = new RequestMappingInfo(new String[] {"/foo"}, GET); + RequestMappingInfo key2 = new RequestMappingInfo(new String[] {"foo"}, GET); assertEquals(key1, key2); assertEquals(key1.hashCode(), key2.hashCode()); @@ -59,213 +58,190 @@ public class RequestMappingInfoTests { @Test public void combinePatterns() { - AntPathMatcher pathMatcher = new AntPathMatcher(); + RequestMappingInfo key1 = createFromPatterns("/t1", "/t2"); + RequestMappingInfo key2 = createFromPatterns("/m1", "/m2"); + RequestMappingInfo key3 = createFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"); + assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition()); - RequestMappingInfo key1 = createKeyFromPatterns("/t1", "/t2"); - RequestMappingInfo key2 = createKeyFromPatterns("/m1", "/m2"); - RequestMappingInfo key3 = createKeyFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"); - assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns()); + key1 = createFromPatterns("/t1"); + key2 = createFromPatterns(); + key3 = createFromPatterns("/t1"); + assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition()); - key1 = createKeyFromPatterns("/t1"); - key2 = createKeyFromPatterns(); - key3 = createKeyFromPatterns("/t1"); - assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns()); + key1 = createFromPatterns(); + key2 = createFromPatterns("/m1"); + key3 = createFromPatterns("/m1"); + assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition()); - key1 = createKeyFromPatterns(); - key2 = createKeyFromPatterns("/m1"); - key3 = createKeyFromPatterns("/m1"); - assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns()); + key1 = createFromPatterns(); + key2 = createFromPatterns(); + key3 = createFromPatterns(""); + assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition()); - key1 = createKeyFromPatterns(); - key2 = createKeyFromPatterns(); - key3 = createKeyFromPatterns(""); - assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns()); - - key1 = createKeyFromPatterns("/t1"); - key2 = createKeyFromPatterns(""); - key3 = createKeyFromPatterns("/t1"); - assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns()); + key1 = createFromPatterns("/t1"); + key2 = createFromPatterns(""); + key3 = createFromPatterns("/t1"); + assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition()); } @Test public void matchPatternsToRequest() { - UrlPathHelper pathHelper = new UrlPathHelper(); - PathMatcher pathMatcher = new AntPathMatcher(); - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); - RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null); - RequestMappingInfo match = - key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + RequestMappingInfo match = createFromPatterns("/foo").getMatchingRequestMapping(request); assertNotNull(match); request = new MockHttpServletRequest("GET", "/foo/bar"); - key = new RequestMappingInfo(singleton("/foo/*"), null); - match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + match = createFromPatterns("/foo/*").getMatchingRequestMapping(request); assertNotNull("Pattern match", match); request = new MockHttpServletRequest("GET", "/foo.html"); - key = new RequestMappingInfo(singleton("/foo"), null); - match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + match = createFromPatterns("/foo").getMatchingRequestMapping(request); assertNotNull("Implicit match by extension", match); - assertEquals("Contains matched pattern", "/foo.*", match.getPatterns().iterator().next()); + assertEquals("Contains matched pattern", "/foo.*", match.getPatternsCondition().getPatterns().iterator().next()); request = new MockHttpServletRequest("GET", "/foo/"); - key = new RequestMappingInfo(singleton("/foo"), null); - match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + match = createFromPatterns("/foo").getMatchingRequestMapping(request); assertNotNull("Implicit match by trailing slash", match); - assertEquals("Contains matched pattern", "/foo/", match.getPatterns().iterator().next()); + assertEquals("Contains matched pattern", "/foo/", match.getPatternsCondition().getPatterns().iterator().next()); request = new MockHttpServletRequest("GET", "/foo.html"); - key = new RequestMappingInfo(singleton("/foo.jpg"), null); - match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + match = createFromPatterns("/foo.jpg").getMatchingRequestMapping(request); assertNull("Implicit match ignored if pattern has extension", match); request = new MockHttpServletRequest("GET", "/foo.html"); - key = new RequestMappingInfo(singleton("/foo.jpg"), null); - match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher); + match = createFromPatterns("/foo.jpg").getMatchingRequestMapping(request); assertNull("Implicit match ignored on pattern with trailing slash", match); } @Test public void matchRequestMethods() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + RequestMappingInfo key = createFromPatterns("/foo"); + RequestMappingInfo match = createFromPatterns("/foo").getMatchingRequestMapping(request); assertNotNull("No method matches any method", match); - key = new RequestMappingInfo(singleton("/foo"), methods(GET)); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo(new String[]{"/foo"}, GET); + match = key.getMatchingRequestMapping(request); assertNotNull("Exact match", match); - key = new RequestMappingInfo(singleton("/foo"), methods(POST)); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo(new String[]{"/foo"}, POST); + match = key.getMatchingRequestMapping(request); assertNull("No match", match); } @Test public void matchingKeyContent() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(asList("/foo*", "/bar"), methods(GET, POST)); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); - RequestMappingInfo expected = new RequestMappingInfo(singleton("/foo*"), methods(GET)); + RequestMappingInfo key = new RequestMappingInfo(new String[] {"/foo*", "/bar"}, GET, POST); + RequestMappingInfo match = key.getMatchingRequestMapping(request); + RequestMappingInfo expected = new RequestMappingInfo(new String[] {"/foo*"}, GET); assertEquals("Matching RequestKey contains matched patterns and methods only", expected, match); - key = new RequestMappingInfo(asList("/**", "/foo*", "/foo"), null); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); - expected = new RequestMappingInfo(asList("/foo", "/foo*", "/**"), null); + key = createFromPatterns("/**", "/foo*", "/foo"); + match = key.getMatchingRequestMapping(request); + expected = createFromPatterns("/foo", "/foo*", "/**"); assertEquals("Matched patterns are sorted with best match at the top", expected, match); } @Test public void paramsCondition() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); request.setParameter("foo", "bar"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); RequestMappingInfo key = - new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null, - null, null); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, + new ParamsRequestCondition("foo=bar"), null, null, null); + RequestMappingInfo match = key.getMatchingRequestMapping(request); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null, - null, null); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, + new ParamsRequestCondition("foo!=bar"), null, null, null); + match = key.getMatchingRequestMapping(request); assertNull(match); } @Test public void headersCondition() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); request.addHeader("foo", "bar"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); RequestMappingInfo key = - new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"), - null, null); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, + new HeadersRequestCondition("foo=bar"), null, null); + RequestMappingInfo match = key.getMatchingRequestMapping(request); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"), - null, null); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, + new HeadersRequestCondition("foo!=bar"), null, null); + match = key.getMatchingRequestMapping(request); assertNull(match); } @Test public void consumesCondition() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); request.setContentType("text/plain"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null, - RequestConditionFactory.parseConsumes("text/plain"), null); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + RequestMappingInfo key = + new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, null, + new ConsumesRequestCondition("text/plain"), null); + RequestMappingInfo match = key.getMatchingRequestMapping(request); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, null, null, - RequestConditionFactory.parseConsumes("application/xml"), null); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, null, + new ConsumesRequestCondition("application/xml"), null); + match = key.getMatchingRequestMapping(request); assertNull(match); } @Test public void producesCondition() { - PathMatcher pathMatcher = new AntPathMatcher(); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); request.addHeader("Accept", "text/plain"); - String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null, - null, RequestConditionFactory.parseProduces("text/plain")); - RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + RequestMappingInfo key = + new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, null, null, + new ProducesRequestCondition("text/plain")); + RequestMappingInfo match = key.getMatchingRequestMapping(request); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, null, null, null, - RequestConditionFactory.parseProduces("application/xml")); - match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + key = new RequestMappingInfo( + new PatternsRequestCondition("/foo"), null, null, null, null, + new ProducesRequestCondition("application/xml")); + match = key.getMatchingRequestMapping(request); assertNull(match); } - private RequestMappingInfo createKeyFromPatterns(String... patterns) { - return new RequestMappingInfo(asList(patterns), null); - } - - private RequestMethod[] methods(RequestMethod... methods) { - if (methods != null) { - return methods; - } - else { - return new RequestMethod[0]; - } + private RequestMappingInfo createFromPatterns(String... patterns) { + return new RequestMappingInfo(patterns); } } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java index c5bb910fc59..d93b0d03eb8 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java @@ -16,13 +16,17 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collection; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; +import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression; /** * @author Arjen Poutsma @@ -31,75 +35,79 @@ public class ConsumesRequestConditionTests { @Test public void consumesMatch() { - RequestCondition condition = new ConsumesRequestCondition("text/plain"); + ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContentType("text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void negatedConsumesMatch() { - RequestCondition condition = new ConsumesRequestCondition("!text/plain"); + ConsumesRequestCondition condition = new ConsumesRequestCondition("!text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContentType("text/plain"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void consumesWildcardMatch() { - RequestCondition condition = new ConsumesRequestCondition("text/*"); + ConsumesRequestCondition condition = new ConsumesRequestCondition("text/*"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContentType("text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void consumesMultipleMatch() { - RequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml"); + ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContentType("text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void consumesSingleNoMatch() { - RequestCondition condition = new ConsumesRequestCondition("text/plain"); + ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContentType("application/xml"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void compareToSingle() { + MockHttpServletRequest request = new MockHttpServletRequest(); + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain"); ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*"); - int result = condition1.compareTo(condition2); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); } @Test public void compareToMultiple() { + MockHttpServletRequest request = new MockHttpServletRequest(); + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("*/*", "text/plain"); ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*", "text/plain;q=0.7"); - int result = condition1.compareTo(condition2); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); } @@ -126,28 +134,11 @@ public class ConsumesRequestConditionTests { public void parseConsumesAndHeaders() { String[] consumes = new String[] {"text/plain"}; String[] headers = new String[]{"foo=bar", "content-type=application/xml,application/pdf"}; - ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); + ConsumesRequestCondition condition = new ConsumesRequestCondition(consumes, headers); assertConditions(condition, "text/plain", "application/xml", "application/pdf"); } - @Test - public void parseConsumesDefault() { - String[] consumes = new String[] {"*/*"}; - String[] headers = new String[0]; - ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); - - assertConditions(condition, "*/*"); - } - @Test - public void parseConsumesDefaultAndHeaders() { - String[] consumes = new String[] {"*/*"}; - String[] headers = new String[]{"foo=bar", "content-type=text/plain"}; - ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); - - assertConditions(condition, "text/plain"); - } - @Test public void getMatchingCondition() { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -165,12 +156,12 @@ public class ConsumesRequestConditionTests { } private void assertConditions(ConsumesRequestCondition condition, String... expected) { - Set conditions = condition.getConditions(); - assertEquals("Invalid amount of conditions", conditions.size(), expected.length); + Collection expressions = condition.getContent(); + assertEquals("Invalid amount of conditions", expressions.size(), expected.length); for (String s : expected) { boolean found = false; - for (ConsumesRequestCondition.ConsumeRequestCondition requestCondition : conditions) { - String conditionMediaType = requestCondition.getMediaType().toString(); + for (ConsumeMediaTypeExpression expr : expressions) { + String conditionMediaType = expr.getMediaType().toString(); if (conditionMediaType.equals(s)) { found = true; break; @@ -181,12 +172,6 @@ public class ConsumesRequestConditionTests { fail("Condition [" + s + "] not found"); } } - - - } - - - } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java index d480015f19f..092a6310272 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java @@ -16,13 +16,17 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; +import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression; /** * @author Arjen Poutsma @@ -40,81 +44,83 @@ public class HeadersRequestConditionTests { @Test public void headerPresent() { - RequestCondition condition = new HeadersRequestCondition("accept"); + HeadersRequestCondition condition = new HeadersRequestCondition("accept"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", ""); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void headerPresentNoMatch() { - RequestCondition condition = new HeadersRequestCondition("foo"); + HeadersRequestCondition condition = new HeadersRequestCondition("foo"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("bar", ""); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void headerNotPresent() { - RequestCondition condition = new HeadersRequestCondition("!accept"); + HeadersRequestCondition condition = new HeadersRequestCondition("!accept"); MockHttpServletRequest request = new MockHttpServletRequest(); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void headerValueMatch() { - RequestCondition condition = new HeadersRequestCondition("foo=bar"); + HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("foo", "bar"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void headerValueNoMatch() { - RequestCondition condition = new HeadersRequestCondition("foo=bar"); + HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("foo", "bazz"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void headerCaseSensitiveValueMatch() { - RequestCondition condition = new HeadersRequestCondition("foo=Bar"); + HeadersRequestCondition condition = new HeadersRequestCondition("foo=Bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("foo", "bar"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void headerValueMatchNegated() { - RequestCondition condition = new HeadersRequestCondition("foo!=bar"); + HeadersRequestCondition condition = new HeadersRequestCondition("foo!=bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("foo", "baz"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void compareTo() { + MockHttpServletRequest request = new MockHttpServletRequest(); + HeadersRequestCondition condition1 = new HeadersRequestCondition("foo", "bar", "baz"); HeadersRequestCondition condition2 = new HeadersRequestCondition("foo", "bar"); - int result = condition1.compareTo(condition2); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); } @@ -125,7 +131,7 @@ public class HeadersRequestConditionTests { HeadersRequestCondition condition2 = new HeadersRequestCondition("foo=baz"); HeadersRequestCondition result = condition1.combine(condition2); - Set conditions = result.getConditions(); + Collection conditions = result.getContent(); assertEquals(2, conditions.size()); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java index 9ab48d15418..9750f4b01f3 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java @@ -16,13 +16,17 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; +import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition.ParamExpression; /** * @author Arjen Poutsma @@ -41,62 +45,64 @@ public class ParamsRequestConditionTests { @Test public void paramPresent() { - RequestCondition condition = new ParamsRequestCondition("foo"); + ParamsRequestCondition condition = new ParamsRequestCondition("foo"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", ""); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void paramPresentNoMatch() { - RequestCondition condition = new ParamsRequestCondition("foo"); + ParamsRequestCondition condition = new ParamsRequestCondition("foo"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("bar", ""); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void paramNotPresent() { - RequestCondition condition = new ParamsRequestCondition("!foo"); + ParamsRequestCondition condition = new ParamsRequestCondition("!foo"); MockHttpServletRequest request = new MockHttpServletRequest(); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void paramValueMatch() { - RequestCondition condition = new ParamsRequestCondition("foo=bar"); + ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", "bar"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void paramValueNoMatch() { - RequestCondition condition = new ParamsRequestCondition("foo=bar"); + ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", "bazz"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void compareTo() { + MockHttpServletRequest request = new MockHttpServletRequest(); + ParamsRequestCondition condition1 = new ParamsRequestCondition("foo", "bar", "baz"); ParamsRequestCondition condition2 = new ParamsRequestCondition("foo", "bar"); - int result = condition1.compareTo(condition2); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); } @@ -106,7 +112,7 @@ public class ParamsRequestConditionTests { ParamsRequestCondition condition2 = new ParamsRequestCondition("foo=baz"); ParamsRequestCondition result = condition1.combine(condition2); - Set conditions = result.getConditions(); + Collection conditions = result.getContent(); assertEquals(2, conditions.size()); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestConditionTests.java index 2ba50282219..adf059af335 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestConditionTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestConditionTests.java @@ -16,17 +16,17 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collection; import org.junit.Test; - -import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; +import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition.ProduceMediaTypeExpression; /** * @author Arjen Poutsma @@ -35,65 +35,66 @@ public class ProducesRequestConditionTests { @Test public void consumesMatch() { - RequestCondition condition = new ProducesRequestCondition("text/plain"); + ProducesRequestCondition condition = new ProducesRequestCondition("text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void negatedConsumesMatch() { - RequestCondition condition = new ProducesRequestCondition("!text/plain"); + ProducesRequestCondition condition = new ProducesRequestCondition("!text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "text/plain"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void consumesWildcardMatch() { - RequestCondition condition = new ProducesRequestCondition("text/*"); + ProducesRequestCondition condition = new ProducesRequestCondition("text/*"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void consumesMultipleMatch() { - RequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml"); + ProducesRequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "text/plain"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void consumesSingleNoMatch() { - RequestCondition condition = new ProducesRequestCondition("text/plain"); + ProducesRequestCondition condition = new ProducesRequestCondition("text/plain"); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "application/xml"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void compareToSingle() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Accept", "text/plain"); + ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain"); ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*"); - List accept = Collections.singletonList(MediaType.TEXT_PLAIN); - - int result = condition1.compareTo(condition2, accept); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1, accept); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); } @@ -102,23 +103,25 @@ public class ProducesRequestConditionTests { ProducesRequestCondition condition1 = new ProducesRequestCondition("*/*", "text/plain"); ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*", "text/plain;q=0.7"); - List accept = Collections.singletonList(MediaType.TEXT_PLAIN); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Accept", "text/plain"); - int result = condition1.compareTo(condition2, accept); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1, accept); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); condition1 = new ProducesRequestCondition("*/*"); condition2 = new ProducesRequestCondition("text/*"); - accept = Collections.singletonList(new MediaType("text", "*")); + request = new MockHttpServletRequest(); + request.addHeader("Accept", "text/*"); - result = condition1.compareTo(condition2, accept); + result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result > 0); - result = condition2.compareTo(condition1, accept); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result < 0); } @@ -127,20 +130,24 @@ public class ProducesRequestConditionTests { ProducesRequestCondition condition1 = new ProducesRequestCondition("text/*", "text/plain"); ProducesRequestCondition condition2 = new ProducesRequestCondition("application/*", "application/xml"); - List accept = Arrays.asList(MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Accept", "text/plain"); + request.addHeader("Accept", "application/xml"); - int result = condition1.compareTo(condition2, accept); + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1, accept); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); - accept = Arrays.asList(MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN); + request = new MockHttpServletRequest(); + request.addHeader("Accept", "application/xml"); + request.addHeader("Accept", "text/plain"); - result = condition1.compareTo(condition2, accept); + result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result > 0); - result = condition2.compareTo(condition1, accept); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result < 0); } @@ -166,28 +173,11 @@ public class ProducesRequestConditionTests { public void parseConsumesAndHeaders() { String[] consumes = new String[] {"text/plain"}; String[] headers = new String[]{"foo=bar", "accept=application/xml,application/pdf"}; - ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers); + ProducesRequestCondition condition = new ProducesRequestCondition(consumes, headers); assertConditions(condition, "text/plain", "application/xml", "application/pdf"); } - @Test - public void parseConsumesDefault() { - String[] consumes = new String[] {"*/*"}; - String[] headers = new String[0]; - ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers); - - assertConditions(condition, "*/*"); - } - @Test - public void parseConsumesDefaultAndHeaders() { - String[] consumes = new String[] {"*/*"}; - String[] headers = new String[]{"foo=bar", "accept=text/plain"}; - ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers); - - assertConditions(condition, "text/plain"); - } - @Test public void getMatchingCondition() { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -205,12 +195,12 @@ public class ProducesRequestConditionTests { } private void assertConditions(ProducesRequestCondition condition, String... expected) { - Set conditions = condition.getConditions(); - assertEquals("Invalid amount of conditions", conditions.size(), expected.length); + Collection expressions = condition.getContent(); + assertEquals("Invalid amount of conditions", expressions.size(), expected.length); for (String s : expected) { boolean found = false; - for (ProducesRequestCondition.ProduceRequestCondition requestCondition : conditions) { - String conditionMediaType = requestCondition.getMediaType().toString(); + for (ProduceMediaTypeExpression expr : expressions) { + String conditionMediaType = expr.getMediaType().toString(); if (conditionMediaType.equals(s)) { found = true; break; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestConditionTests.java index ab16fe22ca1..a3d153ea304 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestConditionTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestConditionTests.java @@ -30,29 +30,29 @@ public class RequestMethodsRequestConditionTests { @Test public void methodMatch() { - RequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET); + RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @Test public void methodNoMatch() { - RequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET); + RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET); MockHttpServletRequest request = new MockHttpServletRequest("POST", "/foo"); - assertFalse(condition.match(request)); + assertNull(condition.getMatchingCondition(request)); } @Test public void multipleMethodsMatch() { - RequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST); + RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); - assertTrue(condition.match(request)); + assertNotNull(condition.getMatchingCondition(request)); } @@ -62,16 +62,18 @@ public class RequestMethodsRequestConditionTests { RequestMethodsRequestCondition condition2 = new RequestMethodsRequestCondition(RequestMethod.POST); RequestMethodsRequestCondition condition3 = new RequestMethodsRequestCondition(); - int result = condition1.compareTo(condition2); + MockHttpServletRequest request = new MockHttpServletRequest(); + + int result = condition1.compareTo(condition2, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition2.compareTo(condition1); + result = condition2.compareTo(condition1, request); assertTrue("Invalid comparison result: " + result, result > 0); - result = condition2.compareTo(condition3); + result = condition2.compareTo(condition3, request); assertTrue("Invalid comparison result: " + result, result < 0); - result = condition1.compareTo(condition1); + result = condition1.compareTo(condition1, request); assertEquals("Invalid comparison result ", 0, result); } @@ -81,7 +83,7 @@ public class RequestMethodsRequestConditionTests { RequestMethodsRequestCondition condition2 = new RequestMethodsRequestCondition(RequestMethod.POST); RequestMethodsRequestCondition result = condition1.combine(condition2); - assertEquals(2, result.getConditions().size()); + assertEquals(2, result.getContent().size()); }