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/08/07 16:16:19 UTC

svn commit: r1511327 - 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-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/ oak-j...

Author: mreutegg
Date: Wed Aug  7 14:16:18 2013
New Revision: 1511327

URL: http://svn.apache.org/r1511327
Log:
OAK-168: Basic JCR VersionManager support
- Add/Remove labels

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionStorageEditor.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.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/VersionConstants.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionExceptionCode.java
    jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd
    jackrabbit/oak/trunk/oak-jcr/pom.xml
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionHistoryDelegate.java
    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/VersionHistoryImpl.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=1511327&r1=1511326&r2=1511327&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 Wed Aug  7 14:16:18 2013
@@ -26,6 +26,7 @@ import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.security.AccessControlException;
+import javax.jcr.version.LabelExistsVersionException;
 import javax.jcr.version.VersionException;
 
 import static java.lang.String.format;
@@ -92,6 +93,11 @@ public class CommitFailedException exten
     public static final String VERSION = "Version";
 
     /**
+     * Type name for label exists version errors.
+     */
+    public static final String LABEL_EXISTS = "LabelExists";
+
+    /**
      * Serial version UID
      */
     private static final long serialVersionUID = 2727602333350620918L;
@@ -220,6 +226,8 @@ public class CommitFailedException exten
             return new InvalidItemStateException(message, this);
         } else if (isOfType(VERSION)) {
             return new VersionException(message, this);
+        } else if (isOfType(LABEL_EXISTS)) {
+            return new LabelExistsVersionException(message, this);
         } else if (isOfType(LOCK)) {
             return new LockException(message, this);
         } else {

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=1511327&r1=1511326&r2=1511327&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 Wed Aug  7 14:16:18 2013
@@ -219,6 +219,61 @@ class ReadWriteVersionManager extends Re
         restore(versionable, uuidFromNode(version), selector);
     }
 
+    /**
+     * Adds a version label to the jcr:versionLabels node of the referenced
+     * version history.
+     *
+     * @param historyRelPath relative path from the jcr:versionStorage node to
+     *                       the version history node.
+     * @param label          the version label.
+     * @param versionName    the name of the version.
+     * @throws CommitFailedException if there is no such version history or if
+     * there is already a label with the given name or if the given version name
+     * is invalid.
+     */
+    public void addVersionLabel(@Nonnull String historyRelPath,
+                                @Nonnull String label,
+                                @Nonnull String versionName)
+           throws CommitFailedException {
+        NodeBuilder labels = getVersionLabelsFor(checkNotNull(historyRelPath));
+        if (labels.hasProperty(checkNotNull(label))) {
+            throw new CommitFailedException(CommitFailedException.LABEL_EXISTS,
+                    VersionExceptionCode.LABEL_EXISTS.ordinal(),
+                    "Version label " + label + " already exists on this version history");
+        }
+        NodeBuilder history = resolve(versionStorageNode, historyRelPath);
+        if (checkNotNull(versionName).equals(JCR_ROOTVERSION)
+                || !history.hasChildNode(checkNotNull(versionName))) {
+            throw new CommitFailedException(CommitFailedException.VERSION,
+                    VersionExceptionCode.NO_SUCH_VERSION.ordinal(),
+                    "Not a valid version on this history: " + versionName);
+        }
+        String uuid = uuidFromNode(history.getChildNode(versionName));
+        labels.setProperty(label, uuid, Type.REFERENCE);
+    }
+
+    /**
+     * Removes a version label from the jcr:versionLabels node of the referenced
+     * version history.
+     *
+     * @param historyRelPath relative path from the jcr:versionStorage node to
+     *                       the version history node.
+     * @param label          the version label.
+     * @throws CommitFailedException if there is no such version history or if
+     * there is no label with the given name.
+     */
+    public void removeVersionLabel(@Nonnull String historyRelPath,
+                                   @Nonnull String label)
+            throws CommitFailedException {
+        NodeBuilder labels = getVersionLabelsFor(checkNotNull(historyRelPath));
+        if (!labels.hasProperty(checkNotNull(label))) {
+            throw new CommitFailedException(CommitFailedException.VERSION,
+                    VersionExceptionCode.NO_SUCH_VERSION_LABEL.ordinal(),
+                    "Version label " + label + " does not exist on this version history");
+        }
+        labels.removeProperty(label);
+    }
+
     // TODO: more methods that modify versions
 
     //------------------------------< internal >--------------------------------
