Author: angela
Date: Thu Jan 16 14:42:34 2014
New Revision: 1558818
URL: http://svn.apache.org/r1558818
Log:
OAK-770 : NodeImpl should implement JackrabbitNode (wip)
Modified:
jackrabbit/oak/trunk/oak-jcr/pom.xml
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/JackrabbitNodeTest.java
Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1558818&r1=1558817&r2=1558818&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Thu Jan 16 14:42:34 2014
@@ -116,9 +116,6 @@
org.apache.jackrabbit.oak.jcr.security.authorization.CopyTest#testCopyInvisibleProperty
<!-- OAK-920 -->
org.apache.jackrabbit.oak.jcr.security.authorization.CopyTest#testCopyInvisibleAcContent
<!-- OAK-920 -->
- <!-- JackrabbitNode -->
- org.apache.jackrabbit.oak.jcr.JackrabbitNodeTest#testSetMixins
<!-- OAK-770 -->
-
<!-- Query -->
org.apache.jackrabbit.test.api.query.ElementTest#testElementTestNameTestSomeNTWithSNS
<!-- OAK-203 -->
org.apache.jackrabbit.test.api.query.SaveTest#testItemExistsException
<!-- OAK-203 -->
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java?rev=1558818&r1=1558817&r2=1558818&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
Thu Jan 16 14:42:34 2014
@@ -16,6 +16,8 @@
*/
package org.apache.jackrabbit.oak.jcr.delegate;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -53,7 +55,6 @@ import static com.google.common.collect.
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.newLinkedHashSet;
-import static java.util.Collections.singletonList;
import static org.apache.jackrabbit.JcrConstants.JCR_ISMIXIN;
import static org.apache.jackrabbit.JcrConstants.JCR_LOCKISDEEP;
import static org.apache.jackrabbit.JcrConstants.JCR_LOCKOWNER;
@@ -117,7 +118,7 @@ public class NodeDelegate extends ItemDe
@Override
@CheckForNull
public NodeDelegate getParent() {
- return tree.isRoot() || !tree.getParent().exists()
+ return tree.isRoot() || !tree.getParent().exists()
? null
: new NodeDelegate(sessionDelegate, tree.getParent());
}
@@ -307,7 +308,7 @@ public class NodeDelegate extends ItemDe
* the value is higher than max). If the implementation does not know the
* exact value, and the child node count is higher than max, it may return
* Long.MAX_VALUE. The cost of the operation is at most O(max).
- *
+ *
* @param max the maximum value
* @return number of child nodes of the node
*/
@@ -385,68 +386,99 @@ public class NodeDelegate extends ItemDe
}
public void removeMixin(String typeName) throws RepositoryException {
- boolean wasLockable = isNodeType(MIX_LOCKABLE);
-
Tree tree = getTree();
Set<String> mixins = newLinkedHashSet(getNames(tree, JCR_MIXINTYPES));
if (!mixins.remove(typeName)) {
- throw new NoSuchNodeTypeException(
- "Mixin " + typeName +" not contained in " + getPath());
+ throw new NoSuchNodeTypeException("Mixin " + typeName +" not
contained in " + getPath());
}
- tree.setProperty(JCR_MIXINTYPES, mixins, NAMES);
-
- boolean isLockable = isNodeType(MIX_LOCKABLE);
- if (wasLockable && !isLockable && holdsLock(false)) {
- // TODO: This should probably be done in a commit hook
- unlock();
- sessionDelegate.refresh(true);
- }
-
- // We need to remove all protected properties and child nodes
- // associated with the removed mixin type, as there's no way for
- // the client to do that. Other items defined in this mixin type
- // might also need to be removed, but it's probably best to let
- // the client take care of that before save(), as it's hard to tell
- // whether removing such items really is the right thing to do.
-
- Tree typeRoot = sessionDelegate.getRoot().getTree(NODE_TYPES_PATH);
- List<Tree> removed = singletonList(typeRoot.getChild(typeName));
- List<Tree> remaining = getNodeTypes(tree, typeRoot);
+ updateMixins(mixins, Collections.singleton(typeName));
+ }
- for (PropertyState property : tree.getProperties()) {
- String name = property.getName();
- Type<?> type = property.getType();
-
- Tree oldDefinition = findMatchingPropertyDefinition(
- removed, name, type, true);
- if (oldDefinition != null) {
- Tree newDefinition = findMatchingPropertyDefinition(
- remaining, name, type, true);
- if (newDefinition == null
- || (getBoolean(oldDefinition, JCR_PROTECTED)
- && !getBoolean(newDefinition, JCR_PROTECTED)))
{
- tree.removeProperty(name);
+ public void setMixins(Set<String> mixinNames) throws RepositoryException {
+ Set<String> existingMixins = newLinkedHashSet(getNames(tree,
JCR_MIXINTYPES));
+ if (existingMixins.isEmpty()) {
+ updateMixins(mixinNames, Collections.EMPTY_SET);
+ } else {
+ Set<String> toRemove = newLinkedHashSet();
+ for (String name : existingMixins) {
+ if (!mixinNames.remove(name)) {
+ toRemove.add(name);
}
}
+ updateMixins(mixinNames, toRemove);
}
+ }
- for (Tree child : tree.getChildren()) {
- String name = child.getName();
- Set<String> typeNames = newLinkedHashSet();
- for (Tree type : getNodeTypes(child, typeRoot)) {
- typeNames.add(TreeUtil.getName(type, JCR_NODETYPENAME));
- addAll(typeNames, getNames(type, REP_SUPERTYPES));
+
+ public void updateMixins(Set<String> addMixinNames, Set<String>
removedOakMixinNames) throws RepositoryException {
+ // 1. set all new mixin types including validation
+ for (String oakMixinName : addMixinNames) {
+ addMixin(oakMixinName);
+ }
+
+ if (!removedOakMixinNames.isEmpty()) {
+ // 2. retrieve the updated set of mixin types, remove the mixins
that should no longer be present
+ Set<String> mixinNames = newLinkedHashSet(getNames(getTree(),
JCR_MIXINTYPES));
+ if (mixinNames.removeAll(removedOakMixinNames)) {
+ // FIXME: add mixins to add again as the removal may change
the effect of type inheritance as evaluated during #addMixin
+ mixinNames.addAll(addMixinNames);
+ tree.setProperty(JCR_MIXINTYPES, mixinNames, NAMES);
+ }
+
+ // 3. deal with locked nodes
+ boolean wasLockable = isNodeType(MIX_LOCKABLE);
+ boolean isLockable = isNodeType(MIX_LOCKABLE);
+ if (wasLockable && !isLockable && holdsLock(false)) {
+ // TODO: This should probably be done in a commit hook
+ unlock();
+ sessionDelegate.refresh(true);
+ }
+
+ // 4. clean up set of properties and child nodes such that all
child items
+ // have a valid item definition according to the effective node
type present
+ // after having updated the mixin property. this includes removing
all
+ // protected properties and child nodes associated with the
removed mixin
+ // type(s), as there's no way for the client to do that. Other
items
+ // defined in this mixin type might also need to be removed if
there
+ // is no longer a matching item definition available.
+ Tree typeRoot = sessionDelegate.getRoot().getTree(NODE_TYPES_PATH);
+ List<Tree> removed = new ArrayList<Tree>();
+ for (String name : removedOakMixinNames) {
+ removed.add(typeRoot.getChild(name));
+ }
+ List<Tree> remaining = getNodeTypes(tree, typeRoot);
+
+ for (PropertyState property : tree.getProperties()) {
+ String name = property.getName();
+ Type<?> type = property.getType();
+
+ Tree oldDefinition = findMatchingPropertyDefinition(removed,
name, type, true);
+ if (oldDefinition != null) {
+ Tree newDefinition =
findMatchingPropertyDefinition(remaining, name, type, true);
+ if (newDefinition == null
+ || (getBoolean(oldDefinition, JCR_PROTECTED)
+ && !getBoolean(newDefinition, JCR_PROTECTED))) {
+ tree.removeProperty(name);
+ }
+ }
}
- Tree oldDefinition = findMatchingChildNodeDefinition(
- removed, name, typeNames);
- if (oldDefinition != null) {
- Tree newDefinition = findMatchingChildNodeDefinition(
- remaining, name, typeNames);
- if (newDefinition == null
- || (getBoolean(oldDefinition, JCR_PROTECTED)
- && !getBoolean(newDefinition, JCR_PROTECTED)))
{
- child.remove();
+ for (Tree child : tree.getChildren()) {
+ String name = child.getName();
+ Set<String> typeNames = newLinkedHashSet();
+ for (Tree type : getNodeTypes(child, typeRoot)) {
+ typeNames.add(TreeUtil.getName(type, JCR_NODETYPENAME));
+ addAll(typeNames, getNames(type, REP_SUPERTYPES));
+ }
+
+ Tree oldDefinition = findMatchingChildNodeDefinition(removed,
name, typeNames);
+ if (oldDefinition != null) {
+ Tree newDefinition =
findMatchingChildNodeDefinition(remaining, name, typeNames);
+ if (newDefinition == null
+ || (getBoolean(oldDefinition, JCR_PROTECTED)
+ && !getBoolean(newDefinition, JCR_PROTECTED))) {
+ child.remove();
+ }
}
}
}
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=1558818&r1=1558817&r2=1558818&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
Thu Jan 16 14:42:34 2014
@@ -1474,8 +1474,45 @@ public class NodeImpl<T extends NodeDele
}
}
+ /**
+ * Simplified implementation of the {@link
org.apache.jackrabbit.api.JackrabbitNode#setMixins(String[])}
+ * method that adds all mixin types that are not yet present on this node
+ * and removes all mixins that are no longer contained in the specified
+ * array. Note, that this implementation will not work exactly like the
+ * variant in Jackrabbit 2.x which first created the effective node type
+ * and adjusted the set of child items accordingly.
+ *
+ * @param mixinNames
+ * @throws RepositoryException
+ */
@Override
- public void setMixins(String[] strings) throws RepositoryException {
- throw new UnsupportedRepositoryOperationException("TODO:
JackrabbitNode.setMixins (OAK-770");
+ public void setMixins(String[] mixinNames) throws RepositoryException {
+ final Set<String> oakTypeNames = newLinkedHashSet();
+ for (String mixinName : mixinNames) {
+ oakTypeNames.add(getOakName(checkNotNull(mixinName)));
+ }
+ perform(new ItemWriteOperation<Void>() {
+ @Override
+ public void checkPreconditions() throws RepositoryException {
+ super.checkPreconditions();
+ if (!isCheckedOut()) {
+ throw new VersionException("Cannot set mixin types. Node
is checked in.");
+ }
+
+ // check for NODE_TYPE_MANAGEMENT permission here as we cannot
+ // distinguish between a combination of removeMixin and
addMixin
+ // and Node#remove plus subsequent addNode when it comes to
+ // autocreated properties like jcr:create, jcr:uuid and so
forth.
+ PropertyDelegate mixinProp =
dlg.getPropertyOrNull(JCR_MIXINTYPES);
+ if (mixinProp != null) {
+
sessionContext.getAccessManager().checkPermissions(dlg.getTree(),
mixinProp.getPropertyState(), Permissions.NODE_TYPE_MANAGEMENT);
+ }
+ }
+ @Override
+ public Void perform() throws RepositoryException {
+ dlg.setMixins(oakTypeNames);
+ return null;
+ }
+ });
}
}
Modified:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/JackrabbitNodeTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/JackrabbitNodeTest.java?rev=1558818&r1=1558817&r2=1558818&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/JackrabbitNodeTest.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/JackrabbitNodeTest.java
Thu Jan 16 14:42:34 2014
@@ -25,11 +25,11 @@ import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.ObservationManager;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.api.JackrabbitNode;
import org.apache.jackrabbit.commons.cnd.CndImporter;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.api.observation.EventResult;
-import org.junit.Ignore;
/**
* JackrabbitNodeTest: Copied and slightly adjusted from
org.apache.jackrabbit.api.JackrabbitNodeTest,
@@ -120,23 +120,96 @@ public class JackrabbitNodeTest extends
}
}
- public void testRemoveMixin() throws RepositoryException {
+ /**
+ * @since oak 1.0
+ */
+ public void testSetNewMixins() throws RepositoryException {
+ // create node with mixin test:AA
+ Node n = testRootNode.addNode("foo", "nt:folder");
+ ((JackrabbitNode) n).setMixins(new String[]{"test:AA", "test:A"});
+ superuser.save();
+
+ assertTrue(n.isNodeType("test:AA"));
+ assertTrue(n.isNodeType("test:A"));
+ assertTrue(n.hasProperty(JcrConstants.JCR_MIXINTYPES));
+ }
+
+ /**
+ * @since oak 1.0
+ */
+ public void testSetNewMixins2() throws RepositoryException {
+ // create node with mixin test:AA
+ Node n = testRootNode.addNode("foo", "nt:folder");
+ ((JackrabbitNode) n).setMixins(new String[]{"test:A", "test:AA"});
+ superuser.save();
+
+ assertTrue(n.isNodeType("test:A"));
+ assertTrue(n.isNodeType("test:AA"));
+ assertTrue(n.hasProperty(JcrConstants.JCR_MIXINTYPES));
+ }
+
+ /**
+ * @since oak 1.0
+ */
+ public void testSetEmptyMixins() throws RepositoryException {
// create node with mixin test:AA
Node n = testRootNode.addNode("foo", "nt:folder");
n.addMixin("test:AA");
- n.setProperty("test:propAA", "AA");
- n.setProperty("test:propA", "A");
superuser.save();
- // 'downgrade' from test:AA to test:A
- n.removeMixin("test:AA");
+ ((JackrabbitNode) n).setMixins(new String[0]);
superuser.save();
- assertFalse(n.hasProperty("test:propA"));
- assertFalse(n.hasProperty("test:propAA"));
+ assertFalse(n.isNodeType("test:AA"));
+ assertTrue(n.hasProperty(JcrConstants.JCR_MIXINTYPES));
+ assertEquals(0,
n.getProperty(JcrConstants.JCR_MIXINTYPES).getValues().length);
+ }
+
+ /**
+ * @since oak 1.0
+ */
+ public void testSetRemoveMixins() throws RepositoryException {
+ // create node with mixin test:AA
+ Node n = testRootNode.addNode("foo", "nt:folder");
+ ((JackrabbitNode) n).setMixins(new String[]{"test:A", "test:AA"});
+ superuser.save();
+
+ ((JackrabbitNode) n).setMixins(new String[]{"test:A"});
+ superuser.save();
+
+ assertTrue(n.isNodeType("test:A"));
+ assertFalse(n.isNodeType("test:AA"));
+ }
+
+ /**
+ * @since oak 1.0
+ */
+ public void testUpdateMixins() throws RepositoryException {
+ // create node with mixin test:AA
+ Node n = testRootNode.addNode("foo", "nt:folder");
+ ((JackrabbitNode) n).setMixins(new String[]{"test:A", "test:AA"});
+ superuser.save();
+
+ assertTrue(n.isNodeType("test:AA"));
+ assertTrue(n.isNodeType("test:A"));
+
+ ((JackrabbitNode) n).setMixins(new String[]{"test:A", "test:AA",
JcrConstants.MIX_REFERENCEABLE});
+ superuser.save();
+
+ assertTrue(n.isNodeType("test:AA"));
+ assertTrue(n.isNodeType("test:A"));
+ assertTrue(n.isNodeType(JcrConstants.MIX_REFERENCEABLE));
+ assertTrue(n.hasProperty(JcrConstants.JCR_UUID));
+
+ ((JackrabbitNode) n).setMixins(new
String[]{JcrConstants.MIX_REFERENCEABLE});
+ superuser.save();
+
+ assertFalse(n.isNodeType("test:AA"));
+ assertFalse(n.isNodeType("test:A"));
+ assertTrue(n.isNodeType(JcrConstants.MIX_REFERENCEABLE));
+ assertTrue(n.hasProperty(JcrConstants.JCR_UUID));
}
- @Ignore("OAK-770") // FIXME: OAK-770
public void testSetMixins() throws RepositoryException {
// create node with mixin test:AA
Node n = testRootNode.addNode("foo", "nt:folder");