Improved annnotation support in ResolvableMethod

This commit is contained in:
Rossen Stoyanchev
2017-03-06 09:18:22 -05:00
parent 0296d003af
commit 37726f4214
11 changed files with 542 additions and 307 deletions

View File

@@ -0,0 +1,308 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.method;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Predicate;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.ValueConstants;
/**
* Predicates for {@code @MVC} annotations.
*
* @author Rossen Stoyanchev
* @since 5.0
*
* @see ResolvableMethod#annot(Predicate[])
* @see ResolvableMethod.Builder#annot(Predicate[])
*/
public class MvcAnnotationPredicates {
// Method parameter predicates
public static ModelAttributePredicate modelAttribute() {
return new ModelAttributePredicate();
}
public static RequestBodyPredicate requestBody() {
return new RequestBodyPredicate();
}
public static RequestParamPredicate requestParam() {
return new RequestParamPredicate();
}
public static RequestPartPredicate requestPart() {
return new RequestPartPredicate();
}
// Method predicates
public static ModelAttributeMethodPredicate modelMethod() {
return new ModelAttributeMethodPredicate();
}
public static ResponseStatusPredicate responseStatus() {
return new ResponseStatusPredicate();
}
public static ResponseStatusPredicate responseStatus(HttpStatus code) {
return new ResponseStatusPredicate(code);
}
public static RequestMappingPredicate requestMapping(String... path) {
return new RequestMappingPredicate(path);
}
public static RequestMappingPredicate getMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.GET);
}
public static RequestMappingPredicate postMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.POST);
}
public static RequestMappingPredicate putMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.PUT);
}
public static RequestMappingPredicate deleteMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.DELETE);
}
public static RequestMappingPredicate optionsMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.OPTIONS);
}
public static RequestMappingPredicate headMapping(String... path) {
return new RequestMappingPredicate(path).method(RequestMethod.HEAD);
}
public static class ModelAttributePredicate implements Predicate<MethodParameter> {
private String name;
private boolean binding = true;
public ModelAttributePredicate name(String name) {
this.name = name;
return this;
}
public ModelAttributePredicate noName() {
this.name = "";
return this;
}
public ModelAttributePredicate noBinding() {
this.binding = false;
return this;
}
@Override
public boolean test(MethodParameter parameter) {
ModelAttribute annotation = parameter.getParameterAnnotation(ModelAttribute.class);
return annotation != null &&
(this.name == null || annotation.name().equals(this.name)) &&
annotation.binding() == this.binding;
}
}
public static class RequestBodyPredicate implements Predicate<MethodParameter> {
private boolean required = true;
public RequestBodyPredicate notRequired() {
this.required = false;
return this;
}
@Override
public boolean test(MethodParameter parameter) {
RequestBody annotation = parameter.getParameterAnnotation(RequestBody.class);
return annotation != null && annotation.required() == this.required;
}
}
public static class RequestParamPredicate implements Predicate<MethodParameter> {
private String name;
private boolean required = true;
private String defaultValue = ValueConstants.DEFAULT_NONE;
public RequestParamPredicate name(String name) {
this.name = name;
return this;
}
public RequestParamPredicate noName() {
this.name = "";
return this;
}
public RequestParamPredicate notRequired() {
this.required = false;
return this;
}
public RequestParamPredicate notRequired(String defaultValue) {
this.defaultValue = defaultValue;
return this;
}
@Override
public boolean test(MethodParameter parameter) {
RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class);
return annotation != null &&
(this.name == null || annotation.name().equals(this.name)) &&
annotation.required() == this.required &&
annotation.defaultValue().equals(this.defaultValue);
}
}
public static class RequestPartPredicate implements Predicate<MethodParameter> {
private String name;
private boolean required = true;
public RequestPartPredicate name(String name) {
this.name = name;
return this;
}
public RequestPartPredicate noName() {
this.name = "";
return this;
}
public RequestPartPredicate notRequired() {
this.required = false;
return this;
}
@Override
public boolean test(MethodParameter parameter) {
RequestPart annotation = parameter.getParameterAnnotation(RequestPart.class);
return annotation != null &&
(this.name == null || annotation.name().equals(this.name)) &&
annotation.required() == this.required;
}
}
public static class ModelAttributeMethodPredicate implements Predicate<Method> {
private String name;
public ModelAttributeMethodPredicate name(String name) {
this.name = name;
return this;
}
public ModelAttributeMethodPredicate noName() {
this.name = "";
return this;
}
@Override
public boolean test(Method method) {
ModelAttribute annot = AnnotatedElementUtils.findMergedAnnotation(method, ModelAttribute.class);
return annot != null && (this.name == null || annot.name().equals(this.name));
}
}
public static class ResponseStatusPredicate implements Predicate<Method> {
private HttpStatus code = HttpStatus.INTERNAL_SERVER_ERROR;
private ResponseStatusPredicate() {
}
private ResponseStatusPredicate(HttpStatus code) {
this.code = code;
}
@Override
public boolean test(Method method) {
ResponseStatus annot = AnnotatedElementUtils.findMergedAnnotation(method, ResponseStatus.class);
return annot != null && annot.code().equals(this.code);
}
}
public static class RequestMappingPredicate implements Predicate<Method> {
private String[] path;
private RequestMethod[] method = {};
private String[] params;
private RequestMappingPredicate(String... path) {
this.path = path;
}
public RequestMappingPredicate method(RequestMethod... methods) {
this.method = methods;
return this;
}
public RequestMappingPredicate params(String... params) {
this.params = params;
return this;
}
@Override
public boolean test(Method method) {
RequestMapping annot = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
return annot != null &&
Arrays.equals(this.path, annot.path()) &&
Arrays.equals(this.method, annot.method()) &&
(this.params == null || Arrays.equals(this.params, annot.params()));
}
}
}

