Sitevision JCR Model API

The Sitevision data is exposed in a tree model that can be accessed using the standard defined in JSR 283 - Java Content Repository.

The Java Content Repository specification (JSR 283) can be downloaded from here.

Sitevision currently supports level 1 of the JCR standard, which means that the model is read-only. The model data can be read but not altered.

There are also some deviations/simplifications that prohibits the Sitevision implementation of JCR to be fully JSR 283 compliant:

  • NodeType and PropertyDefinition handling is simplified
  • Parent / Path is not supported for all nodes and properties (most noteably sv:portlet)

For more information about what is implemented and what is not see the javax.jcr classes in the Sitevision Public API Javadoc. Features that are not implemented are denoted "Unsupported operation".

For fast and effective access of Sitevision data you must have thorough knowledge of the data model tree and knowledge about the properties available for each node type. To access the data in the model, just define your point of origin and extract the corresponding node from the Session or use Sitevision-specific utilities. With this node as base it is possible to traverse the model tree and retrieve information about nodes and their properties using node and property iterators.

Core JCR concepts

All model data is part of a single tree and the topmost object is called the root node. Each entry in the tree is a javax.jcr.Node. A node has a parent and potentially one or more children.

// Get the parent of 'aNode'
Node parent = aNode.getParent();

// Get an iterator that provides potential children of 'aNode'
NodeIterator childIterator = aNode.getNodes();

Each Node has a identifier, a name and a primary node type. A node has a virtual path based on the names of all its parent nodes.

// Get the unique Node identifier (can be used for direct lookups)
String identifier = aNode.getIdentifier();

// Get the Node name (will be part of the 'path' in path lookups)
String name = aNode.getName();

// Get the primary node type
String nodeTypeName = aNode.getPrimaryNodeType().getName();

The identifier can be used for direkt lookup of a Node, regardless of where it resides in the model tree. The path of a node can also be used for direct lookup. Identifier lookups is typically executed via the javax.jcr.Session object. The session also holds the reference to the root node.

// Get the root Node
Node root = aSession.getRootNode();

// First and only root child is always the website Node
Node theSiteNode = root.getNodes().nextNode();

// Lookup the Node with identifier '4.12121212'
Node aNode = aSession.getNodeByIdentifier("4.12121212");

A Node typically has several javax.jcr.Property that is accessible via their property name. A property is somewhat dynamic since the actual javax.jcr.Value of the property can be resolved via one or several of the type-specific methods supported for actual javax.jcr.PropertyType.

// Get the boolean value of the 'published' property of 'aNode'
Property publishedProperty = aNode.getProperty("published");
Value value = publishedProperty.getValue();
boolean published = value.getBoolean();

// Get the actual values of some properties of 'aNode'
String displayName = aNode.getProperty("displayName").getString();
boolean active = aNode.getProperty("active").getBoolean();
Calendar published = aNode.getProperty("published").getDate();
long publishedTimestamp = aNode.getProperty("published").getLong();
double points = aNode.getProperty("points").getDouble();

// Some property values can also be resolved as a Node
Node template = aNode.getProperty("template").getNode();

A property can also be multi-valued.

// Get the value(-s) of a potentially multi-valued property of 'aNode'
Property commentsProperty = aNode.getProperty("comments");
if (commentsProperty.isMultiple()) {
   Value[] values = commentsProperty.getValues();
   for (Value value : values) {
      String comment = value.getString();
      ...
   }
} else {
   String comment = commentsProperty.getString();
}

According to the JCR specification - null properties are forbidden! If you try to get a property and it doesn't exist or if you try to get a property value via the "wrong" type method, an Exception will be thrown!

In fact, almost ALL methods you invoke on a JCR object potentially can throw a javax.jcr.RepositoryException of some kind!

See Node types for information about potentially available properties for each type of Node.

Accessing the javax.jcr.Session

The main entrypoint of the JCR Model API is the Session interface. The JCR Session instance is always available as a request attribute of current javax.portlet.PortletRequest:

// Get the JCR Session instance from the portlet request
Session session = (Session) aPortletRequest.getAttribute("sitevision.jcr.session");

Accessing the JCR Session in Velocity

The JCR Session instance is available as $jcrSession in all JCR capable Sitevision modules that exposes a custom Velocity template.

## The JCR Session instance is available on the Velocity context as 'jcrSession'
$jcrSession

Accessing the JCR Session in (server-side) JavaScript

The JCR Session is annotated with the @Requireable annotation so it can be accessed directly via the require function in server-side Javascripts.

// Get a JCR Session instance
var jcrSession = require('Session');

Permissions

All data in the JCR Model requires exactly the same permission as in the Sitevision editor in order to access it. For example: to access a non-public page in the JCR Model, current user (the one that is executing the actual code) must be authenticated and have READ permission on that specific page.

How to work with JCR in practice

The JCR specification leaves absolutely no room for mistakes! Every operation you do can potentially throw a checked exception. This can be devastating if the code is written in Velocity since it lacks any possibilities to catch and handle any potential exceptions.

Working with the "raw" JCR model tree is cumbersome and requires that the developer has profound knowledge of the tree, the type of nodes that can occur in the tree (and where), the names of all properties different types of nodes can have etc.

Our recommendation is that you treat JCR as "data only" and do all operations on that data via a more forgiving utility of the Sitevision Utility API instead!

Node lookup

Use ResourceLocatorUtil of the utility API instead of the javax.jcr.Session to lookup Nodes

// ResourceLocatorUtil does NOT throw Exception but might return null 
Node aNodeOrNull = aResourceLocatorUtil.getNodeByIdentifier("4.12121212");

Properties / value extraction

Use PropertyUtil instead of using the raw javax.jcr.Property and javax.jcr.Value JCR objects to get property values

// PropertyUtil does NOT throw Exception but might return null 
String nameOrNull = aPropertyUtil.getString(aNode, "displayName");

// PropertyUtil also allows for default value
String nameOrEmpty = aPropertyUtil.getString(aNode, "displayName", "");

// PropertyUtil can also return the value HTML/XML escaped (i.e. to prevent XSS)
String nameOrNull = aPropertyUtil.getStringEscaped(aNode, "displayName");
String nameOrEmpty = aPropertyUtil.getStringEscaped(aNode, "displayName", "");

In server-side JavaScript contexts, you would typically prefer the Properties utility over PropertyUtil.

Properties combines the functionality of ResourceLocatorUtil and PropertyUtil and can extract multiple properties in one go.

// Get the Properties utility
var properties = require('Properties');
   ...

// Get the displayName property value (raw or XML/HTML escaped) from aNode
name = properties.get(aNode, 'displayName');
nameEscaped = properties.getEscaped(aNode, 'displayName');

// Properties can implicitly resolve the Node by identifier (as ResourceLocatorUtil)
name = properties.get('4.12121212', 'displayName');
nameEscaped = properties.getEscaped('4.12121212', 'displayName');

// Properties can also extract multiple values and return a script object
props = properties.get(aNode, 'displayName', 'URI', 'published', 'locale');
propsEscaped = properties.getEscaped(aNode, 'displayName', 'URI', 'published', 'locale');