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.


StatelessDataView dv;
add(dv = new StatelessDataView("pics", getPhotoProvider(), amount, pageParams) {
			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(final Item 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


package com.base.components;

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 extends DataView {
	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


package com.base.components;

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 lnk = new BookmarkablePageLink(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 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(id, clazz, pp) {
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isVisible() {
				return (page + increment) >= 0;
			}
		};
	}

	protected Link 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(id, clazz, pp) {
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isVisible() {
				return (currentPage + increment) < page_count && (currentPage + increment) >= 0;
			}
		};
	}

	@Override
	protected Link 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


package com.base.components;

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;
	}

}



 
	Previous
    
		  5
    
    Next
  

3 comments so far

  1. Roman says:

    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.

  2. victori says:

    I am still on 1.4.0-beta7 and it works correctly as is.

  3. decebal says:

    Can I use a GridView instead DataView?

Leave a Reply