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();
+ }
+
}