SPR-7812 Add CustomRequestCondition, PatternsRequestCondition, and other condition related tests
This commit is contained in:
@@ -204,16 +204,18 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
|
||||
}
|
||||
|
||||
Set<String> paths = getMappingPaths(mapping);
|
||||
for (String path : paths) {
|
||||
urlMap.add(path, mapping);
|
||||
Set<String> patterns = getMappingPathPatterns(mapping);
|
||||
for (String pattern : patterns) {
|
||||
if (!getPathMatcher().isPattern(pattern)) {
|
||||
urlMap.add(pattern, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL paths associated with the given mapping.
|
||||
*/
|
||||
protected abstract Set<String> getMappingPaths(T mapping);
|
||||
protected abstract Set<String> getMappingPathPatterns(T mapping);
|
||||
|
||||
@Override
|
||||
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
|
||||
@@ -25,10 +25,10 @@ import java.util.Iterator;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
|
||||
public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
|
||||
|
||||
/**
|
||||
* Returns the discrete expressions a request condition is composed of such as URL patterns,
|
||||
* Returns the discrete items a request condition is composed of such as URL patterns,
|
||||
* HTTP request methods, parameter expressions, etc.
|
||||
*/
|
||||
protected abstract Collection<?> getContent();
|
||||
@@ -65,8 +65,8 @@ abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> i
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the individual expressions of the condition are combined via logical
|
||||
* conjunction (" && "); or {@code false} otherwise.
|
||||
* The notation to use when printing discrete items of content in the toString() method.
|
||||
* For example URL patterns use " || " while parameter expressions use " && ".
|
||||
*/
|
||||
protected abstract String getToStringInfix();
|
||||
|
||||
|
||||
@@ -14,42 +14,44 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.condition;
|
||||
package org.springframework.web.servlet.mvc.method;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.RequestCondition;
|
||||
|
||||
/**
|
||||
* Wraps and delegates operations to a custom {@link RequestCondition} whose type is not known and even its
|
||||
* presence is not guaranteed ahead of time. The main purpose of this class is to ensure a type-safe and
|
||||
* null-safe way of combining and comparing custom request conditions.
|
||||
* Wraps and delegates operations to a {@link RequestCondition} whose type is not known ahead of time. The main goal
|
||||
* is to provide type-safe and null-safe way of comparing and combining optional custom {@link RequestCondition}s.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class CustomRequestCondition extends AbstractRequestCondition<CustomRequestCondition> {
|
||||
final class CustomRequestCondition extends AbstractRequestCondition<CustomRequestCondition> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final RequestCondition customCondition;
|
||||
|
||||
/**
|
||||
* Creates a {@link CustomRequestCondition} that wraps the given {@link RequestCondition} instance.
|
||||
* @param requestCondition the custom request condition to wrap
|
||||
* Creates an instance that wraps the given custom request condition.
|
||||
* @param requestCondition the custom request condition
|
||||
*/
|
||||
public CustomRequestCondition(RequestCondition<?> requestCondition) {
|
||||
CustomRequestCondition(RequestCondition<?> requestCondition) {
|
||||
this.customCondition = requestCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty {@link CustomRequestCondition}.
|
||||
* Creates an instance that does not wrap any custom request condition.
|
||||
*/
|
||||
public CustomRequestCondition() {
|
||||
CustomRequestCondition() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public RequestCondition<?> getRequestCondition() {
|
||||
public RequestCondition<?> getCondition() {
|
||||
return customCondition;
|
||||
}
|
||||
|
||||
@@ -60,7 +62,7 @@ public final class CustomRequestCondition extends AbstractRequestCondition<Custo
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return "";
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,9 +18,7 @@ package org.springframework.web.servlet.mvc.method;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.CustomRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
|
||||
@@ -39,8 +37,7 @@ import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondit
|
||||
* <li>{@link ProducesRequestCondition}</li>
|
||||
* </ul>
|
||||
*
|
||||
* Optionally a custom request condition may also be provided by wrapping it in an instance
|
||||
* of {@link CustomRequestCondition}.
|
||||
* Optionally a custom request condition may also be provided.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -60,95 +57,82 @@ public final class RequestMappingInfo {
|
||||
|
||||
private final ProducesRequestCondition producesCondition;
|
||||
|
||||
private CustomRequestCondition customCondition = new CustomRequestCondition();
|
||||
private final CustomRequestCondition customCondition;
|
||||
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestMappingInfo} instance.
|
||||
*/
|
||||
public RequestMappingInfo(PatternsRequestCondition patterns,
|
||||
RequestMethodsRequestCondition methods,
|
||||
ParamsRequestCondition params,
|
||||
HeadersRequestCondition headers,
|
||||
ConsumesRequestCondition consumes,
|
||||
ProducesRequestCondition produces) {
|
||||
this(patterns, methods, params, headers, consumes, produces, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestMappingInfo} instance also providing a custom {@link RequestCondition}.
|
||||
*/
|
||||
public RequestMappingInfo(PatternsRequestCondition patterns,
|
||||
RequestMethodsRequestCondition methods,
|
||||
ParamsRequestCondition params,
|
||||
HeadersRequestCondition headers,
|
||||
ConsumesRequestCondition consumes,
|
||||
ProducesRequestCondition produces,
|
||||
CustomRequestCondition custom) {
|
||||
RequestCondition<?> custom) {
|
||||
this.patternsCondition = patterns != null ? patterns : new PatternsRequestCondition();
|
||||
this.methodsCondition = methods != null ? methods : new RequestMethodsRequestCondition();
|
||||
this.paramsCondition = params != null ? params : new ParamsRequestCondition();
|
||||
this.headersCondition = headers != null ? headers : new HeadersRequestCondition();
|
||||
this.consumesCondition = consumes != null ? consumes : new ConsumesRequestCondition();
|
||||
this.producesCondition = produces != null ? produces : new ProducesRequestCondition();
|
||||
this.customCondition = custom != null ? custom : new CustomRequestCondition();
|
||||
this.customCondition = custom != null ? new CustomRequestCondition(custom) : new CustomRequestCondition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Package protected constructor for tests.
|
||||
*/
|
||||
RequestMappingInfo(String[] patterns, RequestMethod... methods) {
|
||||
this(new PatternsRequestCondition(patterns), new RequestMethodsRequestCondition(methods), null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL patterns of this request mapping info.
|
||||
* Returns the URL patterns of this {@link RequestMappingInfo};
|
||||
* or instance with 0 patterns, never {@code null}
|
||||
*/
|
||||
public PatternsRequestCondition getPatternsCondition() {
|
||||
return patternsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP request methods of this {@link RequestMappingInfo}.
|
||||
* Returns the HTTP request methods of this {@link RequestMappingInfo};
|
||||
* or instance with 0 request methods, never {@code null}
|
||||
*/
|
||||
public RequestMethodsRequestCondition getMethodsCondition() {
|
||||
return methodsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "parameters" condition of this {@link RequestMappingInfo}.
|
||||
* Returns the "parameters" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 parameter expressions, never {@code null}
|
||||
*/
|
||||
public ParamsRequestCondition getParamsCondition() {
|
||||
return paramsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "headers" condition of this {@link RequestMappingInfo}.
|
||||
* Returns the "headers" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 header expressions, never {@code null}
|
||||
*/
|
||||
public HeadersRequestCondition getHeadersCondition() {
|
||||
return headersCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "consumes" condition of this {@link RequestMappingInfo}.
|
||||
* Returns the "consumes" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 consumes expressions, never {@code null}
|
||||
*/
|
||||
public ConsumesRequestCondition getConsumesCondition() {
|
||||
return consumesCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "produces" condition of this {@link RequestMappingInfo}.
|
||||
* Returns the "produces" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 produces expressions, never {@code null}
|
||||
*/
|
||||
public ProducesRequestCondition getProducesCondition() {
|
||||
return producesCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom request condition.
|
||||
* Returns the "custom" condition of this {@link RequestMappingInfo}; or {@code null}
|
||||
*/
|
||||
public void setCustomCondition(CustomRequestCondition customCondition) {
|
||||
this.customCondition = customCondition;
|
||||
public RequestCondition<?> getCustomCondition() {
|
||||
return customCondition.getCondition();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +149,7 @@ public final class RequestMappingInfo {
|
||||
ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
|
||||
CustomRequestCondition custom = this.customCondition.combine(other.customCondition);
|
||||
|
||||
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom);
|
||||
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +159,7 @@ public final class RequestMappingInfo {
|
||||
* the current request, sorted with best matching patterns on top.
|
||||
* @return a new instance in case all conditions match; or {@code null} otherwise
|
||||
*/
|
||||
public RequestMappingInfo getMatchingRequestMappingInfo(HttpServletRequest request) {
|
||||
public RequestMappingInfo getMatchingInfo(HttpServletRequest request) {
|
||||
RequestMethodsRequestCondition methods = methodsCondition.getMatchingCondition(request);
|
||||
ParamsRequestCondition params = paramsCondition.getMatchingCondition(request);
|
||||
HeadersRequestCondition headers = headersCondition.getMatchingCondition(request);
|
||||
@@ -196,13 +180,13 @@ public final class RequestMappingInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom);
|
||||
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares "this" info (i.e. the current instance) with another info in the context of a request.
|
||||
* <p>Note: it is assumed both instances have been obtained via
|
||||
* {@link #getMatchingRequestMappingInfo(HttpServletRequest)} to ensure they have conditions with
|
||||
* {@link #getMatchingInfo(HttpServletRequest)} to ensure they have conditions with
|
||||
* content relevant to current request.
|
||||
*/
|
||||
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
|
||||
|
||||
@@ -48,27 +48,21 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
@Override
|
||||
protected void handlerMethodsInitialized(Map<RequestMappingInfo, HandlerMethod> handlerMethods) {
|
||||
List<RequestMappingInfo> mappings = new ArrayList<RequestMappingInfo>(handlerMethods.keySet());
|
||||
while (mappings.size() > 1) {
|
||||
RequestMappingInfo mapping = mappings.remove(0);
|
||||
for (RequestMappingInfo otherMapping : mappings) {
|
||||
// TODO: further validate mapping conditions
|
||||
List<RequestMappingInfo> infos = new ArrayList<RequestMappingInfo>(handlerMethods.keySet());
|
||||
while (infos.size() > 1) {
|
||||
RequestMappingInfo info1 = infos.remove(0);
|
||||
for (RequestMappingInfo info2 : infos) {
|
||||
// TODO: validate duplicate consumable and producible media types
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL paths associated with this {@link RequestMappingInfo}.
|
||||
* Get the URL path patterns associated with this {@link RequestMappingInfo}.
|
||||
*/
|
||||
@Override
|
||||
protected Set<String> getMappingPaths(RequestMappingInfo mapping) {
|
||||
Set<String> paths = new HashSet<String>();
|
||||
for (String pattern : mapping.getPatternsCondition().getPatterns()) {
|
||||
if (!getPathMatcher().isPattern(pattern)) {
|
||||
paths.add(pattern);
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
|
||||
return info.getPatternsCondition().getPatterns();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,8 +73,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
* @returns a RequestMappingInfo instance in case of a match; or {@code null} in case of no match.
|
||||
*/
|
||||
@Override
|
||||
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping, HttpServletRequest request) {
|
||||
return mapping.getMatchingRequestMappingInfo(request);
|
||||
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
|
||||
return info.getMatchingInfo(request);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,8 +83,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
@Override
|
||||
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
|
||||
return new Comparator<RequestMappingInfo>() {
|
||||
public int compare(RequestMappingInfo info, RequestMappingInfo otherInfo) {
|
||||
return info.compareTo(otherInfo, request);
|
||||
public int compare(RequestMappingInfo info1, RequestMappingInfo info2) {
|
||||
return info1.compareTo(info2, request);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
new ParamsRequestCondition(annotation.params()),
|
||||
new HeadersRequestCondition(annotation.headers()),
|
||||
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
|
||||
new ProducesRequestCondition(annotation.produces(), annotation.headers()));
|
||||
new ProducesRequestCondition(annotation.produces(), annotation.headers()), null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user