The Artima Developer Community
This article is sponsored by the Java Community Process.

Leading-Edge Java
Catch Jackrabbit and the Java Content Repository API
by Frank Sommers
June 3, 2005

<<  Page 3 of 3

Advertisement

Blogging with Jackrabbit

To see for ourselves how the JCR API helps in real-world application development, I decided to implement a simple blog management app with Jackrabbit. We were especially interested to compare a content-repository approach with a Hibernate-based solution, since both methods claim to reduce application code complexity.

A blogging application's data model is simple: it typically consists of a set of blog entries, and each blog entry may refer to comments made about that entry. Each comment, in turn, may have zero or more further comments, entered as replies in the discussion thread.

Such a hierarchical data model is well-suited to a repository's tree structure. A coincidence? Perhaps, but many other enterprise tasks are also amenable to hierarchies, such as inventory tracking, user administration, and order management.

In a relational model, defining such a hierarchical structure involves creating self- referencing relations. In the JCR repository model, however, it merely requires defining a new node type, blogentry. That type might have a few properties, such as a title, an author, a date, and the blog's text. In addition, the blogentry type may have a set of attachments as well as comments. The attachments may be binary content, such as images or documents.

The following code snippet authenticates a user into a repository, retrieves the repository's root node (assuming the repository contains a single workplace), creates a new node representing a blog entry, and adds that entry into the repository's root node:

//A repository config file. 
String configFile = "repotest/repository.xml";
//Filesystem for Jackrabbit repository 
String repHomeDir = "repotest";

//Register the repository in JNDI
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
   
"org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
env.put(Context.PROVIDER_URL, "localhost");
InitialContext ctx = new InitialContext(env);
RegistryHelper.registerRepository(ctx, 
       "repo", 
        configFile, 
        repHomeDir, 
        true);

//Obtain the repository through a JNDI lookup 
Repository r = (Repository)ctx.lookup("repo");

//Create a new repository session, after authenticating 
Session session = 
   r.login(new SimpleCredentials("userid", "".toCharArray()), null);

//Obtain the root node 
Node rn = session.getRootNode();

//Create and add a new blog node. The node's type will be "blog". 
Node n = rn.addNode("blog");
n.setProperty("blogtitle",  
  new StringValue("Chasing Jackrabbit article"));
n.setProperty("blogauthor", new StringValue("Joe Blogger"));
n.setProperty("blogdate", new DateValue(Calendar.getInstance()));
n.setProperty("blogtext", 
    new StringValue("JCR is an interesting API to lo learn."));
session.save();

We can subsequently look up the nodes in the repository via any of the query mechanisms supported by a repository. JCR compliant repositories must support at least XPATH, but Jackrabbit also supports SQL. The following code snippet finds and lists all blog entries written by "Joe Blogger:"

Workspace ws = session.getWorkspace();
QueryManager qm = ws.getQueryManager();

//Specify a query using the XPATH query language
Query q = 
   qm.createQuery("//blog[@blogauthor = 'Joe Blogger']", Query.XPATH);
QueryResult res = q.execute();

//Obtain a node iterator 
NodeIterator it = res.getNodes();

while (it.hasNext()) {

   Node n = it.nextNode();
   Property prop = n.getProperty("blogtitle");
   System.out.println("Found blog entry with title: " 
      + prop.getString());
}

Adding a comment to a blog entry involves finding the blog entry's node, and adding a child node representing the comment. The code is so simple, that we leave it as an exercise to the reader.

The following code example will instead demonstrate how to make a blog entry versionable. Versions can allow an application to let a user edit a blog entry before posting it, and to revert to previous versions, if needed. Making a node versionable requires adding the mix:versionable mixin type to the node:

Node n = rn.addNode("versionedblog");

n.addMixin("mix:versionable");
n.setProperty("blogtitle",  new StringValue("Versioned rabbit") );
n.setProperty("blogauthor", new StringValue("Joe Blogger"));
n.setProperty("blogdate", new DateValue(Calendar.getInstance()));
n.setProperty("blogtext", 
   new StringValue("JCR is an interesting API to lo learn."));

session.save();

By adding the mixin node type mix:versionable to the node, a version history is created when invoking session.save(). Having saved an initial version, the blog entry can subsequently be retrieved, edited, and a new version saved:

Query q = 
   qm.createQuery("//versionedblog[
    @blogauthor = 'Joe Blogger' and 
     @blogtitle = 'Versioned rabbit']", Query.XPATH);

QueryResult res = q.execute();

NodeIterator it = res.getNodes();

if (it.hasNext()) {

   Node n = it.nextNode();

   //Check out the current node 
   n.checkout();

   //Set a new property value for blogtext 
   n.setProperty("blogtext", new StringValue("Edited blog text"));

   //Save the new version. 
   session.save();

   //Check the new vesion in, resulting in a new item being added 
   //the the node's version history 
   n.checkin();
}

