JCR: Versioning and versionability

JCR – the Java Content Repository – uses a versioning mechanism to create a history of content’s changes. Versioning means, that a node’s state can be saved for future recovery. Such a saved state is called a version. Saving a state is called checking in.

This article is based on JSR-170 (JCR 1.0) and explains, how this mechanism of versioning works and what has to be done to make it work.

Which implementations of JCR do support versioning?

At least any implementation I know (Day CRX and Apache Jackrabbit) do support versioning. If your repository doesn’t report versionability in its documentation, you can check yourself, wether or not it supporty versioning. Just take a look inside jcr:system/jcr:nodeTypes of your repository and search for a node type called mix:versionable. If it exists, then versioning is supported, if is is not, it is not.

Is the versioning mechanism suppressible?

JSR-170 – JCR 1.0 – says:
“Support for versioning is an optional feature. The versioning system is built on top of the system of workspaces and referenceable nodes described above.”
This means, that you do not have to use this versioning feature if you don’t need it. I recommend not using it if not required to speed up the repository.

Where do the versions go?

Version histories and their version will be stored in the so called version storage. Each repository has its own version storage. The version storage is exposed in each workspace as a protected subtree below /jcr:system/jcr:versionStorage.

Further internals

Notice, that mix:versionable inherits from mix:referenceable which means, that any versionable node will automatically be referenceable by its UUID. This is because the UUID is used to identify correspondig nodes and its versions within different workspaces and version histories. The internal structure looks as show in the following picture¹.
Relations between JCR workspaces and the version storage


You see the lighter grey circles, this are nodes, typed with jcr:versionable. Numbers inside the circles show the UUID of those nodes. If no number is shown, then this node is not typed with jcr:referenceable, otherwise it is. You can see, that nodes with a unique UUID can occur in several workspaces – they correspond with each other.
Refer to JSR-170, chapter 4.11.1.1 to learn even more about the internals and details.

Hands on

The following class shows basically how to access a node’s version history.


package de.mhsdev.jcr.client;

import java.net.MalformedURLException;

import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.naming.NamingException;

import org.apache.jackrabbit.rmi.repository.URLRemoteRepository;

/**
 * Looks up the version history of a given node.
 *
 * @author mhavers
 *
 */
public class JCRVersionHistoryAccess {

	/**
	 * @param args
	 * @throws MalformedURLException
	 * @throws LoginException
	 * @throws NoSuchWorkspaceException
	 * @throws RepositoryException
	 */
	public static void main(String[] args) throws MalformedURLException,
			LoginException, NoSuchWorkspaceException, RepositoryException {
		// Get the Repository object
		Repository repository = new URLRemoteRepository(
				"http://localhost:8081/rmi");

		// Get a Credentials object
		Credentials credentials = new SimpleCredentials("repouser", "repouser".toCharArray());

		// Get a Session
		Session mySession = repository.login(credentials, "default");

		// Select a versionable node
		Node node = mySession.getNode("/org");

		VersionHistory versionHistory = node.getVersionHistory();
		VersionIterator versionIterator = versionHistory.getAllVersions();

		versionIterator.skip(1);
		while (versionIterator.hasNext()) {
			Version version = versionIterator.nextVersion();
			NodeIterator nodeIterator = version.getNodes();

			while (nodeIterator.hasNext()) {
				Node nv = nodeIterator.nextNode();
				System.out.println("Version: " + version.getCreated().getTime());
				System.out.println(nv.getName());
				//System.out.println(...);
			}
		}

	}

}

Footnotes

¹Image taken from JSR-170, page 38

References and resources

Leave a Reply


4 + four =