Author: mreutegg
Date: Wed Dec 18 14:52:04 2013
New Revision: 1551963

URL: http://svn.apache.org/r1551963
Log:
OAK-1272: Remove and create of same versionable node is seen as a change in uuid

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
    
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/VersionableTest.java

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=1551963&r1=1551962&r2=1551963&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
 Wed Dec 18 14:52:04 2013
@@ -34,6 +34,8 @@ import org.apache.jackrabbit.oak.spi.sta
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.RESTORE_PREFIX;
 
@@ -45,22 +47,26 @@ class VersionEditor implements Editor {
     private final VersionEditor parent;
     private final ReadWriteVersionManager vMgr;
     private final NodeBuilder node;
+    private final String name;
     private Boolean isVersionable = null;
     private NodeState before;
     private NodeState after;
-    private boolean wasReadOnly;
+    private boolean isReadOnly;
 
     public VersionEditor(@Nonnull NodeBuilder versionStore,
                          @Nonnull NodeBuilder workspaceRoot) {
         this(null, new ReadWriteVersionManager(checkNotNull(versionStore),
-                checkNotNull(workspaceRoot)), workspaceRoot);
+                checkNotNull(workspaceRoot)), workspaceRoot, "");
     }
 
     VersionEditor(@Nullable VersionEditor parent,
-                  @Nonnull ReadWriteVersionManager vMgr, @Nonnull NodeBuilder 
node) {
+                  @Nonnull ReadWriteVersionManager vMgr,
+                  @Nonnull NodeBuilder node,
+                  @Nonnull String name) {
         this.parent = parent;
         this.vMgr = checkNotNull(vMgr);
         this.node = checkNotNull(node);
+        this.name = checkNotNull(name);
     }
 
     @Override
@@ -71,13 +77,15 @@ class VersionEditor implements Editor {
         if (isVersionable()) {
             vMgr.getOrCreateVersionHistory(node);
         }
-        // calculate wasReadOnly state
+        // calculate isReadOnly state
         if (after.exists() || isVersionable()) {
             // deleted or versionable -> check if it was checked in
-            wasReadOnly = wasCheckedIn();
+            // a node cannot be modified if it was checked in
+            // unless it has a new identifier
+            isReadOnly = wasCheckedIn() && !hasNewIdentifier();
         } else {
             // otherwise inherit from parent
-            wasReadOnly = parent != null && parent.wasReadOnly;
+            isReadOnly = parent != null && parent.isReadOnly;
         }
     }
 
