Skip to content

Commit bcd21b6

Browse files
committed
DATACMNS-1211 - Add ReactiveSortHandlerMethodArgumentResolver.
Add ReactiveSortHandlerMethodArgumentResolver and extract shared code from imperative SortHandlerMethodArgumentResolver into SortHandlerMethodArgumentResolverSupport.
1 parent c4607f1 commit bcd21b6

11 files changed

+1387
-569
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
<artifactId>spring-web</artifactId>
6565
<optional>true</optional>
6666
</dependency>
67+
<dependency>
68+
<groupId>org.springframework</groupId>
69+
<artifactId>spring-webflux</artifactId>
70+
<optional>true</optional>
71+
</dependency>
6772

6873
<dependency>
6974
<groupId>javax.servlet</groupId>

src/main/java/org/springframework/data/web/PageableHandlerMethodArgumentResolver.java

Lines changed: 11 additions & 255 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,11 @@
1515
*/
1616
package org.springframework.data.web;
1717

18-
import static org.springframework.data.web.SpringDataAnnotationUtils.*;
19-
20-
import java.lang.reflect.Method;
21-
import java.util.Optional;
22-
23-
import org.springframework.beans.factory.annotation.Qualifier;
2418
import org.springframework.core.MethodParameter;
2519
import org.springframework.data.domain.PageRequest;
2620
import org.springframework.data.domain.Pageable;
2721
import org.springframework.data.domain.Sort;
2822
import org.springframework.lang.Nullable;
29-
import org.springframework.util.Assert;
30-
import org.springframework.util.StringUtils;
3123
import org.springframework.web.bind.support.WebDataBinderFactory;
3224
import org.springframework.web.context.request.NativeWebRequest;
3325
import org.springframework.web.method.support.ModelAndViewContainer;
@@ -36,33 +28,18 @@
3628
* Extracts paging information from web requests and thus allows injecting {@link Pageable} instances into controller
3729
* methods. Request properties to be parsed can be configured. Default configuration uses request parameters beginning
3830
* with {@link #DEFAULT_PAGE_PARAMETER}{@link #DEFAULT_QUALIFIER_DELIMITER}.
39-
*
31+
*
4032
* @since 1.6
4133
* @author Oliver Gierke
4234
* @author Nick Williams
4335
* @author Mark Paluch
4436
* @author Christoph Strobl
4537
*/
46-
public class PageableHandlerMethodArgumentResolver implements PageableArgumentResolver {
38+
public class PageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolverSupport
39+
implements PageableArgumentResolver {
4740

4841
private static final SortHandlerMethodArgumentResolver DEFAULT_SORT_RESOLVER = new SortHandlerMethodArgumentResolver();
49-
private static final String INVALID_DEFAULT_PAGE_SIZE = "Invalid default page size configured for method %s! Must not be less than one!";
50-
51-
private static final String DEFAULT_PAGE_PARAMETER = "page";
52-
private static final String DEFAULT_SIZE_PARAMETER = "size";
53-
private static final String DEFAULT_PREFIX = "";
54-
private static final String DEFAULT_QUALIFIER_DELIMITER = "_";
55-
private static final int DEFAULT_MAX_PAGE_SIZE = 2000;
56-
static final Pageable DEFAULT_PAGE_REQUEST = PageRequest.of(0, 20);
57-
58-
private Pageable fallbackPageable = DEFAULT_PAGE_REQUEST;
5942
private SortArgumentResolver sortResolver;
60-
private String pageParameterName = DEFAULT_PAGE_PARAMETER;
61-
private String sizeParameterName = DEFAULT_SIZE_PARAMETER;
62-
private String prefix = DEFAULT_PREFIX;
63-
private String qualifierDelimiter = DEFAULT_QUALIFIER_DELIMITER;
64-
private int maxPageSize = DEFAULT_MAX_PAGE_SIZE;
65-
private boolean oneIndexedParameters = false;
6643

6744
/**
6845
* Constructs an instance of this resolved with a default {@link SortHandlerMethodArgumentResolver}.
@@ -73,7 +50,7 @@ public PageableHandlerMethodArgumentResolver() {
7350

7451
/**
7552
* Constructs an instance of this resolver with the specified {@link SortHandlerMethodArgumentResolver}.
76-
*
53+
*
7754
* @param sortResolver the sort resolver to use
7855
*/
7956
public PageableHandlerMethodArgumentResolver(SortHandlerMethodArgumentResolver sortResolver) {
@@ -90,139 +67,7 @@ public PageableHandlerMethodArgumentResolver(@Nullable SortArgumentResolver sort
9067
this.sortResolver = sortResolver == null ? DEFAULT_SORT_RESOLVER : sortResolver;
9168
}
9269

93-
/**
94-
* Configures the {@link Pageable} to be used as fallback in case no {@link PageableDefault} or
95-
* {@link PageableDefault} (the latter only supported in legacy mode) can be found at the method parameter to be
96-
* resolved.
97-
* <p>
98-
* If you set this to {@literal Optional#empty()}, be aware that you controller methods will get {@literal null}
99-
* handed into them in case no {@link Pageable} data can be found in the request. Note, that doing so will require you
100-
* supply bot the page <em>and</em> the size parameter with the requests as there will be no default for any of the
101-
* parameters available.
102-
*
103-
* @param fallbackPageable the {@link Pageable} to be used as general fallback.
104-
*/
105-
public void setFallbackPageable(Pageable fallbackPageable) {
106-
107-
Assert.notNull(fallbackPageable, "Fallback Pageable must not be null!");
108-
109-
this.fallbackPageable = fallbackPageable;
110-
}
111-
112-
/**
113-
* Returns whether the given {@link Pageable} is the fallback one.
114-
*
115-
* @param pageable can be {@literal null}.
116-
* @since 1.9
117-
* @return
118-
*/
119-
public boolean isFallbackPageable(Pageable pageable) {
120-
return fallbackPageable == null ? false : fallbackPageable.equals(pageable);
121-
}
122-
123-
/**
124-
* Configures the maximum page size to be accepted. This allows to put an upper boundary of the page size to prevent
125-
* potential attacks trying to issue an {@link OutOfMemoryError}. Defaults to {@link #DEFAULT_MAX_PAGE_SIZE}.
126-
*
127-
* @param maxPageSize the maxPageSize to set
128-
*/
129-
public void setMaxPageSize(int maxPageSize) {
130-
this.maxPageSize = maxPageSize;
131-
}
132-
133-
/**
134-
* Retrieves the maximum page size to be accepted. This allows to put an upper boundary of the page size to prevent
135-
* potential attacks trying to issue an {@link OutOfMemoryError}. Defaults to {@link #DEFAULT_MAX_PAGE_SIZE}.
136-
*
137-
* @return the maximum page size allowed.
138-
*/
139-
protected int getMaxPageSize() {
140-
return this.maxPageSize;
141-
}
142-
143-
/**
144-
* Configures the parameter name to be used to find the page number in the request. Defaults to {@code page}.
145-
*
146-
* @param pageParameterName the parameter name to be used, must not be {@literal null} or empty.
147-
*/
148-
public void setPageParameterName(String pageParameterName) {
149-
150-
Assert.hasText(pageParameterName, "Page parameter name must not be null or empty!");
151-
this.pageParameterName = pageParameterName;
152-
}
153-
154-
/**
155-
* Retrieves the parameter name to be used to find the page number in the request. Defaults to {@code page}.
156-
*
157-
* @return the parameter name to be used, never {@literal null} or empty.
158-
*/
159-
protected String getPageParameterName() {
160-
return this.pageParameterName;
161-
}
162-
163-
/**
164-
* Configures the parameter name to be used to find the page size in the request. Defaults to {@code size}.
165-
*
166-
* @param sizeParameterName the parameter name to be used, must not be {@literal null} or empty.
167-
*/
168-
public void setSizeParameterName(String sizeParameterName) {
169-
170-
Assert.hasText(sizeParameterName, "Size parameter name must not be null or empty!");
171-
this.sizeParameterName = sizeParameterName;
172-
}
173-
174-
/**
175-
* Retrieves the parameter name to be used to find the page size in the request. Defaults to {@code size}.
176-
*
177-
* @return the parameter name to be used, never {@literal null} or empty.
178-
*/
179-
protected String getSizeParameterName() {
180-
return this.sizeParameterName;
181-
}
182-
183-
/**
184-
* Configures a general prefix to be prepended to the page number and page size parameters. Useful to namespace the
185-
* property names used in case they are clashing with ones used by your application. By default, no prefix is used.
186-
*
187-
* @param prefix the prefix to be used or {@literal null} to reset to the default.
188-
*/
189-
public void setPrefix(String prefix) {
190-
this.prefix = prefix == null ? DEFAULT_PREFIX : prefix;
191-
}
192-
193-
/**
194-
* The delimiter to be used between the qualifier and the actual page number and size properties. Defaults to
195-
* {@code _}. So a qualifier of {@code foo} will result in a page number parameter of {@code foo_page}.
196-
*
197-
* @param qualifierDelimiter the delimiter to be used or {@literal null} to reset to the default.
198-
*/
199-
public void setQualifierDelimiter(String qualifierDelimiter) {
200-
this.qualifierDelimiter = qualifierDelimiter == null ? DEFAULT_QUALIFIER_DELIMITER : qualifierDelimiter;
201-
}
202-
203-
/**
204-
* Configures whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
205-
* {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
206-
* {@literal true}, a page number of 1 in the request will be considered the first page.
207-
*
208-
* @param oneIndexedParameters the oneIndexedParameters to set
209-
*/
210-
public void setOneIndexedParameters(boolean oneIndexedParameters) {
211-
this.oneIndexedParameters = oneIndexedParameters;
212-
}
213-
214-
/**
215-
* Indicates whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
216-
* {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
217-
* {@literal true}, a page number of 1 in the request will be considered the first page.
218-
*
219-
* @return whether to assume 1-based page number indexes in the request parameters.
220-
*/
221-
protected boolean isOneIndexedParameters() {
222-
return this.oneIndexedParameters;
223-
}
224-
225-
/*
70+
/*
22671
* (non-Javadoc)
22772
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
22873
*/
@@ -239,106 +84,17 @@ public boolean supportsParameter(MethodParameter parameter) {
23984
public Pageable resolveArgument(MethodParameter methodParameter, @Nullable ModelAndViewContainer mavContainer,
24085
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
24186

242-
assertPageableUniqueness(methodParameter);
87+
String page = webRequest.getParameter(getParameterNameToUse(getPageParameterName(), methodParameter));
88+
String pageSize = webRequest.getParameter(getParameterNameToUse(getSizeParameterName(), methodParameter));
24389

244-
Optional<Pageable> defaultOrFallback = getDefaultFromAnnotationOrFallback(methodParameter).toOptional();
245-
246-
String pageString = webRequest.getParameter(getParameterNameToUse(pageParameterName, methodParameter));
247-
String pageSizeString = webRequest.getParameter(getParameterNameToUse(sizeParameterName, methodParameter));
248-
249-
Optional<Integer> page = parseAndApplyBoundaries(pageString, Integer.MAX_VALUE, true);
250-
Optional<Integer> pageSize = parseAndApplyBoundaries(pageSizeString, maxPageSize, false);
251-
252-
if (!(page.isPresent() && pageSize.isPresent()) && !defaultOrFallback.isPresent()) {
253-
return Pageable.unpaged();
254-
}
255-
256-
int p = page
257-
.orElseGet(() -> defaultOrFallback.map(Pageable::getPageNumber).orElseThrow(IllegalStateException::new));
258-
int ps = pageSize
259-
.orElseGet(() -> defaultOrFallback.map(Pageable::getPageSize).orElseThrow(IllegalStateException::new));
260-
261-
// Limit lower bound
262-
ps = ps < 1 ? defaultOrFallback.map(Pageable::getPageSize).orElseThrow(IllegalStateException::new) : ps;
263-
// Limit upper bound
264-
ps = ps > maxPageSize ? maxPageSize : ps;
26590

26691
Sort sort = sortResolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
92+
Pageable pageable = getPageable(methodParameter, page, pageSize);
26793

268-
return PageRequest.of(p, ps,
269-
sort.isSorted() ? sort : defaultOrFallback.map(Pageable::getSort).orElseGet(Sort::unsorted));
270-
}
271-
272-
/**
273-
* Returns the name of the request parameter to find the {@link Pageable} information in. Inspects the given
274-
* {@link MethodParameter} for {@link Qualifier} present and prefixes the given source parameter name with it.
275-
*
276-
* @param source the basic parameter name.
277-
* @param parameter the {@link MethodParameter} potentially qualified.
278-
* @return the name of the request parameter.
279-
*/
280-
protected String getParameterNameToUse(String source, @Nullable MethodParameter parameter) {
281-
282-
StringBuilder builder = new StringBuilder(prefix);
283-
284-
Qualifier qualifier = parameter == null ? null : parameter.getParameterAnnotation(Qualifier.class);
285-
286-
if (qualifier != null) {
287-
builder.append(qualifier.value());
288-
builder.append(qualifierDelimiter);
289-
}
290-
291-
return builder.append(source).toString();
292-
}
293-
294-
private Pageable getDefaultFromAnnotationOrFallback(MethodParameter methodParameter) {
295-
296-
PageableDefault defaults = methodParameter.getParameterAnnotation(PageableDefault.class);
297-
298-
if (defaults != null) {
299-
return getDefaultPageRequestFrom(methodParameter, defaults);
94+
if (sort.isSorted()) {
95+
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
30096
}
30197

302-
return fallbackPageable;
303-
}
304-
305-
private static Pageable getDefaultPageRequestFrom(MethodParameter parameter, PageableDefault defaults) {
306-
307-
Integer defaultPageNumber = defaults.page();
308-
Integer defaultPageSize = getSpecificPropertyOrDefaultFromValue(defaults, "size");
309-
310-
if (defaultPageSize < 1) {
311-
Method annotatedMethod = parameter.getMethod();
312-
throw new IllegalStateException(String.format(INVALID_DEFAULT_PAGE_SIZE, annotatedMethod));
313-
}
314-
315-
if (defaults.sort().length == 0) {
316-
return PageRequest.of(defaultPageNumber, defaultPageSize);
317-
}
318-
319-
return PageRequest.of(defaultPageNumber, defaultPageSize, defaults.direction(), defaults.sort());
320-
}
321-
322-
/**
323-
* Tries to parse the given {@link String} into an integer and applies the given boundaries. Will return 0 if the
324-
* {@link String} cannot be parsed.
325-
*
326-
* @param parameter the parameter value.
327-
* @param upper the upper bound to be applied.
328-
* @param shiftIndex whether to shift the index if {@link #oneIndexedParameters} is set to true.
329-
* @return
330-
*/
331-
private Optional<Integer> parseAndApplyBoundaries(@Nullable String parameter, int upper, boolean shiftIndex) {
332-
333-
if (!StringUtils.hasText(parameter)) {
334-
return Optional.empty();
335-
}
336-
337-
try {
338-
int parsed = Integer.parseInt(parameter) - (oneIndexedParameters && shiftIndex ? 1 : 0);
339-
return Optional.of(parsed < 0 ? 0 : parsed > upper ? upper : parsed);
340-
} catch (NumberFormatException e) {
341-
return Optional.of(0);
342-
}
98+
return pageable;
34399
}
344100
}

0 commit comments

Comments
 (0)