DATACMNS-117 - Added PageableHandlerArgumentResolver.
Added PageableHandlerArgumentResolver to supersede the now deprecated PageableArgumentResolver. The latter still stays available for Spring 3.0.x based deployments. Updated reference documentation to mention the newly introduced type as well as possible configuration options.
This commit is contained in:
@@ -42,8 +42,10 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* methods. Request properties to be parsed can be configured. Default configuration uses request properties beginning
|
||||
* with {@link #DEFAULT_PREFIX}{@link #DEFAULT_SEPARATOR}.
|
||||
*
|
||||
* @deprecated use {@link PageableWebHandlerArgumentResolver} instead.
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Deprecated
|
||||
public class PageableArgumentResolver implements WebArgumentResolver {
|
||||
|
||||
private static final Pageable DEFAULT_PAGE_REQUEST = new PageRequest(0, 10);
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2013 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.data.web;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.ServletRequestParameterPropertyValues;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Extracts paging information from web requests and thus allows injecting {@link Pageable} instances into controller
|
||||
* methods. Request properties to be parsed can be configured. Default configuration uses request properties beginning
|
||||
* with {@link #DEFAULT_PREFIX}{@link #DEFAULT_SEPARATOR}.
|
||||
*
|
||||
* @since 1.6
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PageableHandlerArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private static final Pageable DEFAULT_PAGE_REQUEST = new PageRequest(0, 10);
|
||||
private static final String DEFAULT_PREFIX = "page";
|
||||
private static final String DEFAULT_SEPARATOR = ".";
|
||||
|
||||
private Pageable fallbackPagable = DEFAULT_PAGE_REQUEST;
|
||||
private String prefix = DEFAULT_PREFIX;
|
||||
private String separator = DEFAULT_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Setter to configure a fallback instance of {@link Pageable} that is being used to back missing parameters. Defaults
|
||||
* to {@link #DEFAULT_PAGE_REQUEST}.
|
||||
*
|
||||
* @param fallbackPagable the fallbackPagable to set
|
||||
*/
|
||||
public void setFallbackPagable(Pageable fallbackPagable) {
|
||||
this.fallbackPagable = null == fallbackPagable ? DEFAULT_PAGE_REQUEST : fallbackPagable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter to configure the prefix of request parameters to be used to retrieve paging information. Defaults to
|
||||
* {@link #DEFAULT_PREFIX}.
|
||||
*
|
||||
* @param prefix the prefix to set
|
||||
*/
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = null == prefix ? DEFAULT_PREFIX : prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter to configure the separator between prefix and actual property value. Defaults to {@link #DEFAULT_SEPARATOR}.
|
||||
*
|
||||
* @param separator the separator to set. Will default to {@link #DEFAULT_SEPEARATOR} if set to {@literal null}.
|
||||
*/
|
||||
public void setSeparator(String separator) {
|
||||
this.separator = null == separator ? DEFAULT_SEPARATOR : separator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
|
||||
*/
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return Pageable.class.equals(parameter.getParameterType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
|
||||
*/
|
||||
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
assertPageableUniqueness(methodParameter);
|
||||
|
||||
Pageable request = getDefaultFromAnnotationOrFallback(methodParameter);
|
||||
ServletRequest servletRequest = (ServletRequest) webRequest.getNativeRequest();
|
||||
PropertyValues propertyValues = new ServletRequestParameterPropertyValues(servletRequest,
|
||||
getPrefix(methodParameter), separator);
|
||||
|
||||
DataBinder binder = new ServletRequestDataBinder(request);
|
||||
|
||||
binder.initDirectFieldAccess();
|
||||
binder.registerCustomEditor(Sort.class, new SortPropertyEditor("sort.dir", propertyValues));
|
||||
binder.bind(propertyValues);
|
||||
|
||||
if (request.getPageNumber() > 0) {
|
||||
request = new PageRequest(request.getPageNumber() - 1, request.getPageSize(), request.getSort());
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private Pageable getDefaultFromAnnotationOrFallback(MethodParameter methodParameter) {
|
||||
|
||||
// search for PageableDefaults annotation
|
||||
for (Annotation annotation : methodParameter.getParameterAnnotations()) {
|
||||
if (annotation instanceof PageableDefaults) {
|
||||
return getDefaultPageRequestFrom((PageableDefaults) annotation);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct request with fallback request to ensure sensible
|
||||
// default values. Create fresh copy as Spring will manipulate the
|
||||
// instance under the covers
|
||||
return new PageRequest(fallbackPagable.getPageNumber(), fallbackPagable.getPageSize(), fallbackPagable.getSort());
|
||||
}
|
||||
|
||||
private static Pageable getDefaultPageRequestFrom(PageableDefaults defaults) {
|
||||
|
||||
// +1 is because we substract 1 later
|
||||
int defaultPageNumber = defaults.pageNumber() + 1;
|
||||
int defaultPageSize = defaults.value();
|
||||
|
||||
if (defaults.sort().length == 0) {
|
||||
return new PageRequest(defaultPageNumber, defaultPageSize);
|
||||
}
|
||||
|
||||
return new PageRequest(defaultPageNumber, defaultPageSize, defaults.sortDir(), defaults.sort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the prefix to use to bind properties from. Will prepend a possible {@link Qualifier} if available or
|
||||
* return the configured prefix otherwise.
|
||||
*
|
||||
* @param parameter
|
||||
* @return
|
||||
*/
|
||||
private String getPrefix(MethodParameter parameter) {
|
||||
|
||||
for (Annotation annotation : parameter.getParameterAnnotations()) {
|
||||
if (annotation instanceof Qualifier) {
|
||||
return new StringBuilder(((Qualifier) annotation).value()).append("_").append(prefix).toString();
|
||||
}
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts uniqueness of all {@link Pageable} parameters of the method of the given {@link MethodParameter}.
|
||||
*
|
||||
* @param parameter
|
||||
*/
|
||||
private void assertPageableUniqueness(MethodParameter parameter) {
|
||||
|
||||
Method method = parameter.getMethod();
|
||||
|
||||
if (containsMoreThanOnePageableParameter(method)) {
|
||||
Annotation[][] annotations = method.getParameterAnnotations();
|
||||
assertQualifiersFor(method.getParameterTypes(), annotations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Method} has more than one {@link Pageable} parameter.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean containsMoreThanOnePageableParameter(Method method) {
|
||||
|
||||
boolean pageableFound = false;
|
||||
|
||||
for (Class<?> type : method.getParameterTypes()) {
|
||||
|
||||
if (pageableFound && type.equals(Pageable.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.equals(Pageable.class)) {
|
||||
pageableFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that every {@link Pageable} parameter of the given parameters carries an {@link Qualifier} annotation to
|
||||
* distinguish them from each other.
|
||||
*
|
||||
* @param parameterTypes
|
||||
* @param annotations
|
||||
*/
|
||||
private void assertQualifiersFor(Class<?>[] parameterTypes, Annotation[][] annotations) {
|
||||
|
||||
Set<String> values = new HashSet<String>();
|
||||
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
|
||||
if (Pageable.class.equals(parameterTypes[i])) {
|
||||
|
||||
Qualifier qualifier = findAnnotation(annotations[i]);
|
||||
|
||||
if (null == qualifier) {
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous Pageable arguments in handler method. If you use multiple parameters of type Pageable you need to qualify them with @Qualifier");
|
||||
}
|
||||
|
||||
if (values.contains(qualifier.value())) {
|
||||
throw new IllegalStateException("Values of the user Qualifiers must be unique!");
|
||||
}
|
||||
|
||||
values.add(qualifier.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Qualifier} annotation from the given array of {@link Annotation}s. Returns {@literal null} if the
|
||||
* array does not contain a {@link Qualifier} annotation.
|
||||
*
|
||||
* @param annotations
|
||||
* @return
|
||||
*/
|
||||
private Qualifier findAnnotation(Annotation[] annotations) {
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof Qualifier) {
|
||||
return (Qualifier) annotation;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link java.beans.PropertyEditor} to create {@link Sort} instances from textual representations. The implementation
|
||||
* interprets the string as a comma separated list where the first entry is the sort direction ( {@code asc},
|
||||
* {@code desc}) followed by the properties to sort by.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class SortPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private final String orderProperty;
|
||||
private final PropertyValues values;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SortPropertyEditor}.
|
||||
*
|
||||
* @param orderProperty
|
||||
* @param values
|
||||
*/
|
||||
public SortPropertyEditor(String orderProperty, PropertyValues values) {
|
||||
|
||||
this.orderProperty = orderProperty;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String text) {
|
||||
|
||||
PropertyValue rawOrder = values.getPropertyValue(orderProperty);
|
||||
Direction order = null == rawOrder ? Direction.ASC : Direction.fromString(rawOrder.getValue().toString());
|
||||
|
||||
setValue(new Sort(order, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user