Wicket Stateless pagination
One of the first few things I have tackled while creating fabulously40 was stateless pagination. I wanted to keep all the public facing pages stateless to avoid session overhead. Wicket makes pagination brain dead simple at the cost of session use. I wanted to keep the simple programming model yet be completely stateless. Here is my solution to the problem.
Wicket a stateful framework, is actually really good at being stateless.
Example code on how to use it.
update: I would suggest using MixedParamHybridCodingStrategy with the stateless paging navigation to keep links clean.
add(dv = new StatelessDataView<Photo>("pics", getPhotoProvider(), amount, pageParams) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(final Item<Photo> arg0) {
}
});
// call back page class, page paramaters passed, the dataview and 12 pagination links.
add(new StatelessSimplePagingNavigator("nav", PhotosPage.class, pageParams, dataView, 12));
The dataview
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.IDataProvider;
public abstract class StatelessDataView<T> extends DataView<T> {
private static final long serialVersionUID = 1L;
private PageParameters pp;
@Override
protected boolean getStatelessHint() {
return true;
}
protected int getPageNumber(final String param) {
String numResult = param;
if(numResult.contains(".wicket-")) {
numResult = numResult.substring(0,numResult.indexOf(".wicket-"));
}
return Integer.valueOf(numResult);
}
public StatelessDataView(final String id, final IDataProvider dataProvider, final PageParameters pp) {
super(id, dataProvider);
this.pp = pp;
if (pp.getString(id) != null) {
int pageNum = getPageNumber(pp.getString(id));
if(pageNum != -1 && pageNum >= 0 && pageNum <= getPageCount()) {
setCurrentPage(getPageNumber(pp.getString(id)));
}
}
}
public StatelessDataView(final String id, final IDataProvider dataProvider, final int itemsPerPage,
final PageParameters pp) {
super(id, dataProvider, itemsPerPage);
this.pp = pp;
if (pp.getString(id) != null) {
int pageNum = getPageNumber(pp.getString(id));
if(pageNum != -1 && pageNum >= 0 && pageNum <= getPageCount()) {
setCurrentPage(getPageNumber(pp.getString(id)));
}
}
}
}
The paging navigator
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
public class StatelessSimplePagingNavigator extends SimplePagingNavigator {
private static final long serialVersionUID = 1L;
private Class clazz;
private PageParameters pp;
private int current_page;
private int page_count;
private String pageable_id;
private String anchor = null;
@Override
protected boolean getStatelessHint() {
return true;
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize) {
this(id, clazz, pp, pageable, viewsize, false);
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize, final boolean anchorSelf) {
super(id, pageable, viewsize, anchorSelf);
this.clazz = clazz;
this.pp = pp;
this.current_page = pageable.getCurrentPage();
this.page_count = pageable.getPageCount();
this.pageable_id = ((Component) pageable).getId();
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize, final String anchor) {
super(id, pageable, viewsize, false);
this.clazz = clazz;
this.pp = pp;
this.current_page = pageable.getCurrentPage();
this.page_count = pageable.getPageCount();
this.pageable_id = ((Component) pageable).getId();
this.anchor = anchor;
}
@Override
protected void onBeforeRender() {
super.onBeforeRender();
addOrReplace(newStatelessPagingNavigationLink("next", pageable_id, current_page, 1));
addOrReplace(newStatelessPagingNavigationLink("prev", pageable_id, current_page, -1));
}
@Override
protected PagingNavigation newNavigation(final IPageable pageable, final IPagingLabelProvider labelProvider) {
PagingNavigation pg = new PagingNavigation("navigation", pageable, labelProvider) {
private static final long serialVersionUID = 1L;
@Override
protected Link newPagingNavigationLink(final String id, final IPageable pageable, final int pageIndex) {
Component c = (Component) pageable;
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(c.getId(), String.valueOf(pageIndex));
BookmarkablePageLink<Object> lnk = new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
protected void onComponentTag(final ComponentTag arg0) {
super.onComponentTag(arg0);
if (anchor != null) {
if(arg0.getString("href") != null) {
String href = arg0.getString("href").toString();
String atag = anchor.contains("#") ? anchor : "#" + anchor;
arg0.put("href", href + atag);
}
}
}
@Override
public boolean isEnabled() {
return (current_page != pageIndex);
}
};
if (isAnchorSelf()) {
lnk.setAnchor(StatelessSimplePagingNavigator.this);
}
return lnk;
}
};
pg.setViewSize(getViewsize());
return pg;
}
@Override
protected Link<Object> newPagingNavigationIncrementLink(final String id, final IPageable pageable, final int increment) {
Component c = (Component) pageable;
final int page = Integer.valueOf(pp.getString(c.getId()));
pp.put(c.getId(), String.valueOf(page + increment));
return new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return (page + increment) >= 0;
}
};
}
protected Link<Object> newStatelessPagingNavigationLink(final String id, final String pageableId, final int currentPage,
final int increment) {
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(pageableId, String.valueOf(currentPage + increment));
return new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return (currentPage + increment) < page_count && (currentPage + increment) >= 0;
}
};
}
@Override
protected Link<Object> newPagingNavigationLink(final String id, final IPageable pageable, final int pageNumber) {
Component c = (Component) pageable;
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(c.getId(), String.valueOf(pageNumber));
return new BookmarkablePageLink(id, clazz, pp);
}
}
Which inherits from
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
public class SimplePagingNavigator extends PagingNavigator {
private static final long serialVersionUID = 1L;
private int viewsize = 0;
private boolean anchorSelf = false;
public SimplePagingNavigator(final String id, final IPageable pageable, final int viewsize) {
this(id, pageable, viewsize, false);
}
public SimplePagingNavigator(final String id, final IPageable pageable, final int viewsize, final boolean anchorSelf) {
super(id, pageable);
setOutputMarkupId(true);
this.setViewsize(viewsize);
this.setAnchorSelf(anchorSelf);
}
@Override
protected void onBeforeRender() {
if (get("navigation") != null) {
remove("navigation");
}
if (get("prev") != null) {
remove("prev");
}
if (get("next") != null) {
remove("next");
}
super.onBeforeRender();
if (get("first") != null) {
remove("first");
}
if (get("last") != null) {
remove("last");
}
if (getViewsize() != 0) {
getPagingNavigation().setViewSize(getViewsize());
}
}
@Override
protected PagingNavigation newNavigation(final IPageable pageable, final IPagingLabelProvider labelProvider) {
PagingNavigation pg = new PagingNavigation("navigation", pageable, labelProvider) {
private static final long serialVersionUID = 1L;
@Override
protected Link newPagingNavigationLink(final String id, final IPageable pageable, final int pageIndex) {
Link lnk = (Link) super.newPagingNavigationLink(id, pageable, pageIndex);
if (isAnchorSelf()) {
lnk.setAnchor(SimplePagingNavigator.this);
}
return lnk;
}
};
pg.setViewSize(getViewsize());
return pg;
}
@Override
public boolean isVisible() {
if (getPageable() != null) {
return (getPageable().getPageCount() > 1);
}
return true;
}
public void setAnchorSelf(final boolean anchorSelf) {
this.anchorSelf = anchorSelf;
}
public boolean isAnchorSelf() {
return anchorSelf;
}
public void setViewsize(final int viewsize) {
this.viewsize = viewsize;
}
public int getViewsize() {
return viewsize;
}
}



This is great, I’ll use it as base for my stateless naviagtion.
Btw, as of Wicket 1.4.0 “super.onBeforeRender();” should move to the bottom of onBeforeRender(). Else it will throw an exception.
I am still on 1.4.0-beta7 and it works correctly as is.
Can I use a GridView instead DataView?