Skip to content

Commit 33fee76

Browse files
author
eugenp
committed
pagination work
1 parent 1af3eb4 commit 33fee76

File tree

4 files changed

+182
-1
lines changed

4 files changed

+182
-1
lines changed

spring-security-rest-full/src/main/java/org/baeldung/web/controller/FooController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.baeldung.persistence.model.Foo;
1010
import org.baeldung.persistence.service.IFooService;
1111
import org.baeldung.web.exception.MyResourceNotFoundException;
12+
import org.baeldung.web.hateoas.PaginatedResultsRetrievedEvent;
1213
import org.baeldung.web.util.LinkUtil;
1314
import org.baeldung.web.util.ResourceCreated;
1415
import org.baeldung.web.util.RestPreconditions;
@@ -72,7 +73,7 @@ public List<Foo> findPaginated(@RequestParam("page") final int page, @RequestPar
7273
if (page > resultPage.getTotalPages()) {
7374
throw new MyResourceNotFoundException();
7475
}
75-
// eventPublisher.publishEvent(new PaginatedResultsRetrievedEvent<Foo>(Foo.class, uriBuilder, response, page, resultPage.getTotalPages(), size));
76+
eventPublisher.publishEvent(new PaginatedResultsRetrievedEvent<Foo>(Foo.class, uriBuilder, response, page, resultPage.getTotalPages(), size));
7677

7778
return resultPage.getContent();
7879
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package org.baeldung.web.hateoas;
2+
3+
import javax.servlet.http.HttpServletResponse;
4+
5+
import org.baeldung.web.util.LinkUtil;
6+
import org.springframework.context.ApplicationListener;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.web.util.UriComponentsBuilder;
9+
10+
import com.google.common.base.Preconditions;
11+
import com.google.common.net.HttpHeaders;
12+
13+
@SuppressWarnings({ "rawtypes" })
14+
@Component
15+
class PaginatedResultsRetrievedDiscoverabilityListener implements ApplicationListener<PaginatedResultsRetrievedEvent> {
16+
17+
private static final String PAGE = "page";
18+
19+
public PaginatedResultsRetrievedDiscoverabilityListener() {
20+
super();
21+
}
22+
23+
// API
24+
25+
@Override
26+
public final void onApplicationEvent(final PaginatedResultsRetrievedEvent ev) {
27+
Preconditions.checkNotNull(ev);
28+
29+
addLinkHeaderOnPagedResourceRetrieval(ev.getUriBuilder(), ev.getResponse(), ev.getClazz(), ev.getPage(), ev.getTotalPages(), ev.getPageSize());
30+
}
31+
32+
// - note: at this point, the URI is transformed into plural (added `s`) in a hardcoded way - this will change in the future
33+
final void addLinkHeaderOnPagedResourceRetrieval(final UriComponentsBuilder uriBuilder, final HttpServletResponse response, final Class clazz, final int page, final int totalPages, final int pageSize) {
34+
plural(uriBuilder, clazz);
35+
36+
final StringBuilder linkHeader = new StringBuilder();
37+
if (hasNextPage(page, totalPages)) {
38+
final String uriForNextPage = constructNextPageUri(uriBuilder, page, pageSize);
39+
linkHeader.append(LinkUtil.createLinkHeader(uriForNextPage, LinkUtil.REL_NEXT));
40+
}
41+
if (hasPreviousPage(page)) {
42+
final String uriForPrevPage = constructPrevPageUri(uriBuilder, page, pageSize);
43+
appendCommaIfNecessary(linkHeader);
44+
linkHeader.append(LinkUtil.createLinkHeader(uriForPrevPage, LinkUtil.REL_PREV));
45+
}
46+
if (hasFirstPage(page)) {
47+
final String uriForFirstPage = constructFirstPageUri(uriBuilder, pageSize);
48+
appendCommaIfNecessary(linkHeader);
49+
linkHeader.append(LinkUtil.createLinkHeader(uriForFirstPage, LinkUtil.REL_FIRST));
50+
}
51+
if (hasLastPage(page, totalPages)) {
52+
final String uriForLastPage = constructLastPageUri(uriBuilder, totalPages, pageSize);
53+
appendCommaIfNecessary(linkHeader);
54+
linkHeader.append(LinkUtil.createLinkHeader(uriForLastPage, LinkUtil.REL_LAST));
55+
}
56+
57+
if (linkHeader.length() > 0) {
58+
response.addHeader(HttpHeaders.LINK, linkHeader.toString());
59+
}
60+
}
61+
62+
final String constructNextPageUri(final UriComponentsBuilder uriBuilder, final int page, final int size) {
63+
return uriBuilder.replaceQueryParam(PAGE, page + 1).replaceQueryParam("size", size).build().encode().toUriString();
64+
}
65+
66+
final String constructPrevPageUri(final UriComponentsBuilder uriBuilder, final int page, final int size) {
67+
return uriBuilder.replaceQueryParam(PAGE, page - 1).replaceQueryParam("size", size).build().encode().toUriString();
68+
}
69+
70+
final String constructFirstPageUri(final UriComponentsBuilder uriBuilder, final int size) {
71+
return uriBuilder.replaceQueryParam(PAGE, 0).replaceQueryParam("size", size).build().encode().toUriString();
72+
}
73+
74+
final String constructLastPageUri(final UriComponentsBuilder uriBuilder, final int totalPages, final int size) {
75+
return uriBuilder.replaceQueryParam(PAGE, totalPages).replaceQueryParam("size", size).build().encode().toUriString();
76+
}
77+
78+
final boolean hasNextPage(final int page, final int totalPages) {
79+
return page < totalPages - 1;
80+
}
81+
82+
final boolean hasPreviousPage(final int page) {
83+
return page > 0;
84+
}
85+
86+
final boolean hasFirstPage(final int page) {
87+
return hasPreviousPage(page);
88+
}
89+
90+
final boolean hasLastPage(final int page, final int totalPages) {
91+
return totalPages > 1 && hasNextPage(page, totalPages);
92+
}
93+
94+
final void appendCommaIfNecessary(final StringBuilder linkHeader) {
95+
if (linkHeader.length() > 0) {
96+
linkHeader.append(", ");
97+
}
98+
}
99+
100+
// template
101+
102+
protected void plural(final UriComponentsBuilder uriBuilder, final Class clazz) {
103+
final String resourceName = clazz.getSimpleName() + "s";
104+
uriBuilder.path("/" + resourceName);
105+
}
106+
107+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.baeldung.web.hateoas;
2+
3+
import java.io.Serializable;
4+
5+
import javax.servlet.http.HttpServletResponse;
6+
7+
import org.springframework.context.ApplicationEvent;
8+
import org.springframework.web.util.UriComponentsBuilder;
9+
10+
/**
11+
* Event that is fired when a paginated search is performed.
12+
* <p/>
13+
* This event object contains all the information needed to create the URL for the paginated results
14+
*
15+
* @param <T>
16+
* Type of the result that is being handled (commonly Entities).
17+
*/
18+
public final class PaginatedResultsRetrievedEvent<T extends Serializable> extends ApplicationEvent {
19+
private final UriComponentsBuilder uriBuilder;
20+
private final HttpServletResponse response;
21+
private final int page;
22+
private final int totalPages;
23+
private final int pageSize;
24+
25+
public PaginatedResultsRetrievedEvent(final Class<T> clazz, final UriComponentsBuilder uriBuilderToSet, final HttpServletResponse responseToSet, final int pageToSet, final int totalPagesToSet, final int pageSizeToSet) {
26+
super(clazz);
27+
28+
uriBuilder = uriBuilderToSet;
29+
response = responseToSet;
30+
page = pageToSet;
31+
totalPages = totalPagesToSet;
32+
pageSize = pageSizeToSet;
33+
}
34+
35+
// API
36+
37+
public final UriComponentsBuilder getUriBuilder() {
38+
return uriBuilder;
39+
}
40+
41+
public final HttpServletResponse getResponse() {
42+
return response;
43+
}
44+
45+
public final int getPage() {
46+
return page;
47+
}
48+
49+
public final int getTotalPages() {
50+
return totalPages;
51+
}
52+
53+
public final int getPageSize() {
54+
return pageSize;
55+
}
56+
57+
/**
58+
* The object on which the Event initially occurred.
59+
*
60+
* @return The object on which the Event initially occurred.
61+
*/
62+
@SuppressWarnings("unchecked")
63+
public final Class<T> getClazz() {
64+
return (Class<T>) getSource();
65+
}
66+
67+
}

spring-security-rest-full/src/main/java/org/baeldung/web/util/LinkUtil.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
*/
88
public final class LinkUtil {
99

10+
public static final String REL_COLLECTION = "collection";
11+
public static final String REL_NEXT = "next";
12+
public static final String REL_PREV = "prev";
13+
public static final String REL_FIRST = "first";
14+
public static final String REL_LAST = "last";
15+
1016
private LinkUtil() {
1117
throw new AssertionError();
1218
}

0 commit comments

Comments
 (0)