@@ -91,13 +99,13 @@ class VersionEditor implements Editor {
             throws CommitFailedException {
         if (after.getName().equals(JCR_BASEVERSION)
                 && this.after.hasProperty(JcrConstants.JCR_VERSIONHISTORY)
-                && !this.after.hasProperty(JcrConstants.JCR_ISCHECKEDOUT)
+                && !this.after.hasProperty(JCR_ISCHECKEDOUT)
                 && !this.before.exists()) {
             // sentinel node for restore
             vMgr.restore(node, after.getValue(Type.REFERENCE), null);
             return;
         }
-        if (!wasReadOnly) {
+        if (!isReadOnly) {
             return;
         }
         // JCR allows to put a lock on a checked in node.
@@ -113,14 +121,14 @@ class VersionEditor implements Editor {
     public void propertyChanged(PropertyState before, PropertyState after)
             throws CommitFailedException {
         if (!isVersionable()) {
-            if (!isVersionProperty(after) && wasCheckedIn()) {
+            if (!isVersionProperty(after) && isReadOnly) {
                 throwCheckedIn("Cannot change property " + after.getName()
                         + " on checked in node");
             }
             return;
         }
         String propName = after.getName();
-        if (propName.equals(VersionConstants.JCR_ISCHECKEDOUT)) {
+        if (propName.equals(JCR_ISCHECKEDOUT)) {
             if (wasCheckedIn()) {
                 vMgr.checkout(node);
             } else {
@@ -135,7 +143,7 @@ class VersionEditor implements Editor {
             vMgr.restore(node, baseVersion, null);
         } else if (isVersionProperty(after)) {
             throwProtected(after.getName());
-        } else if (wasReadOnly) {
+        } else if (isReadOnly) {
             throwCheckedIn("Cannot change property " + after.getName()
                     + " on checked in node");
         }
@@ -144,7 +152,7 @@ class VersionEditor implements Editor {
     @Override
     public void propertyDeleted(PropertyState before)
             throws CommitFailedException {
-        if (wasReadOnly) {
+        if (isReadOnly) {
             if (!isVersionProperty(before) && !isLockProperty(before)) {
                 throwCheckedIn("Cannot delete property on checked in node");
             }
@@ -159,12 +167,12 @@ class VersionEditor implements Editor {
     @Override
     public Editor childNodeChanged(String name, NodeState before,
             NodeState after) {
-        return new VersionEditor(this, vMgr, node.child(name));
+        return new VersionEditor(this, vMgr, node.child(name), name);
     }
 
     @Override
     public Editor childNodeDeleted(String name, NodeState before) {
-        return new VersionEditor(this, vMgr, MISSING_NODE.builder());
+        return new VersionEditor(this, vMgr, MISSING_NODE.builder(), name);
     }
 
     /**
@@ -195,12 +203,11 @@ class VersionEditor implements Editor {
 
     /**
      * @return {@code true} if this node <b>was</b> checked in. That is,
-     *         this method checks the base state for the jcr:isCheckedOut
+     *         this method checks the before state for the jcr:isCheckedOut
      *         property.
      */
     private boolean wasCheckedIn() {
-        PropertyState prop = before
-                .getProperty(VersionConstants.JCR_ISCHECKEDOUT);
+        PropertyState prop = before.getProperty(JCR_ISCHECKEDOUT);
         if (prop != null) {
             return !prop.getValue(Type.BOOLEAN);
         }
@@ -208,6 +215,34 @@ class VersionEditor implements Editor {
         return parent != null && parent.wasCheckedIn();
     }
 
+    private boolean hasNewIdentifier() {
+        String beforeId = buildBeforeIdentifier(new 
StringBuilder()).toString();
+        String afterId = buildAfterIdentifier(new StringBuilder()).toString();
+        return !beforeId.equals(afterId);
+    }
+
+    private StringBuilder buildBeforeIdentifier(StringBuilder identifier) {
+        String uuid = before.getString(JCR_UUID);
+        if (uuid != null) {
+            identifier.append(uuid);
+        } else if (parent != null) {
+            parent.buildBeforeIdentifier(identifier);
+            identifier.append("/").append(name);
+        }
+        return identifier;
+    }
+
+    private StringBuilder buildAfterIdentifier(StringBuilder identifier) {
+        String uuid = after.getString(JCR_UUID);
+        if (uuid != null) {
+            identifier.append(uuid);
+        } else if (parent != null) {
+            parent.buildAfterIdentifier(identifier);
+            identifier.append("/").append(name);
+        }
+        return identifier;
+    }
+
     private static void throwCheckedIn(String msg)
             throws CommitFailedException {
         throw new CommitFailedException(CommitFailedException.VERSION,

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/VersionableTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/VersionableTest.java?rev=1551963&r1=1551962&r2=1551963&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/VersionableTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/VersionableTest.java
 Wed Dec 18 14:52:04 2013
@@ -17,13 +17,13 @@
 package org.apache.jackrabbit.oak.jcr.version;
 
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionManager;
-
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.test.AbstractJCRTest;
 
@@ -69,7 +69,7 @@ public class VersionableTest extends Abs
 
     /**
      * Test from Jackrabbit: JCR-3635 (OAK-940)
-     *
+     * <p/>
      * Tests the case when a node already has a manual set
      * JcrConstants.JCR_FROZENUUID property and is versioned. The manual set
      * frozenUuid will overwrite the one that is automatically assigned by the
@@ -113,4 +113,145 @@ public class VersionableTest extends Abs
         assertTrue(node.isCheckedOut());
         assertTrue(newNode.isNew());
     }
+
+    // OAK-1272
+    public void testRemoveAndCreateSameVersionableChildNode() throws Exception 
{
+        // create parent
+        Node parentNode = testRootNode.addNode("parent");
+        parentNode.setPrimaryType(ntUnstructured);
+        parentNode.setProperty("name", "sample");
+        // create a versionable child
+        Node createdNode = parentNode.addNode("versionablechild", 
"nt:unstructured");
+        createdNode.addMixin(mixVersionable);
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(testRootNode.getPath()+"/parent/versionablechild");
+
+        // delete and create exact same node
+        Node parent = testRootNode.getNode("parent");
+
+        // remove children
+        NodeIterator nodes = parent.getNodes();
+        while (nodes.hasNext()) {
+            Node childNode = nodes.nextNode();
+            childNode.remove();
+        }
+
+        // create again versionable child node
+        Node recreatedNode = parent.addNode("versionablechild", 
ntUnstructured);
+        recreatedNode.addMixin(mixVersionable);
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithChangedProperty() throws 
Exception {
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.setProperty(propertyName1, "foo");
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.setProperty(propertyName1, "bar");
+
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithNewProperty() throws Exception {
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.setProperty(propertyName1, "bar");
+
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithRemovedProperty() throws 
Exception {
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.setProperty(propertyName1, "foo");
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithChangedChild() throws Exception 
{
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.addNode(nodeName2, ntUnstructured).setProperty(propertyName1, 
"foo");
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.addNode(nodeName2, ntUnstructured).setProperty(propertyName1, 
"bar");
+
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithRemovedChild() throws Exception 
{
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.addNode(nodeName2, ntUnstructured).setProperty(propertyName1, 
"foo");
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+
+        superuser.save();
+    }
+
+    // Oak-1272
+    public void testRecreateVersionableNodeWithAddedChild() throws Exception {
+        Node node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        superuser.save();
+
+        VersionManager vm = superuser.getWorkspace().getVersionManager();
+        vm.checkin(node.getPath());
+
+        // re-create node
+        node.remove();
+        node = testRootNode.addNode(nodeName1, ntUnstructured);
+        node.addMixin(mixVersionable);
+        node.addNode(nodeName2, ntUnstructured).setProperty(propertyName1, 
"bar");
+
+        superuser.save();
+    }
+
 }


Reply via email to