hi all, during the last oak hackathon in basel a couple of people started a discussion how to implement JCR versioning in oak. I'd like to give a quick overview what was discussed and how I think we could implement the various version related operations in oak.
to properly support JCR versioning we will have to make the version store available to all workspaces in oak. though, right now we don't even support multiple workspaces in oak. this means in a first step we will probably have to implement multi-workspace support by moving the current workspace tree one level down the hierarchy. the version store could then simply become a special workspace that will be made visible under /jcr:system/jcr:versionStorage. during the hackathon we discussed multiple options how to make the versions appear at that location: - the version store is mounted at the NodeState level. this has the advantage of being easier to implement, because the API is rather small. it would also allow us to enforce write protection on the version store by exposing it read-only at this location. - the version store is mounted on a higher level. that is, oak-jcr is responsible for constructing the node tree correctly as required by the JCR spec. this way it might be easier to separate JCR concerns from oak core and keep the latter as simple as possible. no matter which approach we take the version storage will be persisted as oak Trees ;) now, to implement the various JCR versioning operations, I'd like to encapsulate them in commit hooks as much as possible. this avoid the need to duplicate code and perform consistency checks in various places. my idea is to trigger those hooks with a defined set of content modification on Trees on through the oak API: = making a node versionable / create an initial version history a node is made versionable by adding a mix:versionable mixin type or by creating a node with a primary type that extends from mix:versionable. a commit hook will detect these modifications and create a version history for that node. in addition it will also modify the versionable node and set appropriate values for jcr:baseVersion, jcr:isCheckedOut, jcr:versionHistory, etc. this is very similar to how the current Jackrabbit implementation works. = checkin / checkout the jcr:isCheckedOut Boolean property is flipped and the change is saved. a commit hook will be triggered by this change and perform either a checkin or checkout operation. e.g. on checkin a version is created in the version store and the version related properties on the versionable node are updated accordingly. any other modification on a previously checked in node is rejected by the commit hook. at the same time oak-jcr will of course respect the jcr:isCheckedOut value and prevent changes when the node is checked in. = checkpoint an easy implementation could simply perform a checkin followed by a checkout. though, this wouldn't be 100% correct because it's not atomic. = restore version on the versionable node the jcr:baseVersion is set and save is called. a commit hook will be triggered by this change and perform the actual restore. handling of removeExisting: oak-jcr takes care of this flag and removes existing nodes if requested and necessary. the version restore commit hook will always fail if it encounters an identifier collision. restoring a node that doesn't exist in the workspace (15.7.8): create node with type stored in jcr:frozenPrimaryType of version to restore. jcr:baseVersion identifies version to restore. on save, the commit hook will restore the rest of the version. = version labels, remove a version these two are a bit tricky because changes are only reflected in the version storage, but not on the versionable node. the idea I have in mind is to set something like a temporary version operation property on the versionable node. e.g. rep:addVersionLabel = '<uuid>:foo' but this only works when the versionable node is actually present in the workspace, which doesn't need to be the case. specifically in the 'remove version' case where you might want to delete a complete version history, you first have to remove the versionable node in the workspace first. maybe version label manipulation and removing versions can work with operation properties on the jcr:versionStorage node? in any case such a property will be removed again by the commit hook, which would perform the requested operation. = merge this operation may work similar to version label manipulation and removing a version. setting a version operation property on the versionable node triggers a commit hook, which performs the actual operation and removes the property on save. feedback is very welcome ;) regards marcel
