Browse Source

SPR-7354 - Added equivalent of JAX-RS @Consumes to Spring MVC

pull/7/head
Arjen Poutsma 14 years ago
parent
commit
bf6693dbc5
  1. 285
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestConditionFactory.java
  2. 78
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java
  3. 5
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java
  4. 100
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java
  5. 47
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java
  6. 64
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java
  7. 48
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java
  8. 31
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java
  9. 73
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java
  10. 66
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java
  11. 49
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java
  12. 77
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java
  13. 175
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java
  14. 155
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestConditionFactoryTests.java
  15. 41
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java
  16. 78
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java
  17. 15
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMappingTests.java
  18. 212
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java
  19. 4
      org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java

285
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestConditionFactory.java

@ -1,285 +0,0 @@ @@ -1,285 +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.annotation;
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;
import org.springframework.web.util.WebUtils;
/**
* Factory for request condition objects.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
*/
public abstract class RequestConditionFactory {
/**
* Parses the given parameters, and returns them as a set of request conditions.
*
* @param params the parameters
* @return the request conditions
* @see org.springframework.web.bind.annotation.RequestMapping#params()
*/
public static Set<RequestCondition> parseParams(String... params) {
if (params == null) {
return Collections.emptySet();
}
Set<RequestCondition> result = new LinkedHashSet<RequestCondition>(params.length);
for (String expression : params) {
result.add(new ParamNameValueCondition(expression));
}
return result;
}
/**
* Parses the given headers, and returns them as a set of request conditions.
*
* @param headers the headers
* @return the request conditions
* @see org.springframework.web.bind.annotation.RequestMapping#headers()
*/
public static Set<RequestCondition> parseHeaders(String... headers) {
if (headers == null) {
return Collections.emptySet();
}
Set<RequestCondition> result = new LinkedHashSet<RequestCondition>(headers.length);
for (String expression : headers) {
HeaderNameValueCondition header = new HeaderNameValueCondition(expression);
if (isMediaTypeHeader(header.name)) {
result.add(new MediaTypeHeaderNameValueCondition(expression));
}
else {
result.add(header);
}
}
return result;
}
private static boolean isMediaTypeHeader(String name) {
return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name);
}
/**
* A condition that supports simple "name=value" style expressions as documented in
* <code>@RequestMapping.params()</code> and <code>@RequestMapping.headers()</code>.
*/
private static abstract class AbstractNameValueCondition<T> implements RequestCondition {
protected final String name;
protected final T value;
protected final boolean isNegated;
protected AbstractNameValueCondition(String expression) {
int separator = expression.indexOf('=');
if (separator == -1) {
this.isNegated = expression.startsWith("!");
this.name = isNegated ? expression.substring(1) : expression;
this.value = null;
}
else {
this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!');
this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator);
this.value = parseValue(expression.substring(separator + 1));
}
}
protected abstract T parseValue(String valueExpression);
public final boolean match(HttpServletRequest request) {
boolean isMatch;
if (this.value != null) {
isMatch = matchValue(request);
}
else {
isMatch = matchName(request);
}
return isNegated ? !isMatch : isMatch;
}
protected abstract boolean matchName(HttpServletRequest request);
protected abstract boolean matchValue(HttpServletRequest request);
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (value != null) {
builder.append(name);
if (isNegated) {
builder.append('!');
}
builder.append('=');
builder.append(value);
}
else {
if (isNegated) {
builder.append('!');
}
builder.append(name);
}
return builder.toString();
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (isNegated ? 1 : 0);
return result;
}
}
/**
* Request parameter name-value condition.
*/
private static class ParamNameValueCondition extends AbstractNameValueCondition<String> {
private ParamNameValueCondition(String expression) {
super(expression);
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpServletRequest request) {
return WebUtils.hasSubmitParameter(request, name);
}
@Override
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 ParamNameValueCondition) {
ParamNameValueCondition other = (ParamNameValueCondition) obj;
return ((this.name.equals(other.name)) &&
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
this.isNegated == other.isNegated);
}
return false;
}
}
/**
* Request header name-value condition.
*/
static class HeaderNameValueCondition extends AbstractNameValueCondition<String> {
public HeaderNameValueCondition(String expression) {
super(expression);
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpServletRequest request) {
return request.getHeader(name) != null;
}
@Override
final protected boolean matchValue(HttpServletRequest request) {
return value.equals(request.getHeader(name));
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof HeaderNameValueCondition) {
HeaderNameValueCondition other = (HeaderNameValueCondition) obj;
return ((this.name.equalsIgnoreCase(other.name)) &&
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
this.isNegated == other.isNegated);
}
return false;
}
}
/**
* A RequestCondition that for headers that contain {@link org.springframework.http.MediaType MediaTypes}.
*/
private static class MediaTypeHeaderNameValueCondition extends AbstractNameValueCondition<List<MediaType>> {
public MediaTypeHeaderNameValueCondition(String expression) {
super(expression);
}
@Override
protected List<MediaType> parseValue(String valueExpression) {
return Collections.unmodifiableList(MediaType.parseMediaTypes(valueExpression));
}
@Override
protected boolean matchName(HttpServletRequest request) {
return request.getHeader(name) != null;
}
@Override
protected boolean matchValue(HttpServletRequest request) {
List<MediaType> requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(name));
for (MediaType mediaType : this.value) {
for (MediaType requestMediaType : requestMediaTypes) {
if (mediaType.includes(requestMediaType)) {
return true;
}
}
}
return false;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof MediaTypeHeaderNameValueCondition) {
MediaTypeHeaderNameValueCondition other = (MediaTypeHeaderNameValueCondition) obj;
return ((this.name.equalsIgnoreCase(other.name)) &&
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
this.isNegated == other.isNegated);
}
return false;
}
}
}