View File

@@ -18,14 +18,15 @@ package org.springframework.web.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.Log;
@@ -44,12 +45,16 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.objenesis.ObjenesisException;
import org.springframework.objenesis.SpringObjenesis;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ValueConstants;
import static java.util.stream.Collectors.joining;
/**
* Convenience class to resolve method parameters from hints.
@@ -74,6 +79,7 @@ import org.springframework.util.ReflectionUtils;
* <pre>
*
* import static org.springframework.web.method.ResolvableMethod.on;
* import static org.springframework.web.method.MvcAnnotationPredicates.requestMapping;
*
* // Return type
* on(TestController.class).resolveReturnType(Foo.class);
@@ -81,16 +87,13 @@ import org.springframework.util.ReflectionUtils;
* on(TestController.class).resolveReturnType(Mono.class, responseEntity(Foo.class));
*
* // Annotation + return type
* on(TestController.class).annotated(ResponseBody.class).resolveReturnType(Bar.class);
* on(TestController.class).annotPresent(RequestMapping.class).resolveReturnType(Bar.class);
*
* // Annotation not present
* on(TestController.class).notAnnotated(ResponseBody.class).resolveReturnType();
* on(TestController.class).annotNotPresent(RequestMapping.class).resolveReturnType();
*
* // Annotation with attributes
* on(TestController.class)
* .annotated(RequestMapping.class, patterns("/foo"), params("p"))
* .annotated(ResponseBody.class)
* .resolveReturnType();
* on(TestController.class).annot(requestMapping("/foo").params("p")).resolveReturnType();
* </pre>
*
* <h2>2. Method Arguments</h2>
@@ -100,12 +103,14 @@ import org.springframework.util.ReflectionUtils;
*
* <pre>
*
* import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
*
* ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build();
*
* testMethod.arg(Foo.class);
* testMethod.annotated(RequestBody.class)).arg(Bar.class);
* testMethod.annotated(RequestBody.class), required()).arg(Bar.class);
* testMethod.notAnnotated(RequestBody.class)).arg(Bar.class);
* testMethod.annotPresent(RequestParam.class).arg(Integer.class);
* testMethod.annotNotPresent(RequestParam.class)).arg(Integer.class);
* testMethod.annot(requestParam().name("c").notRequired()).arg(Integer.class);
* </pre>
*
* <h3>3. Mock Handler Method Invocation</h3>
@@ -180,13 +185,17 @@ public class ResolvableMethod {
}
/**
* Filter on method arguments that have the given annotation.
* @param annotationType the annotation type
* @param filter optional filters on the annotation
* Filter on method arguments with annotation.
* See {@link MvcAnnotationPredicates}.
*/
@SafeVarargs
public final <A extends Annotation> ArgResolver annotated(Class<A> annotationType, Predicate<A>... filter) {
return new ArgResolver().annotated(annotationType, filter);
public final ArgResolver annot(Predicate<MethodParameter>... filter) {
return new ArgResolver(filter);
}
@SafeVarargs
public final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {
return new ArgResolver().annotPresent(annotationTypes);
}
/**
@@ -194,16 +203,8 @@ public class ResolvableMethod {
* @param annotationTypes the annotation types
*/
@SafeVarargs
public final ArgResolver notAnnotated(Class<? extends Annotation>... annotationTypes) {
return new ArgResolver().notAnnotated(annotationTypes);
}
/**
* Filter on method arguments using customer predicates.
*/
@SafeVarargs
public final ArgResolver filtered(Predicate<MethodParameter>... filter) {
return new ArgResolver().filtered(filter);
public final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {
return new ArgResolver().annotNotPresent(annotationTypes);
}
@@ -215,11 +216,25 @@ public class ResolvableMethod {
private String formatMethod() {
return this.method().getName() +
Arrays.stream(this.method.getParameters())
.map(p -> {
Annotation[] annots = p.getAnnotations();
return (annots.length != 0 ? Arrays.toString(annots) : "") + " " + p;
})
.collect(Collectors.joining(",\n\t", "(\n\t", "\n)"));
.map(this::formatParameter)
.collect(joining(",\n\t", "(\n\t", "\n)"));
}
private String formatParameter(Parameter param) {
Annotation[] annot = param.getAnnotations();
return annot.length > 0 ?
Arrays.stream(annot).map(this::formatAnnotation).collect(joining(",", "[", "]")) + " " + param :
param.toString();
}
private String formatAnnotation(Annotation annotation) {
Map<String, Object> map = AnnotationUtils.getAnnotationAttributes(annotation);
map.forEach((key, value) -> {
if (value.equals(ValueConstants.DEFAULT_NONE)) {
map.put(key, "NONE");
}
});
return annotation.annotationType().getName() + map;
}
private static ResolvableType toResolvableType(Class<?> type, Class<?>... generics) {
@@ -273,25 +288,35 @@ public class ResolvableMethod {
}
/**
* Filter on methods with the given annotation type.
* @param annotationType the expected annotation type
* @param filter optional filters on the actual annotation
* Filter on annotated methods.
* See {@link MvcAnnotationPredicates}.
*/
@SafeVarargs
public final <A extends Annotation> Builder annotated(Class<A> annotationType, Predicate<A>... filter) {
String message = "annotated=" + annotationType.getName();
addFilter(message, m -> {
A annot = AnnotatedElementUtils.findMergedAnnotation(m, annotationType);
return (annot != null && Arrays.stream(filter).allMatch(f -> f.test(annot)));
});
public final Builder annot(Predicate<Method>... filters) {
this.filters.addAll(Arrays.asList(filters));
return this;
}
/**
* Filter on methods annotated with the given annotation type.
* @see #annot(Predicate[])
* @see MvcAnnotationPredicates
*/
@SafeVarargs
public final Builder annotPresent(Class<? extends Annotation>... annotationTypes) {
String message = "annotationPresent=" + Arrays.toString(annotationTypes);
addFilter(message, method ->
Arrays.stream(annotationTypes).allMatch(annotType ->
AnnotatedElementUtils.findMergedAnnotation(method, annotType) != null));
return this;
}
/**
* Filter on methods not annotated with the given annotation type.
*/
public final Builder notAnnotated(Class<? extends Annotation>... annotationTypes) {
String message = "notAnnotated=" + Arrays.toString(annotationTypes);
@SafeVarargs
public final Builder annotNotPresent(Class<? extends Annotation>... annotationTypes) {
String message = "annotationNotPresent=" + Arrays.toString(annotationTypes);
addFilter(message, method -> {
if (annotationTypes.length != 0) {
return Arrays.stream(annotationTypes).noneMatch(annotType ->
@@ -334,15 +359,6 @@ public class ResolvableMethod {
return this;
}
/**
* Add custom filters for matching methods.
*/
@SafeVarargs
public final Builder filtered(Predicate<Method>... filters) {
this.filters.addAll(Arrays.asList(filters));
return this;
}
/**
* Build a {@code ResolvableMethod} from the provided filters which must
* resolve to a unique, single method.
@@ -365,7 +381,7 @@ public class ResolvableMethod {
private String formatMethods(Set<Method> methods) {
return "\nMatched:\n" + methods.stream()
.map(Method::toGenericString).collect(Collectors.joining(",\n\t", "[\n\t", "\n]"));
.map(Method::toGenericString).collect(joining(",\n\t", "[\n\t", "\n]"));
}
public ResolvableMethod mockCall(Consumer<T> invoker) {
@@ -440,7 +456,7 @@ public class ResolvableMethod {
private String formatFilters() {
return this.filters.stream().map(Object::toString)
.collect(Collectors.joining(",\n\t\t", "[\n\t\t", "\n\t]"));
.collect(joining(",\n\t\t", "[\n\t\t", "\n\t]"));
}
}
@@ -499,18 +515,25 @@ public class ResolvableMethod {
this.filters.addAll(Arrays.asList(filter));
}
/**
* Filter on method arguments that have the given annotation.
* @param annotationType the annotation type
* @param filter optional filters on the annotation
* Filter on method arguments with annotations.
* See {@link MvcAnnotationPredicates}.
*/
@SafeVarargs
public final <A extends Annotation> ArgResolver annotated(Class<A> annotationType, Predicate<A>... filter) {
this.filters.add(param -> {
A annot = param.getParameterAnnotation(annotationType);
return (annot != null && Arrays.stream(filter).allMatch(f -> f.test(annot)));
});
public final ArgResolver annot(Predicate<MethodParameter>... filters) {
this.filters.addAll(Arrays.asList(filters));
return this;
}
/**
* Filter on method arguments that have the given annotations.
* @param annotationTypes the annotation types
* @see #annot(Predicate[])
* @see MvcAnnotationPredicates
*/
@SafeVarargs
public final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {
this.filters.add(param -> Arrays.stream(annotationTypes).allMatch(param::hasParameterAnnotation));
return this;
}
@@ -519,7 +542,7 @@ public class ResolvableMethod {
* @param annotationTypes the annotation types
*/
@SafeVarargs
public final ArgResolver notAnnotated(Class<? extends Annotation>... annotationTypes) {
public final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {
this.filters.add(param ->
(annotationTypes.length != 0) ?
Arrays.stream(annotationTypes).noneMatch(param::hasParameterAnnotation) :
@@ -527,15 +550,6 @@ public class ResolvableMethod {
return this;
}
/**
* Filter on method arguments using customer predicates.
*/
@SafeVarargs
public final ArgResolver filtered(Predicate<MethodParameter>... filter) {
this.filters.addAll(Arrays.asList(filter));
return this;
}
/**
* Resolve the argument also matching to the given type.
* @param type the expected type
@@ -566,8 +580,10 @@ public class ResolvableMethod {
*/
public final MethodParameter arg() {
List<MethodParameter> matches = applyFilters();
Assert.state(!matches.isEmpty(), () -> "No matching arg in method\n" + formatMethod());
Assert.state(matches.size() == 1, () -> "Multiple matching args in method\n" + formatMethod());
Assert.state(!matches.isEmpty(), () ->
"No matching arg in method\n" + formatMethod());
Assert.state(matches.size() == 1, () ->
"Multiple matching args in method\n" + formatMethod() + "\nMatches:\n\t" + matches);
return matches.get(0);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.web.method.annotation;
import java.util.Collections;
import java.util.Map;
import java.util.function.Predicate;
import org.junit.Before;
import org.junit.Test;
@@ -33,7 +32,10 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.ResolvableMethod;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
/**
* Test fixture with {@link RequestParamMapMethodArgumentResolver}.
@@ -63,16 +65,16 @@ public class RequestParamMapMethodArgumentResolverTests {
@Test
public void supportsParameter() {
MethodParameter param = this.testMethod.annotated(RequestParam.class, name("")).arg(Map.class);
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(MultiValueMap.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class, name("name")).arg(Map.class);
param = this.testMethod.annot(requestParam().name("name")).arg(Map.class);
assertFalse(resolver.supportsParameter(param));
param = this.testMethod.notAnnotated(RequestParam.class).arg(Map.class);
param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class);
assertFalse(resolver.supportsParameter(param));
}
@@ -83,7 +85,7 @@ public class RequestParamMapMethodArgumentResolverTests {
request.addParameter(name, value);
Map<String, String> expected = Collections.singletonMap(name, value);
MethodParameter param = this.testMethod.annotated(RequestParam.class, name("")).arg(Map.class);
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof Map);
@@ -101,17 +103,13 @@ public class RequestParamMapMethodArgumentResolverTests {
expected.add(name, value1);
expected.add(name, value2);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultiValueMap.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof MultiValueMap);
assertEquals("Invalid result", expected, result);
}
private Predicate<RequestParam> name(String name) {
return a -> name.equals(a.name());
}
public void handle(
@RequestParam Map<?, ?> param1,

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import javax.servlet.http.Part;
import org.junit.Before;
@@ -38,7 +37,6 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.bind.support.DefaultDataBinderFactory;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -58,6 +56,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;
import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
import static org.springframework.web.method.MvcAnnotationPredicates.requestPart;
/**
* Test fixture with {@link org.springframework.web.method.annotation.RequestParamMethodArgumentResolver}.
@@ -87,69 +87,69 @@ public class RequestParamMethodArgumentResolverTests {
public void supportsParameter() {
resolver = new RequestParamMethodArgumentResolver(null, true);
MethodParameter param = this.testMethod.annotated(RequestParam.class, value("bar")).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(String[].class);
param = this.testMethod.annotPresent(RequestParam.class).arg(String[].class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class, name("name")).arg(Map.class);
param = this.testMethod.annot(requestParam().name("name")).arg(Map.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(List.class, MultipartFile.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(List.class, MultipartFile.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile[].class);
param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile[].class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(Part.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(Part.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(List.class, Part.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(List.class, Part.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(Part[].class);
param = this.testMethod.annotPresent(RequestParam.class).arg(Part[].class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class, name("")).arg(Map.class);
param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
assertFalse(resolver.supportsParameter(param));
param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.notAnnotated().arg(MultipartFile.class);
param = this.testMethod.annotNotPresent().arg(MultipartFile.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.notAnnotated(RequestParam.class).arg(List.class, MultipartFile.class);
param = this.testMethod.annotNotPresent(RequestParam.class).arg(List.class, MultipartFile.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.notAnnotated(RequestParam.class).arg(Part.class);
param = this.testMethod.annotNotPresent(RequestParam.class).arg(Part.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestPart.class).arg(MultipartFile.class);
param = this.testMethod.annot(requestPart()).arg(MultipartFile.class);
assertFalse(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class, required(), value("")).arg(String.class);
param = this.testMethod.annot(requestParam()).arg(String.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class, required().negate()).arg(String.class);
param = this.testMethod.annot(requestParam().notRequired()).arg(String.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(Optional.class, Integer.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, Integer.class);
assertTrue(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestParam.class).arg(Optional.class, MultipartFile.class);
param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, MultipartFile.class);
assertTrue(resolver.supportsParameter(param));
resolver = new RequestParamMethodArgumentResolver(null, false);
param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
assertFalse(resolver.supportsParameter(param));
param = this.testMethod.annotated(RequestPart.class).arg(MultipartFile.class);
param = this.testMethod.annotPresent(RequestPart.class).arg(MultipartFile.class);
assertFalse(resolver.supportsParameter(param));
}
@@ -158,7 +158,7 @@ public class RequestParamMethodArgumentResolverTests {
String expected = "foo";
request.addParameter("name", expected);
MethodParameter param = this.testMethod.annotated(RequestParam.class, value("bar")).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof String);
assertEquals("Invalid result", expected, result);
@@ -169,7 +169,7 @@ public class RequestParamMethodArgumentResolverTests {
String[] expected = new String[] {"foo", "bar"};
request.addParameter("name", expected);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(String[].class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(String[].class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof String[]);
assertArrayEquals("Invalid result", expected, (String[]) result);
@@ -182,7 +182,7 @@ public class RequestParamMethodArgumentResolverTests {
request.addFile(expected);
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof MultipartFile);
assertEquals("Invalid result", expected, result);
@@ -198,10 +198,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes()));
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod
.annotated(RequestParam.class).arg(List.class, MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(List.class, MultipartFile.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof List);
assertEquals(Arrays.asList(expected1, expected2), result);
}
@@ -216,8 +215,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes()));
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile[].class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile[].class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof MultipartFile[]);
MultipartFile[] parts = (MultipartFile[]) result;
assertEquals(2, parts.length);
@@ -234,8 +234,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addPart(expected);
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(Part.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Part.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof Part);
assertEquals("Invalid result", expected, result);
}
@@ -252,8 +253,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addPart(new MockPart("other", "Hello World 3".getBytes()));
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(List.class, Part.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(List.class, Part.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof List);
assertEquals(Arrays.asList(expected1, expected2), result);
}
@@ -270,8 +272,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addPart(new MockPart("other", "Hello World 3".getBytes()));
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(Part[].class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Part[].class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof Part[]);
Part[] parts = (Part[]) result;
assertEquals(2, parts.length);
@@ -286,14 +289,14 @@ public class RequestParamMethodArgumentResolverTests {
request.addFile(expected);
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.notAnnotated().arg(MultipartFile.class);
MethodParameter param = this.testMethod.annotNotPresent().arg(MultipartFile.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof MultipartFile);
assertEquals("Invalid result", expected, result);
}
@Test
public void resolveMultipartFileListNotAnnotated() throws Exception {
public void resolveMultipartFileListNotannot() throws Exception {
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
MultipartFile expected1 = new MockMultipartFile("multipartFileList", "Hello World 1".getBytes());
MultipartFile expected2 = new MockMultipartFile("multipartFileList", "Hello World 2".getBytes());
@@ -302,7 +305,7 @@ public class RequestParamMethodArgumentResolverTests {
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod
.notAnnotated(RequestParam.class).arg(List.class, MultipartFile.class);
.annotNotPresent(RequestParam.class).arg(List.class, MultipartFile.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof List);
@@ -311,7 +314,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test(expected = MultipartException.class)
public void isMultipartRequest() throws Exception {
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile.class);
resolver.resolveArgument(param, null, webRequest, null);
fail("Expected exception: request is not a multipart request");
}
@@ -325,7 +328,7 @@ public class RequestParamMethodArgumentResolverTests {
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod
.notAnnotated(RequestParam.class).arg(List.class, MultipartFile.class);
.annotNotPresent(RequestParam.class).arg(List.class, MultipartFile.class);
Object actual = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(actual instanceof List);
@@ -335,7 +338,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test(expected = MultipartException.class)
public void noMultipartContent() throws Exception {
request.setMethod("POST");
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile.class);
resolver.resolveArgument(param, null, webRequest, null);
fail("Expected exception: no multipart content");
}
@@ -344,7 +347,7 @@ public class RequestParamMethodArgumentResolverTests {
public void missingMultipartFile() throws Exception {
request.setMethod("POST");
request.setContentType("multipart/form-data");
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultipartFile.class);
resolver.resolveArgument(param, null, webRequest, null);
fail("Expected exception: no such part found");
}
@@ -358,7 +361,7 @@ public class RequestParamMethodArgumentResolverTests {
request.addPart(expected);
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod.notAnnotated(RequestParam.class).arg(Part.class);
MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(Part.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof Part);
assertEquals("Invalid result", expected, result);
@@ -366,7 +369,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test
public void resolveDefaultValue() throws Exception {
MethodParameter param = this.testMethod.annotated(RequestParam.class, value("bar")).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof String);
assertEquals("Invalid result", "bar", result);
@@ -374,7 +377,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test(expected = MissingServletRequestParameterException.class)
public void missingRequestParam() throws Exception {
MethodParameter param = this.testMethod.annotated(RequestParam.class).arg(String[].class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(String[].class);
resolver.resolveArgument(param, null, webRequest, null);
fail("Expected exception");
}
@@ -389,7 +392,7 @@ public class RequestParamMethodArgumentResolverTests {
this.request.addParameter("stringNotAnnot", "");
MethodParameter param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
Object arg = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertNull(arg);
}
@@ -404,7 +407,7 @@ public class RequestParamMethodArgumentResolverTests {
this.request.addParameter("name", "");
MethodParameter param = this.testMethod.annotated(RequestParam.class, required().negate()).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired()).arg(String.class);
Object arg = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertNull(arg);
}
@@ -412,7 +415,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test
public void resolveSimpleTypeParam() throws Exception {
request.setParameter("stringNotAnnot", "plainValue");
MethodParameter param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof String);
@@ -421,7 +424,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test // SPR-8561
public void resolveSimpleTypeParamToNull() throws Exception {
MethodParameter param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertNull(result);
}
@@ -429,7 +432,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test // SPR-10180
public void resolveEmptyValueToDefault() throws Exception {
this.request.addParameter("name", "");
MethodParameter param = this.testMethod.annotated(RequestParam.class, value("bar")).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertEquals("bar", result);
}
@@ -437,7 +440,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test
public void resolveEmptyValueWithoutDefault() throws Exception {
this.request.addParameter("stringNotAnnot", "");
MethodParameter param = this.testMethod.notAnnotated(RequestParam.class).arg(String.class);
MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertEquals("", result);
}
@@ -445,7 +448,7 @@ public class RequestParamMethodArgumentResolverTests {
@Test
public void resolveEmptyValueRequiredWithoutDefault() throws Exception {
this.request.addParameter("name", "");
MethodParameter param = this.testMethod.annotated(RequestParam.class, required(), value("")).arg(String.class);
MethodParameter param = this.testMethod.annot(requestParam().notRequired()).arg(String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertEquals("", result);
}
@@ -457,9 +460,7 @@ public class RequestParamMethodArgumentResolverTests {
initializer.setConversionService(new DefaultConversionService());
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
MethodParameter param = this.testMethod
.annotated(RequestParam.class).arg(Optional.class, Integer.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, Integer.class);
Object result = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertEquals(Optional.empty(), result);
@@ -480,10 +481,9 @@ public class RequestParamMethodArgumentResolverTests {
request.addFile(expected);
webRequest = new ServletWebRequest(request);
MethodParameter param = this.testMethod
.annotated(RequestParam.class).arg(Optional.class, MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, MultipartFile.class);
Object result = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertTrue(result instanceof Optional);
assertEquals("Invalid result", expected, ((Optional<?>) result).get());
}
@@ -497,10 +497,9 @@ public class RequestParamMethodArgumentResolverTests {
request.setMethod("POST");
request.setContentType("multipart/form-data");
MethodParameter param = this.testMethod
.annotated(RequestParam.class).arg(Optional.class, MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, MultipartFile.class);
Object actual = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertEquals(Optional.empty(), actual);
}
@@ -510,27 +509,12 @@ public class RequestParamMethodArgumentResolverTests {
initializer.setConversionService(new DefaultConversionService());
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
MethodParameter param = this.testMethod
.annotated(RequestParam.class).arg(Optional.class, MultipartFile.class);
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(Optional.class, MultipartFile.class);
Object actual = resolver.resolveArgument(param, null, webRequest, binderFactory);
assertEquals(Optional.empty(), actual);
}
private Predicate<RequestParam> name(String name) {
return a -> name.equals(a.name());
}
private Predicate<RequestParam> required() {
return RequestParam::required;
}
private Predicate<RequestParam> value(String value) {
return !value.isEmpty() ?
requestParam -> value.equals(requestParam.defaultValue()) :
requestParam -> ValueConstants.DEFAULT_NONE.equals(requestParam.defaultValue());
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
public void handle(