From ff89c0e55a4f495a341c6b829e9b7c22784e2d5f Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 8 Apr 2011 10:50:45 +0000 Subject: [PATCH] SPR-7354 - Added equivalent of JAX-RS @Consumes to Spring MVC --- .../mvc/method/annotation/RequestKey.java | 25 ++++----- .../RequestMappingHandlerMethodMapping.java | 4 +- .../condition/AbstractNameValueCondition.java | 8 +-- .../condition/AbstractRequestCondition.java | 42 +++++++++++++++ .../condition/ConsumesRequestCondition.java | 6 +-- .../method/condition/RequestCondition.java | 9 +--- .../condition/RequestConditionComposite.java | 19 ++++--- .../condition/RequestConditionFactory.java | 54 ++++++++++++------- 8 files changed, 105 insertions(+), 62 deletions(-) create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java index c6d358d02a..1fcdb95516 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java @@ -155,15 +155,19 @@ public final class RequestKey { * Combines this {@code RequestKey} with another. The typical use case for this is combining type * and method-level {@link RequestMapping @RequestMapping} annotations. * - * @param other the method-level RequestKey + * @param methodKey the method-level RequestKey * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns * @return the combined request key */ - public RequestKey combine(RequestKey other, PathMatcher pathMatcher) { - Set patterns = combinePatterns(this.patterns, other.patterns, pathMatcher); - Set methods = union(this.methods, other.methods); - RequestCondition params = RequestConditionFactory.and(this.paramsCondition, other.paramsCondition); - RequestCondition headers = RequestConditionFactory.and(this.headersCondition, other.headersCondition); + public RequestKey combine(RequestKey methodKey, PathMatcher pathMatcher) { + Set patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher); + Set methods = union(this.methods, methodKey.methods); + RequestCondition params = RequestConditionFactory.and(this.paramsCondition, methodKey.paramsCondition); + RequestCondition headers = RequestConditionFactory.and(this.headersCondition, methodKey.headersCondition); + RequestCondition consumes; +// if (methodKey.consumesCondition.weight() > this.consumesCondition.weight()) { +// +// } return new RequestKey(patterns, methods, params, headers, null); } @@ -277,15 +281,6 @@ public final class RequestKey { } return null; } - - private static boolean checkConditions(Set conditions, HttpServletRequest request) { - for (RequestCondition condition : conditions) { - if (!condition.match(request)) { - return false; - } - } - return true; - } @Override public boolean equals(Object obj) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java index 51f68acda6..e6c0f30d8a 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java @@ -252,11 +252,11 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap if (result != 0) { return result; } - result = otherKey.getParams().weight() - key.getParams().weight(); + result = key.getParams().compareTo(otherKey.getParams()); if (result != 0) { return result; } - result = otherKey.getHeaders().weight() - key.getHeaders().weight(); + result = key.getHeaders().compareTo(otherKey.getHeaders()); if (result != 0) { return result; } 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/AbstractNameValueCondition.java index 58ed4faec4..567b5fd961 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/AbstractNameValueCondition.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest; * @author Arjen Poutsma * @since 3.1 */ -abstract class AbstractNameValueCondition implements RequestCondition { +abstract class AbstractNameValueCondition extends AbstractRequestCondition { protected final String name; @@ -35,6 +35,7 @@ abstract class AbstractNameValueCondition implements RequestCondition { protected final boolean isNegated; AbstractNameValueCondition(String expression) { + super(1); int separator = expression.indexOf('='); if (separator == -1) { this.isNegated = expression.startsWith("!"); @@ -65,11 +66,6 @@ abstract class AbstractNameValueCondition implements RequestCondition { protected abstract boolean matchValue(HttpServletRequest request); - public int weight() { - return 1; - } - - @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/AbstractRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java new file mode 100644 index 0000000000..4cc921b5e0 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java @@ -0,0 +1,42 @@ +/* + * 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; + +/** + * Abstract base class for {@link RequestCondition} that provides a standard {@link Comparable} implementation. + * + * @author Arjen Poutsma + * @since 3.1 + */ +public abstract class AbstractRequestCondition implements RequestCondition { + + private final int weight; + + protected AbstractRequestCondition(int weight) { + this.weight = weight; + } + + public int getWeight() { + return weight; + } + + public int compareTo(RequestCondition o) { + AbstractRequestCondition other = (AbstractRequestCondition) o; + return other.weight - this.weight; + } + +} 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 76d0ba78db..9147146b25 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 @@ -24,11 +24,12 @@ import org.springframework.util.StringUtils; /** * @author Arjen Poutsma */ -class ConsumesRequestCondition implements RequestCondition { +class ConsumesRequestCondition extends AbstractRequestCondition { private final MediaType mediaType; ConsumesRequestCondition(String mediaType) { + super(1); this.mediaType = MediaType.parseMediaType(mediaType); } @@ -41,7 +42,4 @@ class ConsumesRequestCondition implements RequestCondition { return false; } - public int weight() { - return 1; - } } 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 9139d0c4e0..7a016b822c 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 @@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest; * @see RequestConditionFactory * @since 3.1 */ -public interface RequestCondition { +public interface RequestCondition extends Comparable { /** * Indicates whether this condition matches against the given servlet request. @@ -39,11 +39,4 @@ public interface RequestCondition { */ boolean match(HttpServletRequest request); - /** - * Indicates the relative weight of this condition. More important conditions have a higher weight than ones that are - * less so. - * - * @return the weight of this condition - */ - int weight(); } 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/RequestConditionComposite.java index 3b0199f82e..c56b329311 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/RequestConditionComposite.java @@ -16,6 +16,7 @@ package org.springframework.web.servlet.mvc.method.condition; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -25,20 +26,24 @@ import java.util.List; * @author Arjen Poutsma * @since 3.1 */ -abstract class RequestConditionComposite implements RequestCondition { +abstract class RequestConditionComposite extends AbstractRequestCondition { protected final List conditions; - public RequestConditionComposite(List conditions) { - this.conditions = conditions; + protected RequestConditionComposite(List conditions) { + super(getWeight(conditions)); + this.conditions = Collections.unmodifiableList(conditions); } - public int weight() { - int size = 0; + private static int getWeight(List conditions) { + int weight = 0; for (RequestCondition condition : conditions) { - size += condition.weight(); + if (condition instanceof AbstractRequestCondition) { + AbstractRequestCondition abstractRequestCondition = (AbstractRequestCondition) condition; + weight += abstractRequestCondition.getWeight(); + } } - return size; + return weight; } @Override 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 index 2b52e7db64..85e53128b2 100644 --- 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 @@ -33,41 +33,42 @@ import org.springframework.util.ObjectUtils; */ public abstract class RequestConditionFactory { - private static final RequestCondition TRUE_CONDITION = new RequestCondition() { + private static final RequestCondition TRUE_CONDITION = new AbstractRequestCondition(0) { public boolean match(HttpServletRequest request) { return true; } - public int weight() { - return 0; - } - @Override public String toString() { return "TRUE"; } }; - private static final RequestCondition FALSE_CONDITION = new RequestCondition() { + private static final RequestCondition FALSE_CONDITION = new AbstractRequestCondition(0) { public boolean match(HttpServletRequest request) { return false; } - public int weight() { - return 0; - } - - @Override public String toString() { return "FALSE"; } }; + /** + * Returns a condition that always returns {@code true} for {@link RequestCondition#match(HttpServletRequest)}. + * + * @return a condition that returns {@code true} + */ public static RequestCondition trueCondition() { return TRUE_CONDITION; } + /** + * Returns a condition that always returns {@code false} for {@link RequestCondition#match(HttpServletRequest)}. + * + * @return a condition that returns {@code false} + */ public static RequestCondition falseCondition() { return FALSE_CONDITION; } @@ -87,7 +88,12 @@ public abstract class RequestConditionFactory { iterator.remove(); } } - return new LogicalConjunctionRequestCondition(filteredConditions); + if (filteredConditions.isEmpty()) { + return trueCondition(); + } + else { + return new LogicalConjunctionRequestCondition(filteredConditions); + } } /** @@ -108,7 +114,12 @@ public abstract class RequestConditionFactory { iterator.remove(); } } - return new LogicalDisjunctionRequestCondition(filteredConditions); + if (filteredConditions.isEmpty()) { + return trueCondition(); + } + else { + return new LogicalDisjunctionRequestCondition(filteredConditions); + } } /** @@ -120,7 +131,7 @@ public abstract class RequestConditionFactory { */ public static RequestCondition parseParams(String... params) { if (ObjectUtils.isEmpty(params)) { - return TRUE_CONDITION; + return trueCondition(); } RequestCondition[] result = new RequestCondition[params.length]; for (int i = 0; i < params.length; i++) { @@ -138,7 +149,7 @@ public abstract class RequestConditionFactory { */ public static RequestCondition parseHeaders(String... headers) { if (ObjectUtils.isEmpty(headers)) { - return TRUE_CONDITION; + return trueCondition(); } RequestCondition[] result = new RequestCondition[headers.length]; for (int i = 0; i < headers.length; i++) { @@ -157,9 +168,16 @@ public abstract class RequestConditionFactory { return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name); } + /** + * 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 RequestCondition parseConsumes(String... consumes) { if (ObjectUtils.isEmpty(consumes)) { - return TRUE_CONDITION; + return trueCondition(); } RequestCondition[] result = new RequestCondition[consumes.length]; for (int i = 0; i < consumes.length; i++) { @@ -168,8 +186,4 @@ public abstract class RequestConditionFactory { return or(result); } - // - // Conditions - // - }