Author: mreutegg
Date: Wed Dec 14 15:20:22 2016
New Revision: 1774264

URL: http://svn.apache.org/viewvc?rev=1774264&view=rev
Log:
OAK-3328: checked-in state should only affect properties with OPV!=IGNORE

Apply patch provided by Marco Piovesana

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
    
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
    
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/PropertyImpl.java
    
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java
    
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/OpvIgnoreTest.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=1774264&r1=1774263&r2=1774264&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 14 15:20:22 2016
@@ -27,12 +27,15 @@ import static org.apache.jackrabbit.oak.
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.version.OnParentVersionAction;
 
 import org.apache.jackrabbit.JcrConstants;
 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.plugins.lock.LockConstants;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -85,10 +88,10 @@ class VersionEditor implements Editor {
             // deleted or versionable -> check if it was checked in
             // a node cannot be modified if it was checked in
             // unless it has a new identifier
-            isReadOnly = wasCheckedIn() && !hasNewIdentifier();
+            isReadOnly = wasCheckedIn() && !hasNewIdentifier() && 
!isIgnoreOnOPV();
         } else {
             // otherwise inherit from parent
-            isReadOnly = parent != null && parent.isReadOnly;
+            isReadOnly = parent != null && parent.isReadOnly && 
!isIgnoreOnOPV();
         }
     }
 
@@ -108,7 +111,7 @@ class VersionEditor implements Editor {
             vMgr.restore(node, after.getValue(Type.REFERENCE), null);
             return;
         }