78
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestKey.java

@ -23,12 +23,13 @@ import java.util.Collections; @@ -23,12 +23,13 @@ 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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
import org.springframework.web.util.UrlPathHelper;
/**
@ -43,12 +44,18 @@ public final class RequestKey { @@ -43,12 +44,18 @@ public final class RequestKey {
private final Set<RequestMethod> methods;
private final Set<RequestCondition> paramConditions;
private final RequestCondition paramsCondition;
private final RequestCondition headersCondition;
private final Set<RequestCondition> headerConditions;
private final RequestCondition consumesCondition;
private int hash;
RequestKey(Collection<String> patterns, Collection<RequestMethod> methods) {
this(patterns, methods, null, null, null);
}
/**
* Creates a new {@code RequestKey} instance with the given parameters.
*
@ -56,12 +63,14 @@ public final class RequestKey { @@ -56,12 +63,14 @@ public final class RequestKey {
*/
RequestKey(Collection<String> patterns,
Collection<RequestMethod> methods,
Collection<RequestCondition> paramConditions,
Collection<RequestCondition> headerConditions) {
RequestCondition paramsCondition,
RequestCondition headersCondition,
RequestCondition consumesCondition) {
this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
this.methods = asUnmodifiableSet(methods);
this.paramConditions = asUnmodifiableSet(paramConditions);
this.headerConditions = asUnmodifiableSet(headerConditions);
this.paramsCondition = paramsCondition != null ? paramsCondition : RequestConditionFactory.trueCondition();
this.headersCondition = headersCondition != null ? headersCondition : RequestConditionFactory.trueCondition();
this.consumesCondition = consumesCondition != null ? consumesCondition : RequestConditionFactory.trueCondition();
}
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
@ -95,7 +104,9 @@ public final class RequestKey { @@ -95,7 +104,9 @@ public final class RequestKey {
public static RequestKey createFromRequestMapping(RequestMapping annotation) {
return new RequestKey(Arrays.asList(annotation.value()), Arrays.asList(annotation.method()),
RequestConditionFactory.parseParams(annotation.params()),
RequestConditionFactory.parseHeaders(annotation.headers()));
RequestConditionFactory.parseHeaders(annotation.headers()),
RequestConditionFactory.parseConsumes(annotation.consumes())
);
}
/**
@ -109,7 +120,7 @@ public final class RequestKey { @@ -109,7 +120,7 @@ public final class RequestKey {
public static RequestKey createFromServletRequest(HttpServletRequest request, UrlPathHelper urlPathHelper) {
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
RequestMethod method = RequestMethod.valueOf(request.getMethod());
return new RequestKey(Arrays.asList(lookupPath), Arrays.asList(method), null, null);
return new RequestKey(Collections.singleton(lookupPath), Collections.singleton(method));
}
/**
@ -129,32 +140,32 @@ public final class RequestKey { @@ -129,32 +140,32 @@ public final class RequestKey {
/**
* Returns the request parameters of this request key.
*/
public Set<RequestCondition> getParams() {
return paramConditions;
public RequestCondition getParams() {
return paramsCondition;
}
/**
* Returns the request headers of this request key.
*/
public Set<RequestCondition> getHeaders() {
return headerConditions;
public RequestCondition getHeaders() {
return headersCondition;
}
/**
* Creates a new {@code RequestKey} by combining it with another. The typical use case for this is combining type
* Combines this {@code RequestKey} with another. The typical use case for this is combining type
* and method-level {@link RequestMapping @RequestMapping} annotations.
*
* @param methodKey the method-level RequestKey
* @param other the method-level RequestKey
* @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
* @return the combined request key
*/
public RequestKey combine(RequestKey methodKey, PathMatcher pathMatcher) {
Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
Set<RequestMethod> methods = union(this.methods, methodKey.methods);
Set<RequestCondition> params = union(this.paramConditions, methodKey.paramConditions);
Set<RequestCondition> headers = union(this.headerConditions, methodKey.headerConditions);
public RequestKey combine(RequestKey other, PathMatcher pathMatcher) {
Set<String> patterns = combinePatterns(this.patterns, other.patterns, pathMatcher);
Set<RequestMethod> methods = union(this.methods, other.methods);
RequestCondition params = RequestConditionFactory.and(this.paramsCondition, other.paramsCondition);
RequestCondition headers = RequestConditionFactory.and(this.headersCondition, other.headersCondition);
return new RequestKey(patterns, methods, params, headers);
return new RequestKey(patterns, methods, params, headers, null);
}
private static Set<String> combinePatterns(Collection<String> typePatterns,
@ -205,7 +216,7 @@ public final class RequestKey { @@ -205,7 +216,7 @@ public final class RequestKey {
List<String> matchingPatterns = getMatchingPatterns(request, pathMatcher, urlPathHelper);
if (!matchingPatterns.isEmpty()) {
Set<RequestMethod> matchingMethods = getMatchingMethods(request);
return new RequestKey(matchingPatterns, matchingMethods, this.paramConditions, this.headerConditions);
return new RequestKey(matchingPatterns, matchingMethods, this.paramsCondition, this.headersCondition, null);
}
else {
return null;
@ -245,11 +256,11 @@ public final class RequestKey { @@ -245,11 +256,11 @@ public final class RequestKey {
}
private boolean checkParams(HttpServletRequest request) {
return checkConditions(paramConditions, request);
return paramsCondition.match(request);
}
private boolean checkHeaders(HttpServletRequest request) {
return checkConditions(headerConditions, request);
return headersCondition.match(request);
}
private String getMatchingPattern(String pattern, String lookupPath, PathMatcher pathMatcher) {
@ -284,8 +295,8 @@ public final class RequestKey { @@ -284,8 +295,8 @@ public final class RequestKey {
if (obj != null && obj instanceof RequestKey) {
RequestKey other = (RequestKey) obj;
return (this.patterns.equals(other.patterns) && this.methods.equals(other.methods) &&
this.paramConditions.equals(other.paramConditions) &&
this.headerConditions.equals(other.headerConditions));
this.paramsCondition.equals(other.paramsCondition) &&
this.headersCondition.equals(other.headersCondition));
}
return false;
}
@ -296,8 +307,8 @@ public final class RequestKey { @@ -296,8 +307,8 @@ public final class RequestKey {
if (result == 0) {
result = patterns.hashCode();
result = 31 * result + methods.hashCode();
result = 31 * result + paramConditions.hashCode();
result = 31 * result + headerConditions.hashCode();
result = 31 * result + paramsCondition.hashCode();
result = 31 * result + headersCondition.hashCode();
hash = result;
}
return result;
@ -311,14 +322,9 @@ public final class RequestKey { @@ -311,14 +322,9 @@ public final class RequestKey {
builder.append(',');
builder.append(methods);
}
if (!headerConditions.isEmpty()) {
builder.append(',');
builder.append(headerConditions);
}
if (!paramConditions.isEmpty()) {
builder.append(',');
builder.append(paramConditions);
}
builder.append(",params=").append(paramsCondition.toString());
builder.append(",headers=").append(headersCondition.toString());
builder.append(",consumes=").append(consumesCondition.toString());
builder.append('}');
return builder.toString();
}

5
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java

@ -23,7 +23,6 @@ import java.util.Iterator; @@ -23,7 +23,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.AnnotationUtils;
@ -253,11 +252,11 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap @@ -253,11 +252,11 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap
if (result != 0) {
return result;
}
result = otherKey.getParams().size() - key.getParams().size();
result = otherKey.getParams().weight() - key.getParams().weight();
if (result != 0) {
return result;
}
result = otherKey.getHeaders().size() - key.getHeaders().size();
result = otherKey.getHeaders().weight() - key.getHeaders().weight();
if (result != 0) {
return result;
}

100
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
/*
* 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;
/**
* 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()}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @since 3.1
*/
abstract class AbstractNameValueCondition<T> implements RequestCondition {
protected final String name;
protected final T value;
protected final boolean isNegated;
AbstractNameValueCondition(String expression) {
int separator = expression.indexOf('=');
if (separator == -1) {
this.isNegated = expression.startsWith("!");
this.name = isNegated ? expression.substring(1) : expression;
this.value = null;
}
else {
this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!');
this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator);
this.value = parseValue(expression.substring(separator + 1));
}
}
protected abstract T parseValue(String valueExpression);
public final boolean match(HttpServletRequest request) {
boolean isMatch;
if (this.value != null) {
isMatch = matchValue(request);
}
else {
isMatch = matchName(request);
}
return isNegated ? !isMatch : isMatch;
}
protected abstract boolean matchName(HttpServletRequest request);
protected abstract boolean matchValue(HttpServletRequest request);
public int weight() {
return 1;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (isNegated ? 1 : 0);
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (value != null) {
builder.append(name);
if (isNegated) {
builder.append('!');
}
builder.append('=');
builder.append(value);
}
else {
if (isNegated) {
builder.append('!');
}
builder.append(name);
}
return builder.toString();
}
}

47
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
/*
* 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.util.StringUtils;
/**
* @author Arjen Poutsma
*/
class ConsumesRequestCondition implements RequestCondition {
private final MediaType mediaType;
ConsumesRequestCondition(String mediaType) {
this.mediaType = MediaType.parseMediaType(mediaType);
}
public boolean match(HttpServletRequest request) {
String contentTypeString = request.getContentType();
if (StringUtils.hasLength(contentTypeString)) {
MediaType contentType = MediaType.parseMediaType(contentTypeString);
return this.mediaType.includes(contentType);
}
return false;
}
public int weight() {
return 1;
}
}

64
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
/*
* 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;
/**
* Request header name-value condition.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @see org.springframework.web.bind.annotation.RequestMapping#headers()
* @since 3.1
*/
class HeaderRequestCondition extends AbstractNameValueCondition<String> {
public HeaderRequestCondition(String expression) {
super(expression);
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpServletRequest request) {
return request.getHeader(name) != null;
}
@Override
protected boolean matchValue(HttpServletRequest request) {
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;
}
}

48
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
/*
* 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.List;
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(List<RequestCondition> conditions) {
super(conditions);
}
public boolean match(HttpServletRequest request) {
for (RequestCondition condition : conditions) {
if (!condition.match(request)) {
return false;
}
}
return true;
}
@Override
protected String getToStringInfix() {
return " && ";
}
}

31
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestCondition.java → org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java

@ -14,16 +14,35 @@ @@ -14,16 +14,35 @@
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
package org.springframework.web.servlet.mvc.method.condition;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
* A condition that can be matched to a ServletRequest.
*
* @author Rossen Stoyanchev
* {@link RequestCondition} implementation that represents a logical OR (i.e. ||).
*
* @author Arjen Poutsma
* @since 3.1
*/
interface RequestCondition {
class LogicalDisjunctionRequestCondition extends RequestConditionComposite {
LogicalDisjunctionRequestCondition(List<RequestCondition> conditions) {
super(conditions);
}
public boolean match(HttpServletRequest request) {
for (RequestCondition condition : conditions) {
if (condition.match(request)) {
return true;
}
}
return false;
}
@Override
protected String getToStringInfix() {
return " || ";
}
boolean match(HttpServletRequest request);
}

73
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
/*
* 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.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
/**
* A RequestCondition that for headers that contain {@link org.springframework.http.MediaType MediaTypes}.
*/
class MediaTypeHeaderRequestCondition extends AbstractNameValueCondition<List<MediaType>> {
public MediaTypeHeaderRequestCondition(String expression) {
super(expression);
}
@Override
protected List<MediaType> parseValue(String valueExpression) {
return Collections.unmodifiableList(MediaType.parseMediaTypes(valueExpression));
}
@Override
protected boolean matchName(HttpServletRequest request) {
return request.getHeader(name) != null;
}
@Override
protected boolean matchValue(HttpServletRequest request) {
List<MediaType> requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(name));
for (MediaType mediaType : this.value) {
for (MediaType requestMediaType : requestMediaTypes) {
if (mediaType.includes(requestMediaType)) {
return true;
}
}
}
return false;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof MediaTypeHeaderRequestCondition) {
MediaTypeHeaderRequestCondition other = (MediaTypeHeaderRequestCondition) obj;
return ((this.name.equalsIgnoreCase(other.name)) &&
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
this.isNegated == other.isNegated);
}
return false;
}
}

66
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
/*
* 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.web.util.WebUtils;
/**
* Request parameter name-value condition.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @see org.springframework.web.bind.annotation.RequestMapping#params()
* @since 3.1
*/
class ParamRequestCondition extends AbstractNameValueCondition<String> {
ParamRequestCondition(String expression) {
super(expression);
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpServletRequest request) {
return WebUtils.hasSubmitParameter(request, name);
}
@Override
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;
}
}

49
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* 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;
/**
* Defines the contract for conditions that must be met before an incoming request matches a {@link
* org.springframework.web.servlet.mvc.method.annotation.RequestKey RequestKey}.
*
* <p>Implementations of this interface are created by the {@link RequestConditionFactory}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @see RequestConditionFactory
* @since 3.1
*/
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
*/
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();
}

77
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
/*
* 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.Iterator;
import java.util.List;
/**
* Abstract base class for {@link RequestCondition} implementations that wrap other request conditions.
*
* @author Arjen Poutsma
* @since 3.1
*/
abstract class RequestConditionComposite implements RequestCondition {
protected final List<RequestCondition> conditions;
public RequestConditionComposite(List<RequestCondition> conditions) {
this.conditions = conditions;
}
public int weight() {
int size = 0;
for (RequestCondition condition : conditions) {
size += condition.weight();
}
return size;
}
@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);
}
return false;
}
@Override
public int hashCode() {
return conditions.hashCode();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[");
String infix = getToStringInfix();
for (Iterator<RequestCondition> iterator = conditions.iterator(); iterator.hasNext();) {
RequestCondition condition = iterator.next();
builder.append(condition.toString());
if (iterator.hasNext()) {
builder.append(infix);
}
}
builder.append("]");
return builder.toString();
}
protected abstract String getToStringInfix();
}

175
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java

@ -0,0 +1,175 @@ @@ -0,0 +1,175 @@
/*
* 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.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.ObjectUtils;
/**
* Factory for {@link RequestCondition} objects.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/
public abstract class RequestConditionFactory {
private static final RequestCondition TRUE_CONDITION = new RequestCondition() {
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() {
public boolean match(HttpServletRequest request) {
return false;
}
public int weight() {
return 0;
}
@Override
public String toString() {
return "FALSE";
}
};
public static RequestCondition trueCondition() {
return TRUE_CONDITION;
}
public static RequestCondition falseCondition() {
return FALSE_CONDITION;
}
/**
* Combines the given conditions into a logical AND, i.e. the returned condition will return {@code true} for {@link
* RequestCondition#match(HttpServletRequest)} if all of the given conditions do so.
*
* @param conditions the conditions
* @return a condition that represents a logical AND
*/
public static RequestCondition and(RequestCondition... conditions) {
List<RequestCondition> filteredConditions = new ArrayList<RequestCondition>(Arrays.asList(conditions));
for (Iterator<RequestCondition> iterator = filteredConditions.iterator(); iterator.hasNext();) {
RequestCondition condition = iterator.next();
if (condition == TRUE_CONDITION) {
iterator.remove();
}
}
return new LogicalConjunctionRequestCondition(filteredConditions);
}
/**
* Combines the given conditions into a logical OR, i.e. the returned condition will return {@code true} for {@link
* RequestCondition#match(HttpServletRequest)} if any of the given conditions do so.
*
* @param conditions the conditions
* @return a condition that represents a logical OR
*/
public static RequestCondition or(RequestCondition... conditions) {
List<RequestCondition> filteredConditions = new ArrayList<RequestCondition>(Arrays.asList(conditions));
for (Iterator<RequestCondition> iterator = filteredConditions.iterator(); iterator.hasNext();) {
RequestCondition condition = iterator.next();
if (condition == TRUE_CONDITION) {
return trueCondition();
}
else if (condition == FALSE_CONDITION) {
iterator.remove();
}
}
return new LogicalDisjunctionRequestCondition(filteredConditions);
}
/**
* Parses the given parameters, and returns them as a single request conditions.
*
* @param params the parameters
* @return the request condition
* @see org.springframework.web.bind.annotation.RequestMapping#params()
*/
public static RequestCondition parseParams(String... params) {
if (ObjectUtils.isEmpty(params)) {
return TRUE_CONDITION;
}
RequestCondition[] result = new RequestCondition[params.length];
for (int i = 0; i < params.length; i++) {
result[i] = new ParamRequestCondition(params[i]);
}
return and(result);
}
/**
* 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 RequestCondition parseHeaders(String... headers) {
if (ObjectUtils.isEmpty(headers)) {
return TRUE_CONDITION;
}
RequestCondition[] result = new RequestCondition[headers.length];
for (int i = 0; i < headers.length; i++) {
HeaderRequestCondition header = new HeaderRequestCondition(headers[i]);
if (isMediaTypeHeader(header.name)) {
result[i] = new MediaTypeHeaderRequestCondition(headers[i]);
}
else {
result[i] = header;
}
}
return and(result);
}
private static boolean isMediaTypeHeader(String name) {
return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name);
}
public static RequestCondition parseConsumes(String... consumes) {
if (ObjectUtils.isEmpty(consumes)) {
return TRUE_CONDITION;
}
RequestCondition[] result = new RequestCondition[consumes.length];
for (int i = 0; i < consumes.length; i++) {
result[i] = new ConsumesRequestCondition(consumes[i]);
}
return or(result);
}
//
// Conditions
//
}

155
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestConditionFactoryTests.java

@ -1,155 +0,0 @@ @@ -1,155 +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.annotation;
import java.util.Set;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestConditionFactory;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
*/
public class RequestConditionFactoryTests {
@Test
public void paramEquals() {
assertEquals(getSingleParamCondition("foo"), getSingleParamCondition("foo"));
assertFalse(getSingleParamCondition("foo").equals(getSingleParamCondition("bar")));
assertFalse(getSingleParamCondition("foo").equals(getSingleParamCondition("FOO")));
assertEquals(getSingleParamCondition("foo=bar"), getSingleParamCondition("foo=bar"));
assertFalse(getSingleParamCondition("foo=bar").equals(getSingleParamCondition("FOO=bar")));
}
@Test
public void headerEquals() {
assertEquals(getSingleHeaderCondition("foo"), getSingleHeaderCondition("foo"));
assertEquals(getSingleHeaderCondition("foo"), getSingleHeaderCondition("FOO"));
assertFalse(getSingleHeaderCondition("foo").equals(getSingleHeaderCondition("bar")));
assertEquals(getSingleHeaderCondition("foo=bar"), getSingleHeaderCondition("foo=bar"));
assertEquals(getSingleHeaderCondition("foo=bar"), getSingleHeaderCondition("FOO=bar"));
assertEquals(getSingleHeaderCondition("content-type=text/xml"),
getSingleHeaderCondition("Content-Type=TEXT/XML"));
}
@Test
public void headerPresent() {
RequestCondition condition = getSingleHeaderCondition("accept");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "");
assertTrue(condition.match(request));
}
@Test
public void headerPresentNoMatch() {
RequestCondition condition = getSingleHeaderCondition("foo");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("bar", "");
assertFalse(condition.match(request));
}
@Test
public void headerNotPresent() {
RequestCondition condition = getSingleHeaderCondition("!accept");
MockHttpServletRequest request = new MockHttpServletRequest();
assertTrue(condition.match(request));
}
@Test
public void headerValueMatch() {
RequestCondition condition = getSingleHeaderCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
assertTrue(condition.match(request));
}
@Test
public void headerValueNoMatch() {
RequestCondition condition = getSingleHeaderCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bazz");
assertFalse(condition.match(request));
}
@Test
public void headerCaseSensitiveValueMatch() {
RequestCondition condition = getSingleHeaderCondition("foo=Bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
assertFalse(condition.match(request));
}
@Test
public void headerValueMatchNegated() {
RequestCondition condition = getSingleHeaderCondition("foo!=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "baz");
assertTrue(condition.match(request));
}
@Test
public void mediaTypeHeaderValueMatch() {
RequestCondition condition = getSingleHeaderCondition("accept=text/html");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "text/html");
assertTrue(condition.match(request));
}
@Test
public void mediaTypeHeaderValueMatchNegated() {
RequestCondition condition = getSingleHeaderCondition("accept!=text/html");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "application/html");
assertTrue(condition.match(request));
}
private RequestCondition getSingleHeaderCondition(String expression) {
Set<RequestCondition> conditions = RequestConditionFactory.parseHeaders(expression);
assertEquals(1, conditions.size());
return conditions.iterator().next();
}
private RequestCondition getSingleParamCondition(String expression) {
Set<RequestCondition> conditions = RequestConditionFactory.parseParams(expression);
assertEquals(1, conditions.size());
return conditions.iterator().next();
}
}

41
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java

@ -16,11 +16,6 @@ @@ -16,11 +16,6 @@
package org.springframework.web.servlet.mvc.method.annotation;
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.util.Collections;
import java.util.Comparator;
import java.util.List;
@ -28,11 +23,13 @@ import java.util.List; @@ -28,11 +23,13 @@ import java.util.List;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestConditionFactory;
import org.springframework.web.servlet.mvc.method.annotation.RequestKey;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
import static java.util.Arrays.*;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
@ -54,8 +51,8 @@ public class RequestKeyComparatorTests { @@ -54,8 +51,8 @@ public class RequestKeyComparatorTests {
public void moreSpecificPatternWins() {
request.setRequestURI("/foo");
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
RequestKey key1 = new RequestKey(asList("/fo*"), null, null, null);
RequestKey key2 = new RequestKey(asList("/foo"), null, null, null);
RequestKey key1 = new RequestKey(asList("/fo*"), null);
RequestKey key2 = new RequestKey(asList("/foo"), null);
assertEquals(1, comparator.compare(key1, key2));
}
@ -64,8 +61,8 @@ public class RequestKeyComparatorTests { @@ -64,8 +61,8 @@ public class RequestKeyComparatorTests {
public void equalPatterns() {
request.setRequestURI("/foo");
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
RequestKey key1 = new RequestKey(asList("/foo*"), null, null, null);
RequestKey key2 = new RequestKey(asList("/foo*"), null, null, null);
RequestKey key1 = new RequestKey(asList("/foo*"), null);
RequestKey key2 = new RequestKey(asList("/foo*"), null);
assertEquals(0, comparator.compare(key1, key2));
}
@ -73,8 +70,8 @@ public class RequestKeyComparatorTests { @@ -73,8 +70,8 @@ public class RequestKeyComparatorTests {
@Test
public void greaterNumberOfMatchingPatternsWins() throws Exception {
request.setRequestURI("/foo.html");
RequestKey key1 = new RequestKey(asList("/foo", "*.jpeg"), null, null, null);
RequestKey key2 = new RequestKey(asList("/foo", "*.html"), null, null, null);
RequestKey key1 = new RequestKey(asList("/foo", "*.jpeg"), null);
RequestKey key2 = new RequestKey(asList("/foo", "*.html"), null);
RequestKey match1 = handlerMapping.getMatchingKey(key1, request);
RequestKey match2 = handlerMapping.getMatchingKey(key2, request);
List<RequestKey> matches = asList(match1, match2);
@ -86,18 +83,18 @@ public class RequestKeyComparatorTests { @@ -86,18 +83,18 @@ public class RequestKeyComparatorTests {
@Test
public void oneMethodWinsOverNone() {
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
RequestKey key1 = new RequestKey(null, null, null, null);
RequestKey key2 = new RequestKey(null, asList(RequestMethod.GET), null, null);
RequestKey key1 = new RequestKey(null, null);
RequestKey key2 = new RequestKey(null, asList(RequestMethod.GET));
assertEquals(1, comparator.compare(key1, key2));
}
@Test
public void methodsAndParams() {
RequestKey empty = new RequestKey(null, null, null, null);
RequestKey oneMethod = new RequestKey(null, asList(RequestMethod.GET), null, null);
RequestKey empty = new RequestKey(null, null);
RequestKey oneMethod = new RequestKey(null, asList(RequestMethod.GET));
RequestKey oneMethodOneParam =
new RequestKey(null, asList(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null);
new RequestKey(null, asList(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null);
List<RequestKey> list = asList(empty, oneMethod, oneMethodOneParam);
Collections.shuffle(list);
Collections.sort(list, handlerMapping.getKeyComparator(request));
@ -110,9 +107,9 @@ public class RequestKeyComparatorTests { @@ -110,9 +107,9 @@ public class RequestKeyComparatorTests {
@Test
@Ignore // TODO : remove ignore
public void acceptHeaders() {
RequestKey html = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html"));
RequestKey xml = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml"));
RequestKey none = new RequestKey(null, null, null, null);
RequestKey html = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html"), null);
RequestKey xml = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml"), null);
RequestKey none = new RequestKey(null, null);
request.addHeader("Accept", "application/xml, text/html");
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);

78
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java

@ -16,22 +16,20 @@ @@ -16,22 +16,20 @@
package org.springframework.web.servlet.mvc.method.annotation;
import static java.util.Arrays.asList;
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.annotation.RequestConditionFactory;
import org.springframework.web.servlet.mvc.method.annotation.RequestKey;
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.*;
/**
* @author Arjen Poutsma
* @author Rossen Stoyanchev
@ -40,8 +38,8 @@ public class RequestKeyTests { @@ -40,8 +38,8 @@ public class RequestKeyTests {
@Test
public void equals() {
RequestKey key1 = new RequestKey(asList("/foo"), asList(GET), null, null);
RequestKey key2 = new RequestKey(asList("/foo"), asList(GET), null, null);
RequestKey key1 = new RequestKey(singleton("/foo"), singleton(GET));
RequestKey key2 = new RequestKey(singleton("/foo"), singleton(GET));
assertEquals(key1, key2);
assertEquals(key1.hashCode(), key2.hashCode());
@ -49,8 +47,8 @@ public class RequestKeyTests { @@ -49,8 +47,8 @@ public class RequestKeyTests {
@Test
public void equalsPrependSlash() {
RequestKey key1 = new RequestKey(asList("/foo"), asList(GET), null, null);
RequestKey key2 = new RequestKey(asList("foo"), asList(GET), null, null);
RequestKey key1 = new RequestKey(singleton("/foo"), singleton(GET));
RequestKey key2 = new RequestKey(singleton("foo"), singleton(GET));
assertEquals(key1, key2);
assertEquals(key1.hashCode(), key2.hashCode());
@ -63,22 +61,22 @@ public class RequestKeyTests { @@ -63,22 +61,22 @@ public class RequestKeyTests {
RequestKey key1 = createKeyFromPatterns("/t1", "/t2");
RequestKey key2 = createKeyFromPatterns("/m1", "/m2");
RequestKey key3 = createKeyFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2");
assertEquals(key3, key1.combine(key2, pathMatcher));
assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
key1 = createKeyFromPatterns("/t1");
key2 = createKeyFromPatterns(new String[] {});
key2 = createKeyFromPatterns();
key3 = createKeyFromPatterns("/t1");
assertEquals(key3, key1.combine(key2, pathMatcher));
assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
key1 = createKeyFromPatterns(new String[] {});
key1 = createKeyFromPatterns();
key2 = createKeyFromPatterns("/m1");
key3 = createKeyFromPatterns("/m1");
assertEquals(key3, key1.combine(key2, pathMatcher));
assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
key1 = createKeyFromPatterns(new String[] {});
key2 = createKeyFromPatterns(new String[] {});
key1 = createKeyFromPatterns();
key2 = createKeyFromPatterns();
key3 = createKeyFromPatterns("/");
assertEquals(key3, key1.combine(key2, pathMatcher));
assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
}
@ -88,39 +86,39 @@ public class RequestKeyTests { @@ -88,39 +86,39 @@ public class RequestKeyTests {
PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
RequestKey key = new RequestKey(asList("/foo"), null, null, null);
RequestKey key = new RequestKey(singleton("/foo"), null);
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull(match);
request = new MockHttpServletRequest("GET", "/foo/bar");
key = new RequestKey(asList("/foo/*"), null, null, null);
key = new RequestKey(singleton("/foo/*"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull("Pattern match", match);
request = new MockHttpServletRequest("GET", "/foo.html");
key = new RequestKey(asList("/foo"), null, null, null);
key = new RequestKey(singleton("/foo"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull("Implicit match by extension", match);
assertEquals("Contains matched pattern", "/foo.*", match.getPatterns().iterator().next());
request = new MockHttpServletRequest("GET", "/foo/");
key = new RequestKey(asList("/foo"), null, null, null);
key = new RequestKey(singleton("/foo"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull("Implicit match by trailing slash", match);
assertEquals("Contains matched pattern", "/foo/", match.getPatterns().iterator().next());
request = new MockHttpServletRequest("GET", "/foo.html");
key = new RequestKey(asList("/foo.jpg"), null, null, null);
key = new RequestKey(singleton("/foo.jpg"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNull("Implicit match ignored if pattern has extension", match);
request = new MockHttpServletRequest("GET", "/foo.html");
key = new RequestKey(asList("/foo.jpg"), null, null, null);
key = new RequestKey(singleton("/foo.jpg"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNull("Implicit match ignored on pattern with trailing slash", match);
@ -132,17 +130,17 @@ public class RequestKeyTests { @@ -132,17 +130,17 @@ public class RequestKeyTests {
PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
RequestKey key = new RequestKey(asList("/foo"), null, null, null);
RequestKey key = new RequestKey(singleton("/foo"), null);
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull("No method matches any method", match);
key = new RequestKey(asList("/foo"), asList(GET), null, null);
key = new RequestKey(singleton("/foo"), singleton(GET));
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull("Exact match", match);
key = new RequestKey(asList("/foo"), asList(POST), null, null);
key = new RequestKey(singleton("/foo"), singleton(POST));
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNull("No match", match);
@ -154,15 +152,15 @@ public class RequestKeyTests { @@ -154,15 +152,15 @@ public class RequestKeyTests {
PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
RequestKey key = new RequestKey(asList("/foo*", "/bar"), asList(GET, POST), null, null);
RequestKey key = new RequestKey(asList("/foo*", "/bar"), asList(GET, POST));
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
RequestKey expected = new RequestKey(asList("/foo*"), asList(GET), null, null);
RequestKey expected = new RequestKey(singleton("/foo*"), singleton(GET));
assertEquals("Matching RequestKey contains matched patterns and methods only", expected, match);
key = new RequestKey(asList("/**", "/foo*", "/foo"), null, null, null);
key = new RequestKey(asList("/**", "/foo*", "/foo"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
expected = new RequestKey(asList("/foo", "/foo*", "/**"), null, null, null);
expected = new RequestKey(asList("/foo", "/foo*", "/**"), null);
assertEquals("Matched patterns are sorted with best match at the top", expected, match);
@ -175,12 +173,12 @@ public class RequestKeyTests { @@ -175,12 +173,12 @@ public class RequestKeyTests {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setParameter("foo", "bar");
RequestKey key = new RequestKey(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null);
RequestKey key = new RequestKey(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null, null);
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull(match);
key = new RequestKey(asList("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null);
key = new RequestKey(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null, null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNull(match);
@ -193,12 +191,12 @@ public class RequestKeyTests { @@ -193,12 +191,12 @@ public class RequestKeyTests {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.addHeader("foo", "bar");
RequestKey key = new RequestKey(asList("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"));
RequestKey key = new RequestKey(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"), null);
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNotNull(match);
key = new RequestKey(asList("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"));
key = new RequestKey(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"), null);
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
assertNull(match);
@ -208,11 +206,11 @@ public class RequestKeyTests { @@ -208,11 +206,11 @@ public class RequestKeyTests {
public void testCreateFromServletRequest() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
RequestKey key = RequestKey.createFromServletRequest(request, new UrlPathHelper());
assertEquals(new RequestKey(asList("/foo"), asList(RequestMethod.GET), null, null), key);
assertEquals(new RequestKey(singleton("/foo"), singleton(RequestMethod.GET), null, null, null), key);
}
private RequestKey createKeyFromPatterns(String... patterns) {
return new RequestKey(asList(patterns), null, null, null);
return new RequestKey(asList(patterns), null);
}
}

15
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMappingTests.java

@ -16,20 +16,13 @@ @@ -16,20 +16,13 @@
package org.springframework.web.servlet.mvc.method.annotation;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.stereotype.Controller;
@ -42,8 +35,8 @@ import org.springframework.web.servlet.HandlerInterceptor; @@ -42,8 +35,8 @@ 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.annotation.RequestKey;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
@ -96,7 +89,7 @@ public class RequestMappingHandlerMethodMappingTests { @@ -96,7 +89,7 @@ public class RequestMappingHandlerMethodMappingTests {
@Test
public void uriTemplateVariables() {
RequestKey key = new RequestKey(Arrays.asList("/{path1}/{path2}"), null, null, null);
RequestKey key = new RequestKey(Arrays.asList("/{path1}/{path2}"), null);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
mapping.handleMatch(key, request);

212
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java

@ -0,0 +1,212 @@ @@ -0,0 +1,212 @@
/*
* 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 org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
*/
public class RequestConditionFactoryTests {
@Test
public void andMatch() {
RequestCondition condition1 = RequestConditionFactory.trueCondition();
RequestCondition condition2 = RequestConditionFactory.trueCondition();
RequestCondition and = RequestConditionFactory.and(condition1, condition2);
assertTrue(and.match(new MockHttpServletRequest()));
}
@Test
public void andNoMatch() {
RequestCondition condition1 = RequestConditionFactory.trueCondition();
RequestCondition condition2 = RequestConditionFactory.falseCondition();
RequestCondition and = RequestConditionFactory.and(condition1, condition2);
assertFalse(and.match(new MockHttpServletRequest()));
}
@Test
public void orMatch() {
RequestCondition condition1 = RequestConditionFactory.trueCondition();
RequestCondition condition2 = RequestConditionFactory.falseCondition();
RequestCondition and = RequestConditionFactory.or(condition1, condition2);
assertTrue(and.match(new MockHttpServletRequest()));
}
@Test
public void orNoMatch() {
RequestCondition condition1 = RequestConditionFactory.falseCondition();
RequestCondition condition2 = RequestConditionFactory.falseCondition();
RequestCondition and = RequestConditionFactory.and(condition1, condition2);
assertFalse(and.match(new MockHttpServletRequest()));
}
@Test
public void paramEquals() {
assertEquals(RequestConditionFactory.parseParams("foo"), RequestConditionFactory.parseParams("foo"));
assertFalse(RequestConditionFactory.parseParams("foo").equals(RequestConditionFactory.parseParams("bar")));
assertFalse(RequestConditionFactory.parseParams("foo").equals(RequestConditionFactory.parseParams("FOO")));
assertEquals(RequestConditionFactory.parseParams("foo=bar"), RequestConditionFactory.parseParams("foo=bar"));
assertFalse(
RequestConditionFactory.parseParams("foo=bar").equals(RequestConditionFactory.parseParams("FOO=bar")));
}
@Test
public void headerEquals() {
assertEquals(RequestConditionFactory.parseHeaders("foo"), RequestConditionFactory.parseHeaders("foo"));
assertEquals(RequestConditionFactory.parseHeaders("foo"), RequestConditionFactory.parseHeaders("FOO"));
assertFalse(RequestConditionFactory.parseHeaders("foo").equals(RequestConditionFactory.parseHeaders("bar")));
assertEquals(RequestConditionFactory.parseHeaders("foo=bar"), RequestConditionFactory.parseHeaders("foo=bar"));
assertEquals(RequestConditionFactory.parseHeaders("foo=bar"), RequestConditionFactory.parseHeaders("FOO=bar"));
assertEquals(RequestConditionFactory.parseHeaders("content-type=text/xml"),
RequestConditionFactory.parseHeaders("Content-Type=TEXT/XML"));
}
@Test
public void headerPresent() {
RequestCondition condition = RequestConditionFactory.parseHeaders("accept");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "");
assertTrue(condition.match(request));
}
@Test
public void headerPresentNoMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("bar", "");
assertFalse(condition.match(request));
}
@Test
public void headerNotPresent() {
RequestCondition condition = RequestConditionFactory.parseHeaders("!accept");
MockHttpServletRequest request = new MockHttpServletRequest();
assertTrue(condition.match(request));
}
@Test
public void headerValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
assertTrue(condition.match(request));
}
@Test
public void headerValueNoMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bazz");
assertFalse(condition.match(request));
}
@Test
public void headerCaseSensitiveValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=Bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
assertFalse(condition.match(request));
}
@Test
public void headerValueMatchNegated() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo!=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "baz");
assertTrue(condition.match(request));
}
@Test
public void mediaTypeHeaderValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("accept=text/html");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "text/html");
assertTrue(condition.match(request));
}
@Test
public void mediaTypeHeaderValueMatchNegated() {
RequestCondition condition = RequestConditionFactory.parseHeaders("accept!=text/html");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "application/html");
assertTrue(condition.match(request));
}
@Test
public void consumesMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
assertTrue(condition.match(request));
}
@Test
public void consumesWildcardMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/*");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
assertTrue(condition.match(request));
}
@Test
public void consumesMultipleMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain", "application/xml");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
assertTrue(condition.match(request));
}
@Test
public void consumesSingleNoMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("application/xml");
assertFalse(condition.match(request));
}
}

4
org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* 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.
@ -297,4 +297,6 @@ public @interface RequestMapping { @@ -297,4 +297,6 @@ public @interface RequestMapping {
*/
String[] headers() default {};
String[] consumes() default "*/*";
}

Loading…
Cancel
Save