Tuesday, February 23, 2010

Integrating Wicket and JCR

As requested I will describe briefly my take at integrating Wicket with JCR (Jackrabbit).
It is pretty straightforward.

Open/close JCR repository
My override of org.apache.wicket.protocol.http.WebApplication.init() looks up a javax.jcr.RepositoryFactory implementation via a java.util.ServiceLoader. Then I use this factory to open the repository. I store a reference to the repository in a field of my application class.
My override of producat.wicket.Application.onDestroy() closes the repository via org.apache.jackrabbit.api.JackrabbitRepository.shutdown().

Open/close JCR session
I try to keep a JCR session open only for the duration of a HTTP request.
To achieve this my application class overrides org.apache.wicket.protocol.http.WebApplication.newRequestCycle(Request, Response) to provide a custom request cycle implementation.

    public RequestCycle newRequestCycle(Request request, Response response) {
        return new MyRequestCycle(this, (WebRequest) request, response);

My implementation opens JCR session on demand and closes it at the end of the request cycle.
It looks similar to this class.

public class MyRequestCycle extends WebRequestCycle {

    private Session session;

    public MyRequestCycle(WebApplication application, WebRequest request,
            Response response) {
        super(application, request, response);

    protected void onEndRequest() {
        if (session != null) {
            session = null;

    public Session getJcrSession() {
        if (session == null) {
            Repository repository = ((MyApplication) getApplication()).getRepository();
            session = repository.login();
        return session;

    public static MyRequestCycle get() {
        return (MyRequestCycle) RequestCycle.get();

Then I use MyRequestCycle.get().getJcrSession() in my page classes to access the repository.
I save the session explicitly in every place where I make changes in the repository. Initially I thought of saving it automatically at the end of the session, but then it turned out to be difficult to handle decently save errors.

Data models and URLs
I try to avoid HTTP session as much as possible so I use BookmarkablePageLink's, passing the UUID of respective JCR node in PageParameters.
Since JCR nodes are not serializable, my models store only the node UUID and load it on demand via javax.jcr.Session.getNodeByIdentifier(String) - kind of LoadableDetachableModel.