@@ -408,4 +463,26 @@ class ReadWriteVersionManager extends Re
             return String.valueOf(v.getProperty(JCR_SUCCESSORS).count() + 1) + ".0";
         }
     }
+
+    /**
+     * Returns the jcr:versionLabels node of the version history referenced
+     * by the given path.
+     *
+     * @param historyRelPath relative path from the jcr:versionStorage node
+     *                       to the history node.
+     * @return the jcr:versionLabels node.
+     * @throws CommitFailedException if there is no version history at the
+     * given path.
+     */
+    private NodeBuilder getVersionLabelsFor(String historyRelPath)
+            throws CommitFailedException {
+        NodeBuilder history = resolve(versionStorageNode, historyRelPath);
+        if (!history.exists()) {
+            throw new CommitFailedException(CommitFailedException.VERSION,
+                    VersionExceptionCode.UNEXPECTED_REPOSITORY_EXCEPTION.ordinal(),
+                    "Version history does not exist: " + PathUtils.concat(
+                            VERSION_STORE_PATH, historyRelPath));
+        }
+        return history.child(JCR_VERSIONLABELS);
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionConstants.java?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionConstants.java Wed Aug  7 14:16:18 2013
@@ -55,6 +55,15 @@ public interface VersionConstants extend
      */
     String MIX_REP_VERSIONABLE_PATHS = "rep:VersionablePaths";
 
+    /**
+     * Version operation property name to add version labels.
+     */
+    String REP_ADD_VERSION_LABELS = "rep:addVersionLabels";
+
+    /**
+     * Version operation property name to remove version labels.
+     */
+    String REP_REMOVE_VERSION_LABELS = "rep:removeVersionLabels";
 
     /**
      * Quote from JSR 283 Section "15.12.3 Activity Storage"<p/>

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java Wed Aug  7 14:16:18 2013
@@ -21,6 +21,7 @@ import static org.apache.jackrabbit.oak.
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.spi.commit.CompositeEditor;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
@@ -41,8 +42,10 @@ public class VersionEditorProvider imple
         if (!system.hasChildNode(JCR_VERSIONSTORAGE)) {
             return null;
         }
-        NodeBuilder version = system.child(JCR_VERSIONSTORAGE);
-        return new VisibleEditor(new VersionEditor(version, builder));
+        NodeBuilder versionStorage = system.child(JCR_VERSIONSTORAGE);
+        return new VisibleEditor(new CompositeEditor(
+                new VersionEditor(versionStorage, builder),
+                new VersionStorageEditor(versionStorage, builder)));
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionExceptionCode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionExceptionCode.java?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionExceptionCode.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionExceptionCode.java Wed Aug  7 14:16:18 2013
@@ -28,7 +28,9 @@ public enum VersionExceptionCode {
     NODE_CHECKED_IN("Node is checked in"),
     NO_SUCH_VERSION("No such Version"),
     OPV_ABORT_ITEM_PRESENT("Item with OPV ABORT action present"),
-    NO_VERSION_TO_RESTORE("No suitable version to restore");
+    NO_VERSION_TO_RESTORE("No suitable version to restore"),
+    LABEL_EXISTS("Version label already exists"),
+    NO_SUCH_VERSION_LABEL("No such version label");
 
     private final String desc;
 

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionStorageEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionStorageEditor.java?rev=1511327&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionStorageEditor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionStorageEditor.java Wed Aug  7 14:16:18 2013
@@ -0,0 +1,223 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+import java.util.SortedMap;
+
+import javax.annotation.Nonnull;
+
+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.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONLABELS;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_ADD_VERSION_LABELS;
+import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_REMOVE_VERSION_LABELS;
+
+/**
+ * Implements an editor watching for well known properties on the
+ * /jcr:system/jcr:versionStorage node to trigger version operations (like
+ * adding/removing labels or removing versions) on the protected version storage
+ * tree.
+ * <p>
+ * This editor supports the following operations:
+ * <ul>
+ * <li>{@link VersionConstants#REP_ADD_VERSION_LABELS}: adds version labels to
+ * existing version histories. The property is multi-valued and each value is a
+ * PATH, which looks like this:
+ * <code>&lt;version-history-path>/jcr:versionLabels/&lt;version-label>/&lt;version-name></code>.
+ * The <code>version-history-path</code> is a relative path to the version
+ * history node starting at the /jcr:system/jcr:versionStorage node.
+ * An attempt to add a version label that already exists will result in a
+ * {@link CommitFailedException}. </li>
+ * <li>{@link VersionConstants#REP_REMOVE_VERSION_LABELS}: removes version labels from
+ * existing version histories. The property is multi-valued and each value is a
+ * PATH, which looks like this:
+ * <code>&lt;version-history-path>/jcr:versionLabels/&lt;version-label>/&lt;version-name></code>.
+ * The <code>version-history-path</code> is a relative path to the version
+ * history node starting at the /jcr:system/jcr:versionStorage node. The
+ * <code>&lt;version-name></code> part is ignored when labels are removed and
+ * can be anything, though it must be a valid JCR/Oak name.
+ * An attempt to remove a version label, which does not exist, will result in a
+ * {@link CommitFailedException}. </li>
+ * </ul>
+ */
+class VersionStorageEditor extends DefaultEditor {
+
+    private final NodeBuilder versionStorageNode;
+    private final NodeBuilder workspaceRoot;
+    private ReadWriteVersionManager vMgr;
+    private final List<String> pathRemainder;
+
+    private final SortedMap<Integer, Operation> operations = Maps.newTreeMap();
+
+    VersionStorageEditor(@Nonnull NodeBuilder versionStorageNode,
+                         @Nonnull NodeBuilder workspaceRoot) {
+        this(versionStorageNode, workspaceRoot,
+                Arrays.asList(JCR_SYSTEM, JCR_VERSIONSTORAGE));
+    }
+
+    private VersionStorageEditor(@Nonnull NodeBuilder versionStorageNode,
+                         @Nonnull NodeBuilder workspaceRoot,
+                         @Nonnull List<String> pathRemainder) {
+        this.versionStorageNode = versionStorageNode;
+        this.workspaceRoot = workspaceRoot;
+        this.pathRemainder = checkNotNull(pathRemainder);
+    }
+
+    @Override
+    public Editor childNodeChanged(String name,
+                                   NodeState before,
+                                   NodeState after)
+            throws CommitFailedException {
+        if (pathRemainder.isEmpty()) {
+            return null;
+        }
+        if (pathRemainder.get(0).equals(name)) {
+            return new VersionStorageEditor(versionStorageNode, workspaceRoot,
+                    pathRemainder.subList(1, pathRemainder.size()));
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after)
+            throws CommitFailedException {
+        if (after.getName().equals(REP_REMOVE_VERSION_LABELS)) {
+            operations.put(1,
+                    new RemoveVersionLabels(after.getValue(Type.PATHS)));
+            versionStorageNode.removeProperty(after.getName());
+        } else if (after.getName().equals(REP_ADD_VERSION_LABELS)) {
+            operations.put(2,
+                    new AddVersionLabels(after.getValue(Type.PATHS)));
+            versionStorageNode.removeProperty(after.getName());
+        }
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        for (Operation op : operations.values()) {
+            op.perform();
+        }
+    }
+
+    //-------------------------< internal >-------------------------------------
+
+    private ReadWriteVersionManager getVersionManager() {
+        if (vMgr == null) {
+            vMgr = new ReadWriteVersionManager(versionStorageNode, workspaceRoot);
+        }
+        return vMgr;
+    }
+
+    interface Operation {
+
+        void perform() throws CommitFailedException;
+    }
+
+    private class AddVersionLabels implements Operation {
+
+        private Iterable<String> labelPaths;
+
+        public AddVersionLabels(Iterable<String> labelPaths) {
+            this.labelPaths = labelPaths;
+        }
+
+        @Override
+        public void perform() throws CommitFailedException {
+            for (String s : labelPaths) {
+                VersionLabel label = new VersionLabel(s);
+                getVersionManager().addVersionLabel(label.versionHistoryPath, label.label, label.versionName);
+            }
+        }
+    }
+
+    private class RemoveVersionLabels implements Operation {
+
+        private Iterable<String> labelPaths;
+
+        public RemoveVersionLabels(Iterable<String> labelPaths) {
+            this.labelPaths = labelPaths;
+        }
+
+        @Override
+        public void perform() throws CommitFailedException {
+            for (String s : labelPaths) {
+                VersionLabel label = new VersionLabel(s);
+                getVersionManager().removeVersionLabel(label.versionHistoryPath, label.label);
+            }
+        }
+    }
+
+    private static class VersionLabel {
+
+        private final String versionHistoryPath;
+
+        private final String label;
+
+        private final String versionName;
+
+        /**
+         * @param path a label path as defined in the constructor of
+         * {@link VersionStorageEditor}.
+         * @throws IllegalArgumentException if the path is malformed
+         */
+        VersionLabel(@Nonnull String path) throws IllegalArgumentException {
+            checkArgument(!PathUtils.isAbsolute(checkNotNull(path)),
+                    "Version label path must be relative");
+            List<String> elements = Lists.newArrayList(PathUtils.elements(path));
+            // length of the path must be 7:
+            // intermediate versionstorage nodes : 3
+            // version history node : 1
+            // jcr:versionLabels : 1
+            // version label : 1
+            // version name : 1
+            if (elements.size() != 7) {
+                throw new IllegalArgumentException(
+                        "Invalid version label path: " + path);
+            }
+            StringBuilder builder = new StringBuilder();
+            String slash = "";
+            for (String element : elements.subList(0, 4)) {
+                builder.append(slash);
+                builder.append(element);
+                slash = "/";
+            }
+            versionHistoryPath = builder.toString();
+            checkArgument(elements.get(4).equals(JCR_VERSIONLABELS),
+                    "Invalid version label path: " + path);
+            label = elements.get(5);
+            versionName = elements.get(6);
+        }
+    }
+}

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

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

Modified: jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd Wed Aug  7 14:16:18 2013
@@ -569,6 +569,8 @@
 [rep:versionStorage]
   + * (nt:versionHistory) = nt:versionHistory protected ABORT
   + * (rep:versionStorage) = rep:versionStorage protected ABORT
+  - * (UNDEFINED) protected ABORT
+  - * (UNDEFINED) protected multiple ABORT
 
 /**
  * Activities storage

Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Wed Aug  7 14:16:18 2013
@@ -217,7 +217,6 @@
       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.VersionLabelTest
       org.apache.jackrabbit.test.api.version.CopyTest
       org.apache.jackrabbit.test.api.version.RestoreTest#testRestoreWithUUIDConflict
       org.apache.jackrabbit.test.api.version.RestoreTest#testRestoreOrderJcr2

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionHistoryDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionHistoryDelegate.java?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionHistoryDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/VersionHistoryDelegate.java Wed Aug  7 14:16:18 2013
@@ -28,6 +28,7 @@ import java.util.TreeMap;
 import javax.annotation.Nonnull;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.RepositoryException;
+import javax.jcr.version.LabelExistsVersionException;
 import javax.jcr.version.VersionException;
 
 import com.google.common.base.Function;
@@ -63,6 +64,14 @@ public class VersionHistoryDelegate exte
         return VersionDelegate.create(sessionDelegate, rootVersion);
     }
 
+    /**
+     * Gets the version with the given name.
+     *
+     * @param versionName a version name.
+     * @return the version delegate.
+     * @throws VersionException if there is no version with the given name.
+     * @throws RepositoryException if another error occurs.
+     */
     @Nonnull
     public VersionDelegate getVersion(@Nonnull String versionName)
             throws VersionException, RepositoryException {
@@ -142,6 +151,20 @@ public class VersionHistoryDelegate exte
         });
     }
 
