Skip to content
Snippets Groups Projects

Update to add external hook for page size for paginated results

5 files
+ 112
59
Compare changes
  • Side-by-side
  • Inline
Files
5
@@ -4,9 +4,11 @@ import java.io.IOException;
import java.util.List;
import javax.enterprise.inject.Instance;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@@ -18,15 +20,20 @@ import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.spi.LinkHeader;
/**
* Adds pagination and Link headers to the response by slicing the response entity if its a list
* entity. This will not dig into complex entities to avoid false positives.
* Adds pagination and Link headers to the response by slicing the response
* entity if its a list entity. This will not dig into complex entities to avoid
* false positives.
*
* @author Martin Lowe
*/
@Provider
public class PaginatedResultsFilter implements ContainerResponseFilter {
// should be set whenever we pass a limited subset to the response to be paginated of a larger subset
// should be set whenever we pass a limited subset to the response to be
// paginated of a larger subset
public static final String MAX_RESULTS_SIZE_HEADER = "X-Max-Result-Size";
// should be set whenever we request data that has a maximum page size that is
// different than the default here
public static final String MAX_PAGE_SIZE_HEADER = "X-Max-Page-Size";
@ConfigProperty(name = "eclipse.pagination.enabled", defaultValue = "true")
Instance<Boolean> enablePagination;
@@ -40,29 +47,30 @@ public class PaginatedResultsFilter implements ContainerResponseFilter {
@ConfigProperty(name = "eclipse.pagination.scheme.value", defaultValue = "https")
Instance<String> linkScheme;
@Context
HttpServletResponse response;
@Override
public void filter(
ContainerRequestContext requestContext, ContainerResponseContext responseContext)
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
if (enablePagination.get()) {
int pageSize = defaultPageSize.get();
int pageSize = getCurrentLimit(responseContext);
Object entity = responseContext.getEntity();
// only try and paginate if there are multiple entities
if (entity instanceof List) {
List<?> listEntity = (List<?>) entity;
int page = getRequestedPage(listEntity);
int page = getRequestedPage(listEntity, pageSize);
// if available, use max results header value
String rawMaxSize = responseContext.getHeaderString(MAX_RESULTS_SIZE_HEADER);
String rawMaxSize = response.getHeader(MAX_RESULTS_SIZE_HEADER);
int maxSize = listEntity.size();
if (StringUtils.isNumeric(rawMaxSize)) {
maxSize = Integer.valueOf(rawMaxSize);
maxSize = Integer.valueOf(rawMaxSize);
}
int lastPage = (int) Math.ceil((double) maxSize / pageSize);
// set the sliced array as the entity
responseContext.setEntity(
listEntity.subList(
getArrayLimitedNumber(listEntity, Math.max(0, page - 1) * pageSize),
responseContext
.setEntity(listEntity.subList(getArrayLimitedNumber(listEntity, Math.max(0, page - 1) * pageSize),
getArrayLimitedNumber(listEntity, pageSize * page)));
// set the link header to the response
@@ -73,6 +81,7 @@ public class PaginatedResultsFilter implements ContainerResponseFilter {
private LinkHeader createLinkHeader(int page, int lastPage) {
// add link headers for paginated page hints
UriBuilder builder = getUriInfo().getRequestUriBuilder();
LinkHeader lh = new LinkHeader();
// add first + last page link headers
@@ -90,19 +99,21 @@ public class PaginatedResultsFilter implements ContainerResponseFilter {
}
/**
* Gets the current requested page, rounding down to max if larger than the max page number, and
* up if below 1.
* Gets the current requested page, rounding down to max if larger than the max
* page number, and up if below 1.
*
* @param listEntity list entity used to determine the number of pages present for current call.
* @return the current page number if set, the last page if greater, or 1 if not set or negative.
* @param listEntity list entity used to determine the number of pages present
* for current call.
* @return the current page number if set, the last page if greater, or 1 if not
* set or negative.
*/
private int getRequestedPage(List<?> listEntity) {
private int getRequestedPage(List<?> listEntity, int pageSize) {
MultivaluedMap<String, String> params = getUriInfo().getQueryParameters();
if (params.containsKey("page")) {
try {
int page = Integer.parseInt(params.getFirst("page"));
// use double cast int to allow ceil call to round up for pages
int maxPage = (int) Math.ceil((double) listEntity.size() / defaultPageSize.get());
int maxPage = (int) Math.ceil((double) listEntity.size() / pageSize);
// get page, with min of 1 and max of last page
return Math.min(Math.max(1, page), maxPage);
} catch (NumberFormatException e) {
@@ -114,11 +125,29 @@ public class PaginatedResultsFilter implements ContainerResponseFilter {
}
/**
* Builds an href for a paginated link using the BaseUri UriBuilder from the UriInfo object,
* replacing just the page query parameter.
* Allows for external bindings to affect the current page size, defaulting to
* the internal set configuration.
*
* @param responseContext
* @return
*/
private int getCurrentLimit(ContainerResponseContext responseContext) {
if (response.getHeaderNames().contains(MAX_PAGE_SIZE_HEADER)) {
try {
return Integer.parseInt(response.getHeader(MAX_PAGE_SIZE_HEADER));
} catch (NumberFormatException e) {
// page size isn't a number, allow to return default outside current scope
}
}
return defaultPageSize.get();
}
/**
* Builds an href for a paginated link using the BaseUri UriBuilder from the
* UriInfo object, replacing just the page query parameter.
*
* @param builder base URI builder from the UriInfo object.
* @param page the page to link to in the returned link
* @param page the page to link to in the returned link
* @return fully qualified HREF for the paginated results
*/
private String buildHref(UriBuilder builder, int page) {
@@ -132,9 +161,10 @@ public class PaginatedResultsFilter implements ContainerResponseFilter {
* Gets an int bound by the size of a list.
*
* @param list the list to bind the number by
* @param num the number to check for exceeding bounds.
* @return the passed number if its within the size of the given array, 0 if the number is
* negative, and the array size if greater than the maximum bounds.
* @param num the number to check for exceeding bounds.
* @return the passed number if its within the size of the given array, 0 if the
* number is negative, and the array size if greater than the maximum
* bounds.
*/
private int getArrayLimitedNumber(List<?> list, int num) {
if (num < 0) {
Loading