Jackrabbit tracks versioning through a special versioning data store. That data store works much like a regular workspace, and contains nodes that are of a special version history type. Reusing the regular Node object model for version metadata is a good example of how a repository's information model blurs the distinction between metadata and "regular" data.

For every versionable node, there is a hierarchy of version nodes, consisting of at least a single element denoting the current version. When the repository saves a new version, the old version's node data is added as a "frozen" child node of the versioning node. The following code example shows how we can retrieve all versions of a blog entry and display the changes occurring to the blogtext property:

//Obtain the blog node's version history
VersionHistory history = n.getVersionHistory();
VersionIterator ito = history.getAllVersions();

while (ito.hasNext()) {

   Version v = ito.nextVersion();

   //The version node will have a "frozen" child node that contains 
   //the corresponding version's node data
   NodeIterator it = v.getNodes("jcr:frozenNode");

   if (it.hasNext()) {

     Node no = it.nextNode();
     System.out.println("Version saved on " + 
        v.getCreated().getTime().toString() + 
        " has the following message: " + 
        no.getProperty("blogtext").getString());
   }
}

Conclusion

Perhaps the biggest benefit of the JCR API is that it doesn't try to persist Java objects, and cares little about an application's object model. Instead, the JCR API focuses entirely on the content, or data, of an application. While this may at first sound like a step backwards, it actually creates a very clean and easy-to-use programming model with a sharp focus on a handful of data management tasks, such as versioning.

That is almost the exact opposite of object-relational or Java persistence APIs, which try to map an application's object model to some persistence schema, such as a relational database. The JCR API also differs from object-oriented databases, where the persistent representation of data is expressed in terms of the programming language's type system, and matches an application's object model.

Paradoxically, a big payoff in having a completely separate persistent data model and an application object model is the same relational databases first offered almost three decades ago: It allows the reuse of persistent schema from multiple application domains. For instance, having a blogging data repository available on the Web could allow a diverse set of applications to access that repository. Since nothing in that repository information model would be tied to a specific programming language, or even a specific kind of application design, such a repository would be analogous to a huge data "superstore" accessible to anyone permitted by the repository's security policies.

Such public data stores have been dreamt up before, the most the significant effort to date being the ebXML Registry [12]. However, while ebXML focuses on specifying metadata types for a variety of application domains, public repositories can in theory hold any information model.

Still, for a public blogging "superstore" to have real value to application developers, for instance, some agreement on the node types supporting a blogging data model would be helpful. The repository community has so far avoided the politically sensitive pitfalls of trying to initiate agreement about such information models. The jury is still out whether truly universal data "superstores" can emerge in the absence of such shared data models, or if they will remain a dream befitting Utopia.

Talk back!

Have an opinion about the content repositories? Discuss this article in the Articles Forum topic, Catch Jackrabbit and the Java Content Repository API.

Resources

[1] Thomas More's Utopia
http://www.ub.uni-bielefeld.de/diglib/more/utopia/

http://ota.ahds.ac.uk/texts/2080.html

[2] JCP home page for JSR 170, the Java Content Repository API:
http://www.jcp.org/en/jsr/detail?id=170

[3] The Java Community Process:
http://www.jcp.org/

[4] Day Software:
http://www.day.com/en.html

[5] Apache's Jackrabbit:
http://incubator.apache.org/jackrabbit/

[6] "Repositories and Object Oriented Databases," by Philip A. Bernstein:
http://www.sigmod.org/record/issues/9803/bernstein.ps

[7] Microsoft Repository:
http://msdn.microsoft.com/library/default.asp?url=...

[8] CVS:
http://www.gnu.org/software/cvs/

Subversion:
http://subversion.tigris.org/

[9] Sun's ZFS filesystem:
http://www.sun.com/2004-0914/feature/

[10] The Microsoft Windows WINFS file system:
http://msdn.microsoft.com/data/winfs/

[11] JCP 2.6:
http://java.sun.com/developer/technicalArticles/jcp/

[12] ebXML:
http://www.ebxml.org/

[See also] Document repositories:
http://www.manageability.org/blog/stuff/open-source-document-repository/view

A zip file containing the code example from this article (this is a large file because it also includes the JAR files you will need to run the example):
http://www.artima.com/lejava/articles/examples/rabbitblog.zip

About the author

Frank Sommers is a Senior Editor with Artima Developer. He also serves as chief editor of the Web zine ClusterComputing.org, the IEEE Technical Committee on Scalable Computing's newsletter, and is an elected member of the Jini Community's Technical Advisory Committee. Prior to joining Artima, Frank wrote the Jiniology and Web services columns for JavaWorld.

<<  Page 3 of 3


This article is sponsored by the Java Community Process.

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us