AEM Tech Bits - A Word on Versioning
Typically versioning in AEM is something only thought of in the context of pages. In fact, the common way in which one works with versioning is baked into the Touch UI Sites console which further couples the two. However, versioning isn't limited to pages. Any node in the JCR can theoretically be versioned. Let's go through it briefly.
Versioning for any JCR-compliant repository is available to any node with either the mix:simpleVersionable or mix:versionable mixins applied. cq:PageContent nodes have mix:versionable applied to them automatically:
The jcr:mixinTypes Name array holds all the mixins for a node as shown above. It's important to understand that mixins are just node types. Jackrabbit as a JCR implementation allows for additional node types to be applied in addition to a node's jcr:primaryType after that node has been instantiated. All the available node types in AEM live at /jcr:system/jcr:nodeTypes. Note that in the above example, the cq:PageContent node has two mixins: cq:LiveSync and mix:versionable. The prior was added as part of MSM to denote that this page is involved in a LiveRelationship (I'm going to write an article soon on MSM in detail so stay tuned) and the latter is the versionable mixin we just mentioned which denotes that this node is able to be versioned. For the purposes of this article, we won't get into what the underlying purpose of node types and mixins actually is but suffice it to say they provide a means of dictating the structure (both in properties as well as descendant nodes) of a node.
So what is the difference between simpleVersionable and versionable? A good amount is called out in the official JCR documentation around versioning and even more can be gleaned from the mix:versionable and mix:simpleVersionable mixins themselves under jcr:system but the biggest differences are that full versioning allows for far more complex versioning (branching and merging) and physically persists a node's version history under /jcr:system/jcr:versionStorage while simple versioning only allows for linear progression (every new version is the direct successor of the previous version) and does not persist any nodes under jcr:versionStorage.
To create a new version of a versionable node, simply check it out and check it back in. Here's what this looks like for full versioning:
Before checkout + checkin:
After checkout + checkin:
Version history for the above node (stored under jcr:system/jcr:versionStorage):
There's a few things worth pointing out:
- Note that the "isCheckedOut" property was created after we did our checking + checkout. This property is created the first time the node is checked out. Typically the flow would be to checkout a node, make some updates, and then check it back in.
- The jcr:baseVersion and jcr:versionHistory properties were added. The latter points directly to the version history as captured under jcr:system/jcr:versionStorage.
- A jcr:uuid property is created and a random UUID is created for the node. This UUID maps to the node name under jcr:system/jcr:versionStorage that holds the version history
- Checking out and back in again results in a version 1.1 created alongside 1.0 above and the "live" copy of the node has its jcr:baseVersion property updated with the UUID matching the 1.1 version that was just created.
There's a lot to versioning in AEM (too much IMHO) and you could easily spend an entire weekend studying it alone. For 99% of us, though, the basics should be sufficient so I'll share a few more interesting bits about versioning and then call it a day:
- The JCR documentation calls out that the base version of a versionable node is that version relative to which the current state of the checked-out versionable node constitutes a versionable change. Stated a bit simpler, a base version is what your node would be if you scrapped any changes you had in progress.
- The root version of your node is a stateless, well, root that provides the foundation for your node's entire version history. Think of it as the spray paint on the ground at a construction site where a new house is being built.
- If you manually delete a fully versionable node from the repo, AEM will not clean up the version history for you so make sure you clean it up yourself or get used to running the Version Purge tool that ships with AEM.
- If you're looking for a createVersion method, stop. Versions in AEM are created by virtue of checking in a versionable node. A label is assigned automatically to that version (following a basic trend of 1.0, 1.1, 1.2, etc) but you can add your own labels as desired.
- Activation of a modified page will automatically create a version. Activation of a page that hasn't been modified will not. Pages aren't versionable OOTB in AEM. Rather, AEM adds mix:versionable to the cq:PageContent node's jcr:mixinTypes property when a verison-creating action such as activation takes place.
- A "frozen node" refers to a versionable node's captured state as part of a single version in that node's version history. If you go look at the version history for a node under full versioning, you'll see such a frozen node under each individual version.
- Predecessor refers to the most recent, former version of a node while successor refers to the next version of that node. Think of it like what would happen by calling prev() or next() on an iterator respectively.
?