You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2013/05/13 15:54:16 UTC

svn commit: r1481851 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ oak-jcr/ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/ oak-jcr/src/...

Author: mreutegg
Date: Mon May 13 13:54:16 2013
New Revision: 1481851

URL: http://svn.apache.org/r1481851
Log:
OAK-168: Basic JCR VersionManager support 
- implement checkin/checkout (work in progress)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionablePathHook.java
    jackrabbit/oak/trunk/oak-jcr/pom.xml
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionManagerDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java Mon May 13 13:54:16 2013
@@ -39,6 +39,11 @@ public class CommitFailedException exten
      */
     public static final String CONSTRAINT = "Constraint";
 
+    /**
+     * Type name for version violation errors.
+     */
+    public static final String VERSION = "Version";
+
     /** Serial version UID */
     private static final long serialVersionUID = 2727602333350620918L;
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java Mon May 13 13:54:16 2013
@@ -19,14 +19,10 @@
 package org.apache.jackrabbit.oak.plugins.version;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
 import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
-import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES;
-import static org.apache.jackrabbit.JcrConstants.JCR_FROZENNODE;
-import static org.apache.jackrabbit.JcrConstants.JCR_FROZENPRIMARYTYPE;
-import static org.apache.jackrabbit.JcrConstants.JCR_FROZENUUID;
 import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
-import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.JCR_ROOTVERSION;
@@ -35,17 +31,20 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONABLEUUID;
 import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
 import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONLABELS;
-import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE;
 import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
 import static org.apache.jackrabbit.JcrConstants.NT_VERSIONHISTORY;
 import static org.apache.jackrabbit.JcrConstants.NT_VERSIONLABELS;
+import static org.apache.jackrabbit.oak.plugins.version.Utils.uuidFromNode;
 import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_VERSIONSTORAGE;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
@@ -59,6 +58,10 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.util.TODO;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
 /**
  * TODO document
  */
@@ -106,13 +109,10 @@ class ReadWriteVersionManager extends Re
      *                                  {@code jcr:uuid} property.
      */
     @Nonnull