+    public void addVersionLabel(@Nonnull VersionDelegate version,
+                                @Nonnull String oakVersionLabel,
+                                boolean moveLabel)
+            throws LabelExistsVersionException, VersionException, RepositoryException {
+        VersionManagerDelegate vMgr = VersionManagerDelegate.create(sessionDelegate);
+        vMgr.addVersionLabel(this, version, oakVersionLabel, moveLabel);
+    }
+
+    public void removeVersionLabel(@Nonnull String oakVersionLabel)
+            throws VersionException, RepositoryException {
+        VersionManagerDelegate vMgr = VersionManagerDelegate.create(sessionDelegate);
+        vMgr.removeVersionLabel(this, oakVersionLabel);
+    }
+
     //-----------------------------< internal >---------------------------------
 
     /**

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=1511327&r1=1511326&r2=1511327&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 Wed Aug  7 14:16:18 2013
@@ -30,21 +30,23 @@ import javax.annotation.Nonnull;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.version.LabelExistsVersionException;
+import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.jcr.version.ReadWriteVersionManager;
+import org.apache.jackrabbit.oak.jcr.version.VersionStorage;
 
 /**
  * {@code VersionManagerDelegate}...
  */
 public class VersionManagerDelegate {
 
-    /**
-     * TODO: this assumes the version store is in the same workspace.
-     */
     private static final String VERSION_STORAGE_PATH
             = '/' + JcrConstants.JCR_SYSTEM + '/' + JcrConstants.JCR_VERSIONSTORAGE;
 
@@ -53,12 +55,13 @@ public class VersionManagerDelegate {
     private final ReadWriteVersionManager versionManager;
 
     public static VersionManagerDelegate create(SessionDelegate sessionDelegate) {
-        Tree versionStorage = sessionDelegate.getRoot().getTree(VERSION_STORAGE_PATH);
-        return new VersionManagerDelegate(sessionDelegate, versionStorage);
+        Tree vsTree = getVersionStorageTree(sessionDelegate.getRoot());
+        return new VersionManagerDelegate(sessionDelegate,
+                new VersionStorage(sessionDelegate.getRoot(), vsTree));
     }
 
     private VersionManagerDelegate(SessionDelegate sessionDelegate,
-                                   Tree versionStorage) {
+                                   VersionStorage versionStorage) {
         this.sessionDelegate = sessionDelegate;
         this.versionManager = new ReadWriteVersionManager(
                 versionStorage,
@@ -163,6 +166,61 @@ public class VersionManagerDelegate {
         }
     }
 
+    /**
+     * Add a version label to the given version history.
+     *
+     * @param versionHistory the version history.
+     * @param version the version.
+     * @param oakVersionLabel the version label.
+     * @param moveLabel whether to move the label if it already exists.
+     * @throws InvalidItemStateException if any of the nodes is stale.
+     * @throws LabelExistsVersionException if moveLabel is false, and an attempt
+     * is made to add a label that already exists in this version history.
+     * @throws VersionException if the specified version does not exist in this
+     * version history or if the specified version is the root version (jcr:rootVersion).
+     * @throws RepositoryException if another error occurs.
+     */
+    public void addVersionLabel(@Nonnull VersionHistoryDelegate versionHistory,
+                                @Nonnull VersionDelegate version,
+                                @Nonnull String oakVersionLabel,
+                                boolean moveLabel)
+            throws InvalidItemStateException, LabelExistsVersionException,
+            VersionException, RepositoryException {
+        // perform operation on fresh storage to not interfere
+        // with pending changes in the workspace.
+        Root fresh = sessionDelegate.getContentSession().getLatestRoot();
+        VersionStorage storage = new VersionStorage(
+                fresh, getVersionStorageTree(fresh));
+        String vhRelPath = PathUtils.relativize(VERSION_STORAGE_PATH,
+                checkNotNull(versionHistory).getPath());
+        versionManager.addVersionLabel(storage, vhRelPath,
+                checkNotNull(version).getName(), checkNotNull(oakVersionLabel),
+                moveLabel);
+    }
+
+    /**
+     * Removes a version label from the given history.
+     *
+     * @param versionHistory the version history.
+     * @param oakVersionLabel the version label.
+     * @throws InvalidItemStateException if any of the nodes is stale.
+     * @throws VersionException if the name label does not exist in this version history.
+     * @throws RepositoryException if another error occurs.
+     */
+    public void removeVersionLabel(@Nonnull VersionHistoryDelegate versionHistory,
+                                   @Nonnull String oakVersionLabel)
+            throws InvalidItemStateException, VersionException, RepositoryException {
+        // perform operation on fresh storage to not interfere
+        // with pending changes in the workspace.
+        Root fresh = sessionDelegate.getContentSession().getLatestRoot();
+        VersionStorage storage = new VersionStorage(
+                fresh, getVersionStorageTree(fresh));
+        String vhRelPath = PathUtils.relativize(VERSION_STORAGE_PATH,
+                checkNotNull(versionHistory).getPath());
+        versionManager.removeVersionLabel(storage, vhRelPath,
+                checkNotNull(oakVersionLabel));
+    }
+
     //----------------------------< internal >----------------------------------
 
     /**
@@ -177,4 +235,14 @@ public class VersionManagerDelegate {
             throws InvalidItemStateException {
         return checkNotNull(nodeDelegate).getTree();
     }
+
+    /**
+     * Returns the version storage tree for the given workspace.
+     * @param workspaceRoot the root of the workspace.
+     * @return the version storage tree.
+     */
+    private static Tree getVersionStorageTree(@Nonnull Root workspaceRoot) {
+        // TODO: this assumes the version store is in the same workspace.
+        return checkNotNull(workspaceRoot).getTree(VERSION_STORAGE_PATH);
+    }
 }

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=1511327&r1=1511326&r2=1511327&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 Wed Aug  7 14:16:18 2013
@@ -16,23 +16,32 @@
  */
 package org.apache.jackrabbit.oak.jcr.version;
 
+import java.util.Collections;
+
 import javax.annotation.Nonnull;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.version.LabelExistsVersionException;
+import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
 import org.apache.jackrabbit.oak.plugins.version.ReadOnlyVersionManager;
-import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
+import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONLABELS;
+import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_ADD_VERSION_LABELS;
+import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_REMOVE_VERSION_LABELS;
 
 /**
  * {@code ReadWriteVersionManager}...
@@ -40,10 +49,10 @@ import static com.google.common.base.Pre
 public class ReadWriteVersionManager extends ReadOnlyVersionManager {
 
     private static final Logger log = LoggerFactory.getLogger(ReadWriteVersionManager.class);
-    private final Tree versionStorage;
+    private final VersionStorage versionStorage;
     private final Root workspaceRoot;
 
-    public ReadWriteVersionManager(@Nonnull Tree versionStorage,
+    public ReadWriteVersionManager(@Nonnull VersionStorage versionStorage,
                                    @Nonnull Root workspaceRoot) {
         this.versionStorage = checkNotNull(versionStorage);
         this.workspaceRoot = checkNotNull(workspaceRoot);
@@ -64,7 +73,7 @@ public class ReadWriteVersionManager ext
     @Override
     @Nonnull
     protected Tree getVersionStorage() {
-        return versionStorage;
+        return versionStorage.getTree();
     }
 
     @Override
@@ -107,14 +116,14 @@ public class ReadWriteVersionManager ext
                     versionable.getPath() + " is not versionable");
         }
         if (isCheckedOut(versionable)) {
-            versionable.setProperty(VersionConstants.JCR_ISCHECKEDOUT,
+            versionable.setProperty(JCR_ISCHECKEDOUT,
                     Boolean.FALSE, Type.BOOLEAN);
             try {
                 getWorkspaceRoot().commit();
                 refresh();
             } catch (CommitFailedException e) {
                 getWorkspaceRoot().refresh();
-                throw newRepositoryException(e);
+                throw e.asRepositoryException();
             }
         }
         return getBaseVersion(getWorkspaceRoot().getTree(versionable.getPath()));
@@ -144,29 +153,73 @@ public class ReadWriteVersionManager ext
                 log.warn("Session has pending changes. Checkout operation will " +
                         "save those changes as well.");
             }
-            versionable.setProperty(VersionConstants.JCR_ISCHECKEDOUT,
+            versionable.setProperty(JCR_ISCHECKEDOUT,
                     Boolean.TRUE, Type.BOOLEAN);
             try {
                 getWorkspaceRoot().commit();
                 refresh();
             } catch (CommitFailedException e) {
                 getWorkspaceRoot().refresh();
-                throw newRepositoryException(e);
+                throw e.asRepositoryException();
             }
         }
     }
 
-    // TODO: more methods that modify versions
+    public void addVersionLabel(@Nonnull VersionStorage versionStorage,
+                                @Nonnull String versionHistoryOakRelPath,
+                                @Nonnull String versionOakName,
+                                @Nonnull String oakVersionLabel,
+                                boolean moveLabel) throws RepositoryException {
+        Tree versionHistory = TreeUtil.getTree(checkNotNull(versionStorage.getTree()),
+                checkNotNull(versionHistoryOakRelPath));
+        Tree labels = checkNotNull(versionHistory).getChild(JCR_VERSIONLABELS);
+        if (labels.hasProperty(checkNotNull(oakVersionLabel))) {
+            if (moveLabel) {
+                String labelPath = PathUtils.concat(versionHistoryOakRelPath,
+                        JCR_VERSIONLABELS, oakVersionLabel, "dummy");
+                versionStorage.getTree().setProperty(REP_REMOVE_VERSION_LABELS,
+                        Collections.singleton(labelPath), Type.PATHS);
+            } else {
+                throw new LabelExistsVersionException("Version label '"
+                        + oakVersionLabel + "' already exists on this version history");
+            }
+        }
+        String labelPath = PathUtils.concat(versionHistoryOakRelPath,
+                JCR_VERSIONLABELS, oakVersionLabel, checkNotNull(versionOakName));
+        versionStorage.getTree().setProperty(REP_ADD_VERSION_LABELS,
+                Collections.singleton(labelPath), Type.PATHS);
+        try {
+            checkNotNull(versionStorage).commit();
+            refresh();
+        } catch (CommitFailedException e) {
+            versionStorage.refresh();
+            throw e.asRepositoryException();
+        }
+    }
 
-    /**
-     * Wraps the given {@link CommitFailedException} instance using the
-     * appropriate {@link RepositoryException} subclass based on the
-     * {@link CommitFailedException#getType() type} of the given exception.
-     *
-     * @param exception typed commit failure exception
-     * @return matching repository exception
-     */
-    private static RepositoryException newRepositoryException(@Nonnull CommitFailedException exception) {
-        return exception.asRepositoryException();
+    public void removeVersionLabel(@Nonnull VersionStorage versionStorage,
+                                   @Nonnull String versionHistoryOakRelPath,
+                                   @Nonnull String oakVersionLabel)
+            throws RepositoryException {
+        Tree versionHistory = TreeUtil.getTree(checkNotNull(versionStorage.getTree()),
+                checkNotNull(versionHistoryOakRelPath));
+        Tree labels = checkNotNull(versionHistory).getChild(JCR_VERSIONLABELS);
+        if (!labels.hasProperty(oakVersionLabel)) {
+            throw new VersionException("Version label " + oakVersionLabel +
+                    " does not exist on this version history");
+        }
+        String labelPath = PathUtils.concat(versionHistoryOakRelPath,
+                JCR_VERSIONLABELS, oakVersionLabel, "dummy");
+        versionStorage.getTree().setProperty(REP_REMOVE_VERSION_LABELS,
+                Collections.singleton(labelPath), Type.PATHS);
+        try {
+            checkNotNull(versionStorage).commit();
+            refresh();
+        } catch (CommitFailedException e) {
+            versionStorage.refresh();
+            throw e.asRepositoryException();
+        }
     }
+
+    // TODO: more methods that modify versions
 }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java?rev=1511327&r1=1511326&r2=1511327&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java Wed Aug  7 14:16:18 2013
@@ -33,16 +33,19 @@ import javax.jcr.version.VersionIterator
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterators;
+
 import org.apache.jackrabbit.commons.iterator.FrozenNodeIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.VersionIteratorAdapter;
 import org.apache.jackrabbit.oak.jcr.NodeImpl;
 import org.apache.jackrabbit.oak.jcr.SessionContext;
 import org.apache.jackrabbit.oak.jcr.delegate.VersionDelegate;
 import org.apache.jackrabbit.oak.jcr.delegate.VersionHistoryDelegate;
+import org.apache.jackrabbit.oak.jcr.operation.SessionOperation;
 import org.apache.jackrabbit.oak.util.TODO;
 
 /**
  * {@code VersionHistoryImpl}...
+ * TODO: wrap all calls with sessionDelegate.perform()
  */
 public class VersionHistoryImpl extends NodeImpl<VersionHistoryDelegate>
         implements VersionHistory {
@@ -106,18 +109,34 @@ public class VersionHistoryImpl extends 
     }
 
     @Override
-    public void addVersionLabel(String versionName,
-                                String label,
-                                boolean moveLabel)
+    public void addVersionLabel(final String versionName,
+                                final String label,
+                                final boolean moveLabel)
             throws LabelExistsVersionException, VersionException,
             RepositoryException {
-        TODO.unimplemented().doNothing();
+        sessionDelegate.perform(new SessionOperation<Void>() {
+            @Override
+            public Void perform() throws RepositoryException {
+                String oakLabel = sessionContext.getOakName(label);
+                // will throw VersionException if version does not exist
+                VersionDelegate version = dlg.getVersion(versionName);
+                dlg.addVersionLabel(version, oakLabel, moveLabel);
+                return null;
+            }
+        });
     }
 
     @Override
-    public void removeVersionLabel(String label)
+    public void removeVersionLabel(final String label)
             throws VersionException, RepositoryException {
-        TODO.unimplemented().doNothing();
+        sessionDelegate.perform(new SessionOperation<Void>() {
+            @Override
+            public Void perform() throws RepositoryException {
+                String oakLabel = sessionContext.getOakName(label);
+                dlg.removeVersionLabel(oakLabel);
+                return null;
+            }
+        });
     }
 
     @Override

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.java?rev=1511327&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.java Wed Aug  7 14:16:18 2013
@@ -0,0 +1,66 @@
+/*
+ * 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.jcr.version;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+
+/**
+ * Simple abstraction of the version storage.
+ */
+public class VersionStorage {
+
+    private final Root root;
+    private final Tree versionStorage;
+
+    public VersionStorage(@Nonnull Root versionStorageRoot,
+                   @Nonnull Tree versionStorageTree) {
+        this.root = versionStorageRoot;
+        this.versionStorage = versionStorageTree;
+    }
+
+    /**
+     * The version storage tree. I.e. the tree at path
+     * <code>/jcr:system/jcr:versionStorage</code>, though the returned
+     * tree instance may not necessarily return this path on
+     * {@link Tree#getPath()}!
+     *
+     * @return the version storage tree.
+     */
+    Tree getTree() {
+        return versionStorage;
+    }
+
+    /**
+     * Commits changes made to the version storage tree.
+     *
+     * @throws CommitFailedException if the commit fails.
+     */
+    void commit() throws CommitFailedException {
+        root.commit();
+    }
+
+    /**
+     * Reverts all changes made to the version storage tree.
+     */
+    void refresh() {
+        root.refresh();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionStorage.java
------------------------------------------------------------------------------
    svn:eol-style = native

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