-        if (!isReadOnly) {
+        if (!isReadOnly || getOPV(after) == OnParentVersionAction.IGNORE) {
             return;
         }
         // JCR allows to put a lock on a checked in node.
@@ -124,7 +127,7 @@ class VersionEditor implements Editor {
     public void propertyChanged(PropertyState before, PropertyState after)
             throws CommitFailedException {
         if (!isVersionable()) {
-            if (!isVersionProperty(after) && isReadOnly) {
+            if (!isVersionProperty(after) && isReadOnly && getOPV(after) != 
OnParentVersionAction.IGNORE) {
                 throwCheckedIn("Cannot change property " + after.getName()
                         + " on checked in node");
             }
@@ -146,7 +149,7 @@ class VersionEditor implements Editor {
             vMgr.restore(node, baseVersion, null);
         } else if (isVersionProperty(after)) {
             throwProtected(after.getName());
-        } else if (isReadOnly) {
+        } else if (isReadOnly && getOPV(after) != 
OnParentVersionAction.IGNORE) {
             throwCheckedIn("Cannot change property " + after.getName()
                     + " on checked in node");
         }
@@ -156,7 +159,7 @@ class VersionEditor implements Editor {
     public void propertyDeleted(PropertyState before)
             throws CommitFailedException {
         if (isReadOnly) {
-            if (!isVersionProperty(before) && !isLockProperty(before)) {
+            if (!isVersionProperty(before) && !isLockProperty(before) && 
getOPV(before) != OnParentVersionAction.IGNORE) {
                 throwCheckedIn("Cannot delete property on checked in node");
             }
         }
@@ -253,4 +256,27 @@ class VersionEditor implements Editor {
         throw new CommitFailedException(CommitFailedException.CONSTRAINT, 100,
                 "Property is protected: " + name);
     }
+
+    private boolean isIgnoreOnOPV() throws CommitFailedException {
+        if (this.parent != null) {
+            try {
+                NodeDefinition definition = 
this.vMgr.getNodeTypeManager().getDefinition(TreeFactory.createTree(parent.node),
 this.name);
+                return definition.getOnParentVersion() == 
OnParentVersionAction.IGNORE;
+            } catch (Exception e) {
+                throw new CommitFailedException(CommitFailedException.VERSION,
+                        
VersionExceptionCode.UNEXPECTED_REPOSITORY_EXCEPTION.ordinal(), e.getMessage());
+            }
+        }
+        return false;
+    }
+
+    private int getOPV(PropertyState property) throws CommitFailedException {
+        try {
+            return 
this.vMgr.getNodeTypeManager().getDefinition(TreeFactory.createReadOnlyTree(this.node.getNodeState()),
+                    property, false).getOnParentVersion();
+        } catch (Exception e) {
+            throw new CommitFailedException(CommitFailedException.VERSION,
+                    
VersionExceptionCode.UNEXPECTED_REPOSITORY_EXCEPTION.ordinal(), e.getMessage());
+        }
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java?rev=1774264&r1=1774263&r2=1774264&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
 Wed Dec 14 15:20:22 2016
@@ -61,6 +61,7 @@ import javax.jcr.nodetype.ConstraintViol
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.version.OnParentVersionAction;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
@@ -1364,7 +1365,7 @@ public class NodeImpl<T extends NodeDele
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!isCheckedOut()) {
+                if (!isCheckedOut() && getOPV(dlg.getTree(), state) != 
OnParentVersionAction.IGNORE) {
                     throw new VersionException(format(
                             "Cannot set property. Node [%s] is checked in.", 
getNodePath()));
                 }
@@ -1400,7 +1401,7 @@ public class NodeImpl<T extends NodeDele
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!isCheckedOut()) {
+                if (!isCheckedOut() && getOPV(dlg.getTree(), state) != 
OnParentVersionAction.IGNORE) {
                     throw new VersionException(format(
                             "Cannot set property. Node [%s] is checked in.", 
getNodePath()));
                 }
@@ -1444,7 +1445,8 @@ public class NodeImpl<T extends NodeDele
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!isCheckedOut()) {
+                PropertyDelegate property = dlg.getPropertyOrNull(oakName);
+                if (!isCheckedOut() && getOPV(dlg.getTree(), 
property.getPropertyState()) != OnParentVersionAction.IGNORE) {
                     throw new VersionException(format(
                             "Cannot remove property. Node [%s] is checked 
in.", getNodePath()));
                 }
@@ -1599,6 +1601,13 @@ public class NodeImpl<T extends NodeDele
         return dlg.getPath();
     }
 
+    private int getOPV(Tree nodeTree, PropertyState property)
+            throws RepositoryException {
+        return getNodeTypeManager().getDefinition(nodeTree,
+                property, false).getOnParentVersion();
+
+    }
+
     private static class PropertyIteratorDelegate {
         private final NodeDelegate node;
         private final Predicate<PropertyDelegate> predicate;

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/PropertyImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/PropertyImpl.java?rev=1774264&r1=1774263&r2=1774264&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/PropertyImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/PropertyImpl.java
 Wed Dec 14 15:20:22 2016
@@ -37,6 +37,7 @@ import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.OnParentVersionAction;
 import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.oak.api.Tree.Status;
@@ -113,7 +114,7 @@ public class PropertyImpl extends ItemIm
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!getParent().isCheckedOut()) {
+                if (!getParent().isCheckedOut() && 
getDefinition().getOnParentVersion() != OnParentVersionAction.IGNORE) {
                     throw new VersionException(
                             "Cannot set property. Node is checked in.");
                 }
@@ -463,7 +464,7 @@ public class PropertyImpl extends ItemIm
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!getParent().isCheckedOut()) {
+                if (!getParent().isCheckedOut() && 
getDefinition().getOnParentVersion() != OnParentVersionAction.IGNORE) {
                     throw new VersionException(
                             "Cannot set property. Node is checked in.");
                 }
@@ -499,7 +500,7 @@ public class PropertyImpl extends ItemIm
             @Override
             public void checkPreconditions() throws RepositoryException {
                 super.checkPreconditions();
-                if (!getParent().isCheckedOut()) {
+                if (!getParent().isCheckedOut() && 
getDefinition().getOnParentVersion() != OnParentVersionAction.IGNORE) {
                     throw new VersionException(
                             "Cannot set property. Node is checked in.");
                 }

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=1774264&r1=1774263&r2=1774264&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
 Wed Dec 14 15:20:22 2016
@@ -33,8 +33,10 @@ import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.util.TraversingItemVisitor;
+import javax.jcr.version.OnParentVersionAction;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
@@ -50,6 +52,7 @@ import org.apache.jackrabbit.oak.jcr.del
 import org.apache.jackrabbit.oak.jcr.lock.LockManagerImpl;
 import org.apache.jackrabbit.oak.jcr.session.SessionContext;
 import org.apache.jackrabbit.oak.jcr.session.operation.SessionOperation;
+import 
org.apache.jackrabbit.oak.plugins.nodetype.write.ReadWriteNodeTypeManager;
 
 public class VersionManagerImpl implements VersionManager {
 
@@ -257,7 +260,20 @@ public class VersionManagerImpl implemen
                 if (nodeDelegate == null) {
                     throw new PathNotFoundException(absPath);
                 }
-                return versionManagerDelegate.isCheckedOut(nodeDelegate);
+                boolean isCheckedOut = 
versionManagerDelegate.isCheckedOut(nodeDelegate);
+                if (!isCheckedOut) {
+                    // check OPV
+                    ReadWriteNodeTypeManager ntMgr = 
sessionContext.getWorkspace().getNodeTypeManager();
+                    NodeDelegate parent = nodeDelegate.getParent();
+                    NodeDefinition definition;
+                    if (parent == null) {
+                        definition = ntMgr.getRootDefinition();
+                    } else {
+                        definition = ntMgr.getDefinition(parent.getTree(), 
nodeDelegate.getTree());
+                    }
+                    isCheckedOut = definition.getOnParentVersion() == 
OnParentVersionAction.IGNORE;
+                }
+                return isCheckedOut;
             }
         });
     }

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/OpvIgnoreTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/OpvIgnoreTest.java?rev=1774264&r1=1774263&r2=1774264&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/OpvIgnoreTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/version/OpvIgnoreTest.java
 Wed Dec 14 15:20:22 2016
@@ -18,6 +18,13 @@ package org.apache.jackrabbit.oak.jcr.ve
 
 import javax.annotation.Nonnull;
 import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeDefinitionTemplate;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.NodeTypeTemplate;
+import javax.jcr.nodetype.PropertyDefinitionTemplate;
 import javax.jcr.security.AccessControlManager;
 import javax.jcr.security.Privilege;
 import javax.jcr.version.OnParentVersionAction;
@@ -32,6 +39,8 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
 import org.apache.jackrabbit.test.AbstractJCRTest;
 
+import java.util.List;
+
 /**
  * Test OPV IGNORE
  */
@@ -91,4 +100,147 @@ public class OpvIgnoreTest extends Abstr
         assertTrue(frozenChild.hasNode(nodeName2));
         assertFalse(frozenChild.hasNode(AccessControlConstants.REP_POLICY));
     }
+
+    //OAK-3328
+    public void testWritePropertyWithIgnoreOPVAfterCheckIn() throws 
RepositoryException {
+        Node ignoreTestNode = testRootNode.addNode("ignoreTestNode", 
JcrConstants.NT_UNSTRUCTURED);
+        String ignoredPropertyName = "ignoredProperty";
+        NodeTypeTemplate mixinWithIgnoreProperty = 
createNodeTypeWithIgnoreOPVProperty(ignoredPropertyName);
+
+        Node node = ignoreTestNode.addNode("testNode", testNodeType);
+        node.addMixin(mixinWithIgnoreProperty.getName());
+        node.setProperty(ignoredPropertyName, "initial value");
+        node.addMixin(mixVersionable);
+        superuser.save();
+        VersionManager vMgr = superuser.getWorkspace().getVersionManager();
+        if (!node.isCheckedOut()) {
+            vMgr.checkout(node.getPath());
+        }
+        vMgr.checkin(node.getPath());
+        node.setProperty(ignoredPropertyName, "next value");
+        superuser.save();
+        Property ignoreProperty = node.getProperty(ignoredPropertyName);
+        assertEquals("next value", ignoreProperty.getString());
+    }
+
+    //OAK-3328
+    public void testRemovePropertyWithIgnoreOPVAfterCheckIn() throws 
RepositoryException {
+        Node ignoreTestNode = testRootNode.addNode("ignoreTestNode", 
JcrConstants.NT_UNSTRUCTURED);
+        String ignoredPropertyName = "test:ignoredProperty";
+        NodeTypeTemplate mixinWithIgnoreProperty = 
createNodeTypeWithIgnoreOPVProperty(ignoredPropertyName);
+
+        Node node = ignoreTestNode.addNode("testNode", testNodeType);
+        node.addMixin(mixinWithIgnoreProperty.getName());
+        node.setProperty(ignoredPropertyName, "initial value");
+        node.addMixin(mixVersionable);
+        superuser.save();
+        VersionManager vMgr = superuser.getWorkspace().getVersionManager();
+        if (!node.isCheckedOut()) {
+            vMgr.checkout(node.getPath());
+        }
+        vMgr.checkin(node.getPath());
+        node.getProperty(ignoredPropertyName).remove();
+        superuser.save();
+        assertFalse(node.hasProperty(ignoredPropertyName));
+    }
+
+    //OAK-3328
+    public void testAddChildNodeWithIgnoreOPVAfterCheckIn() throws 
RepositoryException {
+        Node ignoreTestNode = testRootNode.addNode("ignoreTestNode", 
JcrConstants.NT_UNSTRUCTURED);
+        String nodeTypeName = "testOpvIgnore";
+        NodeDefinitionTemplate nodeDefinition = 
createNodeDefinitionWithIgnoreOPVNode(nodeTypeName);
+
+        ignoreTestNode.addMixin(JcrConstants.MIX_VERSIONABLE);
+        ignoreTestNode.addMixin(nodeTypeName);
+        superuser.save();
+
+        VersionManager vMgr = superuser.getWorkspace().getVersionManager();
+        if (!ignoreTestNode.isCheckedOut()) {
+            vMgr.checkout(ignoreTestNode.getPath());
+        }
+        vMgr.checkin(ignoreTestNode.getPath());
+
+        Node expected = ignoreTestNode.addNode(nodeDefinition.getName(), 
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+
+        superuser.save();
+        Node childNode = ignoreTestNode.getNode(nodeDefinition.getName());
+        assertTrue(expected.isSame(childNode));
+    }
+
+    //OAK-3328
+    public void testRemoveChildNodeWithIgnoreOPVAfterCheckIn() throws 
RepositoryException {
+        Node ignoreTestNode = testRootNode.addNode("ignoreTestNode", 
JcrConstants.NT_UNSTRUCTURED);
+        String nodeTypeName = "testOpvIgnore";
+        NodeDefinitionTemplate nodeDefinition = 
createNodeDefinitionWithIgnoreOPVNode(nodeTypeName);
+
+        ignoreTestNode.addMixin(JcrConstants.MIX_VERSIONABLE);
+        ignoreTestNode.addMixin(nodeTypeName);
+        Node expected = ignoreTestNode.addNode(nodeDefinition.getName(), 
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        superuser.save();
+
+        VersionManager vMgr = superuser.getWorkspace().getVersionManager();
+        if (!ignoreTestNode.isCheckedOut()) {
+            vMgr.checkout(ignoreTestNode.getPath());
+        }
+        vMgr.checkin(ignoreTestNode.getPath());
+
+        Node child = ignoreTestNode.getNode(nodeDefinition.getName());
+        child.remove();
+
+        superuser.save();
+        assertFalse(ignoreTestNode.hasNode(nodeDefinition.getName()));
+    }
+
+    //OAK-3328
+    public void testIsCheckedOutOPVIgnore() throws RepositoryException {
+        Node test = testRootNode.addNode("test", JcrConstants.NT_UNSTRUCTURED);
+        String nodeTypeName = "testOpvIgnore";
+        NodeDefinitionTemplate nodeDefinition = 
createNodeDefinitionWithIgnoreOPVNode(nodeTypeName);
+
+        test.addMixin(JcrConstants.MIX_VERSIONABLE);
+        test.addMixin(nodeTypeName);
+        Node ignored = test.addNode(nodeDefinition.getName(), 
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        superuser.save();
+
+        VersionManager vMgr = superuser.getWorkspace().getVersionManager();
+        vMgr.checkin(test.getPath());
+
+        assertTrue(ignored.isCheckedOut());
+        assertTrue(vMgr.isCheckedOut(ignored.getPath()));
+    }
+
+    private NodeTypeTemplate createNodeTypeWithIgnoreOPVProperty(String 
propertyName) throws RepositoryException {
+        NodeTypeManager manager = 
superuser.getWorkspace().getNodeTypeManager();
+
+        NodeTypeTemplate nt = manager.createNodeTypeTemplate();
+        nt.setName("testType");
+        nt.setMixin(true);
+        PropertyDefinitionTemplate opt = 
manager.createPropertyDefinitionTemplate();
+        opt.setMandatory(false);
+        opt.setName(propertyName);
+        opt.setRequiredType(PropertyType.STRING);
+        opt.setOnParentVersion(OnParentVersionAction.IGNORE);
+        List pdt = nt.getPropertyDefinitionTemplates();
+        pdt.add(opt);
+        manager.registerNodeType(nt, true);
+
+        return nt;
+    }
+
+    private NodeDefinitionTemplate 
createNodeDefinitionWithIgnoreOPVNode(String nodeTypeName) throws 
RepositoryException {
+        NodeTypeManager manager = 
superuser.getWorkspace().getNodeTypeManager();
+
+        NodeDefinitionTemplate def = manager.createNodeDefinitionTemplate();
+        def.setOnParentVersion(OnParentVersionAction.IGNORE);
+        def.setName("child");
+        def.setRequiredPrimaryTypeNames(new String[]{JcrConstants.NT_BASE});
+
+        NodeTypeTemplate tmpl = manager.createNodeTypeTemplate();
+        tmpl.setName(nodeTypeName);
+        tmpl.setMixin(true);
+        tmpl.getNodeDefinitionTemplates().add(def);
+        manager.registerNodeType(tmpl, true);
+
+        return def;
+    }
 }
\ No newline at end of file


Reply via email to