-    NodeBuilder getOrCreateVersionHistory(@Nonnull NodeBuilder versionable) {
+    NodeBuilder getOrCreateVersionHistory(@Nonnull NodeBuilder versionable)
+            throws CommitFailedException {
         checkNotNull(versionable);
-        PropertyState p = versionable.getProperty(JCR_UUID);
-        if (p == null) {
-            throw new IllegalArgumentException("Not referenceable");
-        }
-        String vUUID = p.getValue(Type.STRING);
+        String vUUID = uuidFromNode(versionable);
         String relPath = getVersionHistoryPath(vUUID);
         NodeBuilder node = versionStorageNode;
         for (Iterator<String> it = PathUtils.elements(relPath).iterator(); it.hasNext(); ) {
@@ -129,9 +129,9 @@ class ReadWriteVersionManager extends Re
                 node.setProperty(JCR_PRIMARYTYPE, nt, Type.NAME);
             }
         }
-        // use jcr:versionLabels node to detect if we need to initialize the
+        // use jcr:rootVersion node to detect if we need to initialize the
         // version history
-        if (!node.hasChildNode(JCR_VERSIONLABELS)) {
+        if (!node.hasChildNode(JCR_ROOTVERSION)) {
             // jcr:versionableUuuid property
             node.setProperty(JCR_VERSIONABLEUUID, vUUID, Type.STRING);
             node.setProperty(JCR_UUID,
@@ -142,54 +142,23 @@ class ReadWriteVersionManager extends Re
             vLabels.setProperty(JCR_PRIMARYTYPE, NT_VERSIONLABELS, Type.NAME);
 
             // jcr:rootVersion child node
-            NodeBuilder rootVersion = node.child(JCR_ROOTVERSION);
-            rootVersion.setProperty(JCR_UUID,
-                    IdentifierManager.generateUUID(), Type.STRING);
-            rootVersion.setProperty(JCR_PRIMARYTYPE, NT_VERSION, Type.NAME);
-            long now = System.currentTimeMillis();
-            rootVersion.setProperty(JCR_CREATED, now, Type.DATE);
-            rootVersion.setProperty(JCR_PREDECESSORS,
-                    Collections.<String>emptyList(), Type.REFERENCES);
-            rootVersion.setProperty(JCR_SUCCESSORS,
-                    Collections.<String>emptyList(), Type.REFERENCES);
-
-            // jcr:frozenNode of jcr:rootVersion
-            NodeBuilder frozenNode = rootVersion.child(JCR_FROZENNODE);
-            frozenNode.setProperty(JCR_UUID,
-                    IdentifierManager.generateUUID(), Type.STRING);
-            frozenNode.setProperty(JCR_PRIMARYTYPE, NT_FROZENNODE, Type.NAME);
-            Iterable<String> mixinTypes;
-            if (versionable.hasProperty(JCR_MIXINTYPES)) {
-                mixinTypes = versionable.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES);
-            } else {
-                mixinTypes = Collections.emptyList();
-            }
-            frozenNode.setProperty(JCR_FROZENMIXINTYPES, mixinTypes, Type.NAMES);
-            frozenNode.setProperty(JCR_FROZENPRIMARYTYPE,
-                    versionable.getProperty(JCR_PRIMARYTYPE).getValue(Type.NAME),
-                    Type.NAME);
-            frozenNode.setProperty(JCR_FROZENUUID, vUUID, Type.STRING);
-
-            // set jcr:isCheckedOut, jcr:versionHistory, jcr:baseVersion and
-            // jcr:predecessors on versionable node
-            versionable.setProperty(JCR_ISCHECKEDOUT, Boolean.TRUE, Type.BOOLEAN);
-            versionable.setProperty(JCR_VERSIONHISTORY,
-                    node.getProperty(JCR_UUID).getValue(Type.STRING), Type.REFERENCE);
-            String rootVersionUUID = rootVersion.getProperty(
-                    JCR_UUID).getValue(Type.STRING);
-            versionable.setProperty(JCR_BASEVERSION, rootVersionUUID, Type.REFERENCE);
-            versionable.setProperty(JCR_PREDECESSORS,
-                    Collections.singletonList(rootVersionUUID), Type.REFERENCES);
+            createVersion(node, versionable);
         }
         return node;
     }
 
     public void checkout(NodeBuilder versionable) {
-        TODO.unimplemented();
+        versionable.setProperty(JCR_ISCHECKEDOUT, true, Type.BOOLEAN);
+        PropertyState baseVersion = versionable.getProperty(JCR_BASEVERSION);
+        List<String> predecessors = Collections.singletonList(
+                baseVersion.getValue(Type.REFERENCE));
+        versionable.setProperty(JCR_PREDECESSORS, predecessors, Type.REFERENCES);
     }
 
-    public void checkin(NodeBuilder versionable) {
-        TODO.unimplemented();
+    public void checkin(@Nonnull NodeBuilder versionable)
+            throws CommitFailedException {
+        NodeBuilder history = getOrCreateVersionHistory(versionable);
+        createVersion(history, versionable);
     }
 
     public void restore(NodeBuilder versionable) {
@@ -197,4 +166,187 @@ class ReadWriteVersionManager extends Re
     }
 
     // TODO: more methods that modify versions
+
+    //------------------------------< internal >--------------------------------
+
+    /**
+     * Creates a version in the given version history. If the given version
+     * history does not yet have a version, then a root version is created and
+     * the versionable node is in a checked out state. Otherwise a version is
+     * created and the versionable node is set to checked in.
+     *
+     * @param vHistory the version history node.
+     * @param versionable the versionable node.
+     * @return the created version (nt:version) node.
+     * @throws CommitFailedException if creating the version fails. E.g. because
+     * the versionable node contains a OPV item with ABORT.
+     */
+    private NodeBuilder createVersion(@Nonnull NodeBuilder vHistory,
+                                      @Nonnull NodeBuilder versionable)
+            throws IllegalArgumentException, CommitFailedException {
+        List<String> predecessors;
+        NodeBuilder version;
+        boolean isRootVersion;
+        if (!vHistory.hasChildNode(JCR_ROOTVERSION)) {
+            // create root version
+            isRootVersion = true;
+            predecessors = Collections.emptyList();
+            version = vHistory.child(JCR_ROOTVERSION);
+        } else {
+            isRootVersion = false;
+            checkState(versionable.hasProperty(JCR_PREDECESSORS));
+            PropertyState state = versionable.getProperty(JCR_PREDECESSORS);
+            predecessors = ImmutableList.copyOf(state.getValue(Type.REFERENCES));
+            version = vHistory.child(calculateVersion(vHistory, versionable));
+        }
+        String versionUUID = IdentifierManager.generateUUID();
+        version.setProperty(JCR_UUID, versionUUID, Type.STRING);
+        version.setProperty(JCR_PRIMARYTYPE, NT_VERSION, Type.NAME);
+        long now = System.currentTimeMillis();
+        version.setProperty(JCR_CREATED, now, Type.DATE);
+        version.setProperty(JCR_PREDECESSORS, predecessors, Type.REFERENCES);
+        version.setProperty(JCR_SUCCESSORS, Collections.<String>emptyList(), Type.REFERENCES);
+
+        // update successors of versions identified by predecessors
+        // FIXME: inefficient because it iterates over all versions
+        for (String name : vHistory.getChildNodeNames()) {
+            if (name.equals(JCR_VERSIONLABELS)) {
+                continue;
+            }
+            NodeBuilder predecessor = vHistory.getChildNode(name);
+            if (predecessors.contains(uuidFromNode(predecessor))) {
+                PropertyState state = predecessor.getProperty(JCR_SUCCESSORS);
+                if (state == null) {
+                    throw new IllegalStateException("Missing " + JCR_SUCCESSORS +
+                            " property on " + predecessor);
+                }
+                Set<String> refs = Sets.newHashSet(state.getValue(Type.REFERENCES));
+                refs.add(versionUUID);
+                predecessor.setProperty(JCR_SUCCESSORS, refs, Type.REFERENCES);
+            }
+        }
+
+        // jcr:frozenNode of created version
+        VersionableState versionableState = VersionableState.fromVersion(
+                version, versionable, ntMgr);
+        if (!isRootVersion) {
+            versionableState.create();
+        }
+
+        // set jcr:isCheckedOut, jcr:versionHistory, jcr:baseVersion and
+        // jcr:predecessors on versionable node
+        versionable.setProperty(JCR_ISCHECKEDOUT, isRootVersion, Type.BOOLEAN);
+        versionable.setProperty(JCR_VERSIONHISTORY,
+                uuidFromNode(vHistory), Type.REFERENCE);
+        versionable.setProperty(JCR_BASEVERSION, versionUUID, Type.REFERENCE);
+        if (isRootVersion) {
+            // set predecessors to base version if this is the root version
+            predecessors = Collections.singletonList(versionUUID);
+        } else {
+            // otherwise clear predecessors for check-in
+            predecessors = Collections.emptyList();
+        }
+        versionable.setProperty(JCR_PREDECESSORS, predecessors, Type.REFERENCES);
+        return version;
+    }
+
+    /**
+     * <i>Copied from Apache Jackrabbit Core</i>
+     * <p>
+     * Calculates the name of the new version that will be created by a
+     * checkin call. The name is determined as follows:
+     * <ul>
+     * <li> first the predecessor version with the shortest name is searched.
+     * <li> if that predecessor version is the root version, the new version gets
+     *      the name "{number of successors}+1" + ".0"
+     * <li> if that predecessor version has no successor, the last digit of it's
+     *      version number is incremented.
+     * <li> if that predecessor version has successors but the incremented name
+     *      does not exist, that name is used.
+     * <li> otherwise a ".0" is added to the name until a non conflicting name
+     *      is found.
+     * <ul>
+     * </p>
+     * Example Graph:
+     * <pre>
+     * jcr:rootVersion
+     *  |     |
+     * 1.0   2.0
+     *  |
+     * 1.1
+     *  |
+     * 1.2 ---\  ------\
+     *  |      \        \
+     * 1.3   1.2.0   1.2.0.0
+     *  |      |
+     * 1.4   1.2.1 ----\
+     *  |      |        \
+     * 1.5   1.2.2   1.2.1.0
+     *  |      |        |
+     * 1.6     |     1.2.1.1
+     *  |-----/
+     * 1.7
+     * </pre>
+     *
+     * @param history the version history
+     * @param versionable the node to checkin
+     * @return the new version name
+     * @throws IllegalStateException if mandatory version properties are missing.
+     */
+    protected String calculateVersion(@Nonnull NodeBuilder history,
+                                      @Nonnull NodeBuilder versionable)
+            throws IllegalStateException {
+
+        // 1. search a predecessor, suitable for generating the new name
+        PropertyState predecessors = versionable.getProperty(JCR_PREDECESSORS);
+
+        if (predecessors == null || predecessors.count() == 0) {
+            String message;
+            if (predecessors == null) {
+                message = "Mandatory jcr:predecessors property missing on node " + uuidFromNode(versionable);
+            } else {
+                message = "Mandatory jcr:predecessors property is empty on node " + uuidFromNode(versionable);
+            }
+            throw new IllegalStateException(message);
+        }
+
+        // FIXME: inefficient because it iterates over all versions!
+        Set<String> uuids = ImmutableSet.copyOf(predecessors.getValue(Type.REFERENCES));
+        String best = null;
+        for (String name : history.getChildNodeNames()) {
+            if (name.equals(JCR_VERSIONLABELS)) {
+                continue;
+            }
+            NodeBuilder child = history.getChildNode(name);
+            if (uuids.contains(uuidFromNode(child))) {
+                if (best == null || name.length() < best.length()) {
+                    best = name;
+                }
+            }
+        }
+
+        if (best == null) {
+            String message = "Could not find 'best' predecessor node for " +
+                    uuidFromNode(versionable);
+            throw new IllegalStateException(message);
+        }
+
+        // 2. generate version name (assume no namespaces in version names)
+        String versionName = best;
+        int pos = versionName.lastIndexOf('.');
+        if (pos > 0) {
+            String newVersionName = versionName.substring(0, pos + 1)
+                    + (Integer.parseInt(versionName.substring(pos + 1)) + 1);
+            while (history.hasChildNode(newVersionName)) {
+                versionName += ".0";
+                newVersionName = versionName;
+            }
+            return newVersionName;
+        } else {
+            // best is root version
+            checkState(history.hasChildNode(JCR_ROOTVERSION));
+            NodeBuilder v = history.getChildNode(JCR_ROOTVERSION);
+            return String.valueOf(v.getProperty(JCR_SUCCESSORS).count() + 1) + ".0";
+        }
+    }
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java?rev=1481851&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java Mon May 13 13:54:16 2013
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+
+/**
+ * <code>Utils</code> provide some utility methods.
+ */
+public class Utils {
+
+    /**
+     * Returns the jcr:uuid value of given <code>node</code>.
+     *
+     * @param node a referenceable node.
+     * @return the value of the jcr:uuid property.
+     * @throws IllegalArgumentException if the node is not referenceable.
+     */
+    @Nonnull
+    static String uuidFromNode(@Nonnull NodeBuilder node)
+            throws IllegalArgumentException {
+        PropertyState p = checkNotNull(node).getProperty(JCR_UUID);
+        if (p == null) {
+            throw new IllegalArgumentException("Not referenceable");
+        }
+        return p.getValue(Type.STRING);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java Mon May 13 13:54:16 2013
@@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -46,6 +47,9 @@ class VersionEditor implements Editor {
     private final ReadWriteVersionManager vMgr;
     private final NodeBuilder node;
     private Boolean isVersionable = null;
+    private NodeState before;
+    private NodeState after;
+    private boolean wasReadOnly;
 
     public VersionEditor(@Nonnull NodeBuilder versionStore,
             @Nonnull NodeBuilder workspaceRoot) {
@@ -63,20 +67,29 @@ class VersionEditor implements Editor {
     @Override
     public void enter(NodeState before, NodeState after)
             throws CommitFailedException {
+        this.before = before;
+        this.after = after;
         if (isVersionable()) {
             vMgr.getOrCreateVersionHistory(node);
         }
+        // calculate wasReadOnly state
+        if (after.exists() || isVersionable()) {
+            // deleted or versionable -> check if it was checked in
+            wasReadOnly = wasCheckedIn();
+        } else {
+            // otherwise inherit from parent
+            wasReadOnly = parent != null && parent.wasReadOnly;
+        }
     }
 
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
-
     }
 
     @Override
     public void propertyAdded(PropertyState after) {
-        if (!isVersionable()) {
+        if (!wasReadOnly) {
             return;
         }
         // JCR allows to put a lock on a checked in node.
@@ -84,14 +97,13 @@ class VersionEditor implements Editor {
                 || after.getName().equals(JcrConstants.JCR_LOCKISDEEP)) {
             return;
         }
-        if (wasCheckedIn()) {
-            throwCheckedIn("Cannot add property " + after.getName()
-                    + " on checked in node");
-        }
+        throwCheckedIn("Cannot add property " + after.getName()
+                + " on checked in node");
     }
 
     @Override
-    public void propertyChanged(PropertyState before, PropertyState after) {
+    public void propertyChanged(PropertyState before, PropertyState after)
+            throws CommitFailedException {
         if (!isVersionable()) {
             if (!isVersionProperty(after) && wasCheckedIn()) {
                 throwCheckedIn("Cannot change property " + after.getName()
@@ -110,7 +122,7 @@ class VersionEditor implements Editor {
             vMgr.restore(node);
         } else if (isVersionProperty(after)) {
             throwProtected(after.getName());
-        } else if (wasCheckedIn()) {
+        } else if (wasReadOnly) {
             throwCheckedIn("Cannot change property " + after.getName()
                     + " on checked in node");
         }
@@ -118,8 +130,8 @@ class VersionEditor implements Editor {
 
     @Override
     public void propertyDeleted(PropertyState before) {
-        if (!isVersionable()) {
-            if (!isVersionProperty(before) && wasCheckedIn()) {
+        if (wasReadOnly) {
+            if (!isVersionProperty(before)) {
                 throwProtected("Cannot delete property on checked in node");
             }
         }
@@ -127,7 +139,7 @@ class VersionEditor implements Editor {
 
     @Override
     public Editor childNodeAdded(String name, NodeState after) {
-        return childNodeChanged(name, EMPTY_NODE, after);
+        return childNodeChanged(name, MISSING_NODE, after);
     }
 
     @Override
@@ -138,7 +150,7 @@ class VersionEditor implements Editor {
 
     @Override
     public Editor childNodeDeleted(String name, NodeState before) {
-        return new VersionEditor(this, vMgr, EMPTY_NODE.builder());
+        return new VersionEditor(this, vMgr, MISSING_NODE.builder());
     }
 
     /**
@@ -150,9 +162,9 @@ class VersionEditor implements Editor {
     private boolean isVersionable() {
         if (isVersionable == null) {
             // this is not 100% correct, because t.getPath() will
-            // not return the correct path for nodeAfter, but is
+            // not return the correct path for node after, but is
             // sufficient to check if it is versionable
-            Tree t = new ReadOnlyTree(node.getNodeState());
+            Tree t = new ReadOnlyTree(after);
             isVersionable = vMgr.isVersionable(t);
         }
         return isVersionable;
@@ -169,13 +181,10 @@ class VersionEditor implements Editor {
      *         property.
      */
     private boolean wasCheckedIn() {
-        NodeState state = node.getBaseState();
-        if (state != null) {
-            PropertyState prop = state
-                    .getProperty(VersionConstants.JCR_ISCHECKEDOUT);
-            if (prop != null) {
-                return !prop.getValue(Type.BOOLEAN);
-            }
+        PropertyState prop = before
+                .getProperty(VersionConstants.JCR_ISCHECKEDOUT);
+        if (prop != null) {
+            return !prop.getValue(Type.BOOLEAN);
         }
         // new node or not versionable, check parent
         return parent != null && parent.wasCheckedIn();
@@ -194,6 +203,7 @@ class VersionEditor implements Editor {
 
     private static void throwUnchecked(RepositoryException e)
             throws UncheckedRepositoryException {
+        // TODO: still necessary?
         throw new UncheckedRepositoryException(e);
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionablePathHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionablePathHook.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionablePathHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionablePathHook.java Mon May 13 13:54:16 2013
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.oak.plugins.version;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 
 import javax.annotation.Nonnull;
@@ -55,7 +58,12 @@ public class VersionablePathHook impleme
         NodeBuilder rootBuilder = after.builder();
         NodeBuilder vsRoot = rootBuilder.child(NodeTypeConstants.JCR_SYSTEM).child(NodeTypeConstants.JCR_VERSIONSTORAGE);
         ReadWriteVersionManager vMgr = new ReadWriteVersionManager(vsRoot, rootBuilder);
-        after.compareAgainstBaseState(before, new Diff(vMgr, new Node(rootBuilder)));
+        List<CommitFailedException> exceptions = new ArrayList<CommitFailedException>();
+        after.compareAgainstBaseState(before,
+                new Diff(vMgr, new Node(rootBuilder), exceptions));
+        if (!exceptions.isEmpty()) {
+            throw exceptions.get(0);
+        }
         return rootBuilder.getNodeState();
     }
 
@@ -63,17 +71,28 @@ public class VersionablePathHook impleme
 
         private final ReadWriteVersionManager versionManager;
         private final Node nodeAfter;
+        private final List<CommitFailedException> exceptions;
 
-        private Diff(@Nonnull ReadWriteVersionManager versionManager, @Nonnull Node node) {
+        private Diff(@Nonnull ReadWriteVersionManager versionManager,
+                     @Nonnull Node node,
+                     @Nonnull List<CommitFailedException> exceptions) {
             this.versionManager = versionManager;
             this.nodeAfter = node;
+            this.exceptions = exceptions;
         }
 
         @Override
         public boolean propertyAdded(PropertyState after) {
             if (VersionConstants.JCR_VERSIONHISTORY.equals(after.getName())
                     && nodeAfter.isVersionable(versionManager)) {
-                NodeBuilder vhBuilder = versionManager.getOrCreateVersionHistory(nodeAfter.builder);
+                NodeBuilder vhBuilder;
+                try {
+                    vhBuilder = versionManager.getOrCreateVersionHistory(nodeAfter.builder);
+                } catch (CommitFailedException e) {
+                    exceptions.add(e);
+                    // stop further comparison
+                    return false;
+                }
 
                 if (!vhBuilder.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
                     vhBuilder.setProperty(
@@ -98,7 +117,7 @@ public class VersionablePathHook impleme
                 String name, NodeState before, NodeState after) {
             Node node = new Node(nodeAfter, name);
             return after.compareAgainstBaseState(
-                    before, new Diff(versionManager, node));
+                    before, new Diff(versionManager, node, exceptions));
         }
     }
 

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java?rev=1481851&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java Mon May 13 13:54:16 2013
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import javax.jcr.version.OnParentVersionAction;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENNODE;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENPRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENUUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE;
+import static org.apache.jackrabbit.oak.plugins.version.Utils.uuidFromNode;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.core.IdentifierManager;
+import org.apache.jackrabbit.oak.core.ReadOnlyTree;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+/**
+ * <code>VersionableState</code> provides methods to create a versionable state
+ * for a version based on a versionable node.
+ */
+class VersionableState {
+
+    private static final String JCR_CHILDVERSIONHISTORY = "jcr:childVersionHistory";
+
+    private final NodeBuilder frozenNode;
+    private final NodeBuilder versionable;
+    private final ReadOnlyNodeTypeManager ntMgr;
+
+    private VersionableState(@Nonnull NodeBuilder version,
+                             @Nonnull NodeBuilder versionable,
+                             @Nonnull ReadOnlyNodeTypeManager ntMgr) {
+        this.frozenNode = checkNotNull(version).child(JCR_FROZENNODE);
+        this.versionable = checkNotNull(versionable);
+        this.ntMgr = checkNotNull(ntMgr);
+        // initialize jcr:frozenNode
+        frozenNode.setProperty(JCR_UUID, IdentifierManager.generateUUID(), Type.STRING);
+        frozenNode.setProperty(JCR_PRIMARYTYPE, NT_FROZENNODE, Type.NAME);
+        Iterable<String> mixinTypes;
+        if (versionable.hasProperty(JCR_MIXINTYPES)) {
+            mixinTypes = versionable.getNames(JCR_MIXINTYPES);
+        } else {
+            mixinTypes = Collections.emptyList();
+        }
+        frozenNode.setProperty(JCR_FROZENMIXINTYPES, mixinTypes, Type.NAMES);
+        frozenNode.setProperty(JCR_FROZENPRIMARYTYPE,
+                versionable.getName(JCR_PRIMARYTYPE), Type.NAME);
+        frozenNode.setProperty(JCR_FROZENUUID, uuidFromNode(versionable), Type.STRING);
+    }
+
+    /**
+     * Creates a frozen node under the version and initializes it with the basic
+     * frozen properties (jcr:frozenPrimaryType, jcr:frozenMixinTypes and
+     * jcr:frozenUuid) from the given versionable node.
+     *
+     * @param version the parent node of the frozen node.
+     * @param versionable the versionable node.
+     * @param ntMgr the node type manager.
+     * @return a versionable state
+     */
+    @Nonnull
+    static VersionableState fromVersion(@Nonnull NodeBuilder version,
+                                        @Nonnull NodeBuilder versionable,
+                                        @Nonnull ReadOnlyNodeTypeManager ntMgr) {
+        return new VersionableState(version, versionable, ntMgr);
+    }
+
+    @Nonnull
+    NodeBuilder getFrozenNode() {
+        return frozenNode;
+    }
+
+    /**
+     * Creates the versionable state under the version.
+     *
+     * @return the frozen node.
+     * @throws CommitFailedException if the operation fails. E.g. because the
+     *              versionable node has a property with OPV ABORT.
+     */
+    NodeBuilder create() throws CommitFailedException {
+        try {
+            createState(versionable, frozenNode);
+            return frozenNode;
+        } catch (RepositoryException e) {
+            throw new CommitFailedException(CommitFailedException.VERSION, 0,
+                    "Unexpected RepositoryException", e);
+        }
+    }
+
+    private void createState(NodeBuilder src,
+                             NodeBuilder dest)
+            throws CommitFailedException, RepositoryException {
+        copyProperties(src, dest, false, true);
+
+        // add the frozen children and histories
+        for (String name : src.getChildNodeNames()) {
+            NodeBuilder child = src.getChildNode(name);
+            int opv = getOPV(src, child);
+
+            if (opv == OnParentVersionAction.ABORT) {
+                throw new CommitFailedException(CommitFailedException.VERSION, 1,
+                        "Checkin aborted due to OPV abort in " + name);
+            }
+            if (opv == OnParentVersionAction.VERSION) {
+                if (ntMgr.isNodeType(new ReadOnlyTree(child.getNodeState()), MIX_VERSIONABLE)) {
+                    // create frozen versionable child
+                    versionedChild(child, dest.child(name));
+                } else {
+                    // else copy
+                    copy(child, dest.child(name));
+                }
+            } else if (opv == OnParentVersionAction.COPY) {
+                copy(child, dest.child(name));
+            }
+        }
+    }
+
+    private void versionedChild(NodeBuilder src, NodeBuilder dest) {
+        String ref = src.getProperty(JCR_VERSIONHISTORY).getValue(Type.REFERENCE);
+        dest.setProperty(JCR_CHILDVERSIONHISTORY, ref, Type.REFERENCE);
+    }
+
+    private void copy(NodeBuilder src,
+                      NodeBuilder dest)
+            throws RepositoryException, CommitFailedException {
+        copyProperties(src, dest, true, false);
+        for (String name : src.getChildNodeNames()) {
+            NodeBuilder child = src.getChildNode(name);
+            copy(child, dest.child(name));
+        }
+    }
+
+    private void copyProperties(NodeBuilder src,
+                                NodeBuilder dest,
+                                boolean forceCopy,
+                                boolean ignoreTypeAndUUID)
+            throws RepositoryException, CommitFailedException {
+        // add the properties
+        for (PropertyState prop : src.getProperties()) {
+            int opv;
+            if (forceCopy) {
+                opv = OnParentVersionAction.COPY;
+            } else {
+                opv = getOPV(src, prop);
+            }
+
+            String propName = prop.getName();
+            if (opv == OnParentVersionAction.ABORT) {
+                throw new CommitFailedException(CommitFailedException.VERSION, 1,
+                        "Checkin aborted due to OPV abort in " + propName);
+            }
+            if (ignoreTypeAndUUID
+                    && (propName.equals(JCR_PRIMARYTYPE)
+                        || propName.equals(JCR_MIXINTYPES)
+                        || propName.equals(JCR_UUID))) {
+                continue;
+            }
+            if (opv == OnParentVersionAction.VERSION
+                    || opv == OnParentVersionAction.COPY) {
+                dest.setProperty(prop);
+            }
+        }
+    }
+
+    private int getOPV(NodeBuilder parent, NodeBuilder child)
+            throws RepositoryException {
+        return ntMgr.getDefinition(new ReadOnlyTree(parent.getNodeState()),
+                new ReadOnlyTree(child.getNodeState())).getOnParentVersion();
+    }
+
+    private int getOPV(NodeBuilder node, PropertyState property)
+            throws RepositoryException {
+        return ntMgr.getDefinition(new ReadOnlyTree(node.getNodeState()),
+                property, false).getOnParentVersion();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Mon May 13 13:54:16 2013
@@ -238,7 +238,6 @@
       org.apache.jackrabbit.test.api.observation.LockingTest#testAddLockToNode
       org.apache.jackrabbit.test.api.observation.LockingTest#testRemoveLockFromNode
       <!-- Versioning -->
-      org.apache.jackrabbit.test.api.version.VersionTest#testGetUUID
       org.apache.jackrabbit.test.api.version.VersionTest#testRestore
       org.apache.jackrabbit.test.api.version.VersionTest#testUnlock
       org.apache.jackrabbit.test.api.version.VersionTest#testUnlockJcr2
@@ -253,7 +252,6 @@
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testUnlock
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testUnlockJcr2
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testUpdate
-      org.apache.jackrabbit.test.api.version.VersionHistoryTest#testInitialNumberOfVersions
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testGetCorrespondingNodePath
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testGetLock
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testGetLockJcr2
@@ -262,20 +260,16 @@
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testMerge
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testInitialNumberOfLinearVersions
       org.apache.jackrabbit.test.api.version.VersionHistoryTest#testInitiallyGetAllLinearVersionsContainsTheRootAndTheBaseVersion
-      org.apache.jackrabbit.test.api.version.VersionHistoryTest#testGetAllFrozenNodes
       org.apache.jackrabbit.test.api.version.VersionLabelTest
-      org.apache.jackrabbit.test.api.version.CheckinTest
       org.apache.jackrabbit.test.api.version.CopyTest
       org.apache.jackrabbit.test.api.version.RestoreTest
       org.apache.jackrabbit.test.api.version.WorkspaceRestoreTest
-      org.apache.jackrabbit.test.api.version.OnParentVersionAbortTest
       org.apache.jackrabbit.test.api.version.OnParentVersionComputeTest
       org.apache.jackrabbit.test.api.version.OnParentVersionCopyTest
       org.apache.jackrabbit.test.api.version.OnParentVersionIgnoreTest
       org.apache.jackrabbit.test.api.version.OnParentVersionInitializeTest
       org.apache.jackrabbit.test.api.version.GetPredecessorsTest
-      org.apache.jackrabbit.test.api.version.GetContainingHistoryTest
-      org.apache.jackrabbit.test.api.version.GetVersionableUUIDTest
+      org.apache.jackrabbit.test.api.version.GetReferencesNodeTest#testGetReferencesNeverFromVersions <!-- Node.getReferences must not return references from version storage -->
       org.apache.jackrabbit.test.api.version.SessionMoveVersionExceptionTest
       org.apache.jackrabbit.test.api.version.WorkspaceMoveVersionExceptionTest
       org.apache.jackrabbit.test.api.version.MergeCancelMergeTest

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionManagerDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionManagerDelegate.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionManagerDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionManagerDelegate.java Mon May 13 13:54:16 2013
@@ -78,23 +78,29 @@ public class VersionManagerDelegate {
     }
 
     @Nonnull
-    public VersionHistoryDelegate getVersionHistory(@Nonnull NodeDelegate nodeDelegate)
+    public VersionHistoryDelegate createVersionHistory(@Nonnull NodeDelegate versionHistory)
             throws RepositoryException {
-        Tree vh = versionManager.getVersionHistory(getTree(nodeDelegate));
+        return new VersionHistoryDelegate(sessionDelegate, getTree(versionHistory));
+    }
+
+    @Nonnull
+    public VersionHistoryDelegate getVersionHistory(@Nonnull NodeDelegate versionable)
+            throws RepositoryException {
+        Tree vh = versionManager.getVersionHistory(getTree(versionable));
         if (vh == null) {
             throw new UnsupportedRepositoryOperationException("Node does not" +
-                    " have a version history: " + nodeDelegate.getPath());
+                    " have a version history: " + versionable.getPath());
         }
         return new VersionHistoryDelegate(sessionDelegate, vh);
     }
 
     @Nonnull
-    public VersionDelegate getBaseVersion(@Nonnull NodeDelegate nodeDelegate)
+    public VersionDelegate getBaseVersion(@Nonnull NodeDelegate versionable)
             throws RepositoryException {
-        Tree v = versionManager.getBaseVersion(getTree(nodeDelegate));
+        Tree v = versionManager.getBaseVersion(getTree(versionable));
         if (v == null) {
             throw new UnsupportedRepositoryOperationException("Node does not" +
-                    " have a base version: " + nodeDelegate.getPath());
+                    " have a base version: " + versionable.getPath());
         }
         return VersionDelegate.create(sessionDelegate, v);
     }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java Mon May 13 13:54:16 2013
@@ -22,6 +22,7 @@ import javax.annotation.Nonnull;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Root;
@@ -111,7 +112,13 @@ public class ReadWriteVersionManager ext
                 refresh();
             } catch (CommitFailedException e) {
                 getWorkspaceRoot().refresh();
-                throw new RepositoryException(e);
+                // FIXME: hardcoded exception code
+                if (e.getType().equals(CommitFailedException.VERSION)
+                        && e.getCode() == 1) {
+                    throw new VersionException(e.getMessage());
+                } else {
+                    throw new RepositoryException(e);
+                }
             }
         }
         return getBaseVersion(getWorkspaceRoot().getTree(versionable.getPath()));

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java Mon May 13 13:54:16 2013
@@ -51,7 +51,8 @@ class VersionImpl extends NodeImpl<Versi
     @Override
     public VersionHistory getContainingHistory() throws RepositoryException {
         return new VersionHistoryImpl(
-                getVersionManagerDelegate().getVersionHistory(dlg.getParent()), sessionContext);
+                getVersionManagerDelegate().createVersionHistory(
+                        dlg.getParent()), sessionContext);
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java?rev=1481851&r1=1481850&r2=1481851&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java Mon May 13 13:54:16 2013
@@ -193,7 +193,10 @@ public class VersionManagerImpl implemen
 
     @Override
     public Version checkpoint(String absPath) throws RepositoryException {
-        return TODO.unimplemented().returnValue(null);
+        // FIXME: atomic?
+        Version v = checkin(absPath);
+        checkout(absPath);
+        return v;
     }
 
     @Nonnull