Every time when a task comes to page some big resultset in JSP you have to solve the same problem again and again - how to print correct links with all search criteria and a page index and possibly in a most reusable way. Recently I have finally come up with a simple tag which does this with minimal efforts. The first and foremost - the data transfer object which we will need for the pager to work:
package com.anydoby.dao;
import java.util.List;
/**
* @author SergeyZ
*/
public class ListPage<T> {
private int page;
private List<T> data;
private int total;
private int maxPerPage;
public ListPage(List<T> data, int page, int total, int maxPerPage) {
this.data = data;
this.page = page;
this.total = total;
this.maxPerPage = maxPerPage;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getMaxPerPage() {
return maxPerPage;
}
public void setMaxPerPage(int maxPerPage) {
this.maxPerPage = maxPerPage;
}
}This is a trivial list wrapper which we will need to pass to the JSP page: data - a results list, page - current page index (starting with 0), total - total amount of results in a resultset (not less than the total number of entries in data), maxPerPage - max results to show per page.
And here is the tag which outputs the links to result pages:
package com.anydoby.utils;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import com.anydoby.spingmvc.dao.ListPage;
/**
* @author SergeyZ
*/
public class PagerTag extends SimpleTagSupport {
@SuppressWarnings("unchecked")
private ListPage page;
private String pageParam;
public String getPageParam() {
return pageParam;
}
public void setPageParam(String pageParam) {
this.pageParam = pageParam;
}
@SuppressWarnings("unchecked")
public ListPage getPage() {
return page;
}
@SuppressWarnings("unchecked")
public void setPage(ListPage page) {
this.page = page;
}
@Override
public void doTag() throws JspException, IOException {
StringBuffer requestURL = getRequestUrl();
int pagesTotal = page.getTotal() / page.getMaxPerPage();
if (page.getTotal() % page.getMaxPerPage() != 0) {
pagesTotal++;
}
if (pagesTotal > 1) {
int startOfPageParam = requestURL.indexOf(pageParam + "=");
if (startOfPageParam != -1) {
int endOfPageParam = requestURL.indexOf("&", startOfPageParam + pageParam.length());
if (endOfPageParam == -1) {
endOfPageParam = requestURL.length();
}
requestURL.delete(startOfPageParam, endOfPageParam);
}
char lastQueryChar = requestURL.charAt(requestURL.length() - 1);
if (lastQueryChar != '?' && lastQueryChar != '&') {
requestURL.append('&');
}
requestURL.append(pageParam).append('=');
String url = requestURL.toString();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pagesTotal; i++) {
if (page.getPage() != i) {
sb.append("<a href=\"").append(url).append(i).append("\">");
sb.append(i + 1);
sb.append("</a> ");
} else {
sb.append("<b>");
sb.append(i + 1);
sb.append("</b> ");
}
}
getJspContext().getOut().append(sb.toString());
}
}
private StringBuffer getRequestUrl() {
StringBuffer construct = construct(getJspContext(), "javax.servlet.forward.");
if (construct == null) {
construct = construct(getJspContext(), "javax.servlet.include.");
}
if (construct == null) {
PageContext ctx = (PageContext) getJspContext();
HttpServletRequest request = (HttpServletRequest) ctx.getRequest();
construct = request.getRequestURL();
construct.append('?');
if (request.getQueryString() != null) {
construct.append(request.getQueryString());
}
}
return construct;
}
private StringBuffer construct(JspContext context, String prefix) {
Object uri = context.getAttribute(prefix + "request_uri", PageContext.REQUEST_SCOPE);
if (uri != null) {
Object ctxPath = context.getAttribute(prefix + "context_path", PageContext.REQUEST_SCOPE);
Object servletPath = context.getAttribute(prefix + "servlet_path", PageContext.REQUEST_SCOPE);
Object pathInfo = context.getAttribute(prefix + "path_info", PageContext.REQUEST_SCOPE);
Object query = context.getAttribute(prefix + "query_string", PageContext.REQUEST_SCOPE);
StringBuffer buffer = new StringBuffer();
buffer.append(ctxPath).append(servletPath);
if (pathInfo != null) {
buffer.append(pathInfo);
}
buffer.append('?');
if (query != null) {
buffer.append(query);
}
return buffer;
}
return null;
}
}The code ensures that the tag automatically determines which url the user is on (it can be hidden if you use for example dispatch or include, and you obviously use it with Spring MVC or Struts).
And here is the taglib.tld, which you will place in the WEB-INF/tags:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>r</short-name>
<uri>http://anydoby.com/simpletags</uri>
<tag>
<description>
Prints a simple pager
</description>
<name>pager</name>
<tag-class>com.anydoby.utils.PagerTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>page</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>com.anydoby.dao.ListPage</type>
</attribute>
<attribute>
<name>pageParam</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>on a JSP page this tag will look like this:
<%@ taglib prefix="r" uri="http://anydoby.com/simpletags" %>
... results displayed ...
<r:pager page="${results}" pageParam="page"/>The tag constructs links to current url (keeping all request parameters) by adding a page parameter (chose any name you like), with the number of next result page.
First published on the old anydoby.com blog on 2008-04-25.