Author: jukka
Date: Wed Mar 19 18:50:07 2014
New Revision: 1579349

URL: http://svn.apache.org/r1579349
Log:
OAK-850: Degrade gracefully when :childOrder is out of sync

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableTree.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/AbstractTree.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java?rev=1579349&r1=1579348&r2=1579349&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
 Wed Mar 19 18:50:07 2014
@@ -196,8 +196,8 @@ class MutableRoot implements Root {
 
         boolean success = source.moveTo(newParent, newName);
         if (success) {
-            getTree(getParentPath(sourcePath)).updateChildOrder();
-            getTree(getParentPath(destPath)).updateChildOrder();
+            getTree(getParentPath(sourcePath)).updateChildOrder(false);
+            getTree(getParentPath(destPath)).updateChildOrder(false);
             lastMove = lastMove.setMove(sourcePath, newParent, newName);
             updated();
             // remember all move operations for further processing in the 
commit hooks.

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableTree.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableTree.java?rev=1579349&r1=1579348&r2=1579349&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableTree.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableTree.java
 Wed Mar 19 18:50:07 2014
@@ -21,28 +21,23 @@ package org.apache.jackrabbit.oak.core;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.indexOf;
-import static org.apache.jackrabbit.oak.api.Type.NAME;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
 import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
 import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
+import static 
org.apache.jackrabbit.oak.plugins.tree.TreeConstants.OAK_CHILD_ORDER;
 import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;
 
-import java.util.Collections;
-import java.util.Set;
+import java.util.List;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.core.MutableRoot.Move;
-import org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.tree.AbstractTree;
 import org.apache.jackrabbit.oak.plugins.tree.TreeConstants;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -178,13 +173,7 @@ class MutableTree extends AbstractTree {
         beforeWrite();
         if (parent != null && parent.hasChild(name)) {
             nodeBuilder.remove();
-            if (parent.hasOrderableChildren()) {
-                parent.nodeBuilder.setProperty(
-                        PropertyBuilder.copy(NAME, 
parent.nodeBuilder.getProperty(TreeConstants.OAK_CHILD_ORDER))
-                                .removeValue(name)
-                                .getPropertyState()
-                );
-            }
+            updateChildOrder(false);
             root.updated();
             return true;
         } else {
@@ -198,12 +187,7 @@ class MutableTree extends AbstractTree {
         beforeWrite();
         if (!super.hasChild(name)) {
             nodeBuilder.setChildNode(name);
-            if (hasOrderableChildren()) {
-                nodeBuilder.setProperty(
-                        PropertyBuilder.copy(NAME, 
nodeBuilder.getProperty(TreeConstants.OAK_CHILD_ORDER))
-                                .addValue(name)
-                                .getPropertyState());
-            }
+            updateChildOrder(false);
             root.updated();
         }
         return createChild(name);
@@ -213,9 +197,9 @@ class MutableTree extends AbstractTree {
     public void setOrderableChildren(boolean enable) {
         beforeWrite();
         if (enable) {
-            ensureChildOrderProperty();
+            updateChildOrder(true);
         } else {
-            nodeBuilder.removeProperty(TreeConstants.OAK_CHILD_ORDER);
+            nodeBuilder.removeProperty(OAK_CHILD_ORDER);
         }
     }
 
@@ -233,37 +217,19 @@ class MutableTree extends AbstractTree {
             }
         }
         // perform the reorder
-        parent.ensureChildOrderProperty();
-        // all siblings but not this one
-        Iterable<String> siblings = filter(
-                parent.getChildNames(),
-                new Predicate<String>() {
-                    @Override
-                    public boolean apply(String name) {
-                        return !MutableTree.this.name.equals(name);
-                    }
-                });
-        // create head and tail
-        Iterable<String> head;
-        Iterable<String> tail;
+        List<String> names = newArrayList();
+        for (String n : parent.getChildNames()) {
+            if (n.equals(name)) {
+                names.add(this.name);
+            }
+            if (!n.equals(this.name)) {
+                names.add(n);
+            }
+        }
         if (name == null) {
-            head = siblings;
-            tail = Collections.emptyList();
-        } else {
-            int idx = indexOf(siblings, new Predicate<String>() {
-                @Override
-                public boolean apply(String sibling) {
-                    return name.equals(sibling);
-                }
-            });
-            head = Iterables.limit(siblings, idx);
-            tail = Iterables.skip(siblings, idx);
-        }
-        // concatenate head, this name and tail
-        parent.nodeBuilder.setProperty(
-                MultiGenericPropertyState.nameProperty(
-                        TreeConstants.OAK_CHILD_ORDER, Iterables.concat(head, 
Collections.singleton(getName()), tail))
-        );
+            names.add(this.name);
+        }
+        parent.nodeBuilder.setProperty(OAK_CHILD_ORDER, names, NAMES);
         root.updated();
         return true;
     }
@@ -339,26 +305,18 @@ class MutableTree extends AbstractTree {
     }
 
     /**
-     * Update the child order with children that have been removed or added.
-     * Added children are appended to the end of the {@link 
org.apache.jackrabbit.oak.plugins.tree.TreeConstants#OAK_CHILD_ORDER}
-     * property.
+     * Updates the child order to match any added or removed child nodes that
+     * are not yet reflected in the {@link TreeConstants#OAK_CHILD_ORDER}
+     * property. If the {@code force} flag is set, the child order is set
+     * in any case, otherwise only if the node already is orderable.
+     *
+     * @param force whether to add child order information if it doesn't exist
      */
-    void updateChildOrder() {
-        if (!hasOrderableChildren()) {
-            return;
-        }
-        Set<String> names = Sets.newLinkedHashSet();
-        for (String name : getChildNames()) {
-            if (nodeBuilder.hasChildNode(name)) {
-                names.add(name);
-            }
+    void updateChildOrder(boolean force) {
+        if (force || hasOrderableChildren()) {
+            nodeBuilder.setProperty(PropertyStates.createProperty(
+                    OAK_CHILD_ORDER, getChildNames(), Type.NAMES));
         }
-        for (String name : nodeBuilder.getChildNodeNames()) {
-            names.add(name);
-        }
-        PropertyBuilder<String> builder = PropertyBuilder.array(NAME, 
TreeConstants.OAK_CHILD_ORDER);
-        builder.setValues(names);
-        nodeBuilder.setProperty(builder.getPropertyState());
     }
 
     String getPathInternal() {
@@ -435,18 +393,4 @@ class MutableTree extends AbstractTree {
         return movesApplied;
     }
 
-    /**
-     * Ensures that the {@link 
org.apache.jackrabbit.oak.plugins.tree.TreeConstants#OAK_CHILD_ORDER} exists. 
This method will create
-     * the property if it doesn't exist and initialize the value with the names
-     * of the children as returned by {@link NodeBuilder#getChildNodeNames()}.
-     */
-    private void ensureChildOrderProperty() {
-        if (!nodeBuilder.hasProperty(TreeConstants.OAK_CHILD_ORDER)) {
-            nodeBuilder.setProperty(
-                    
MultiGenericPropertyState.nameProperty(TreeConstants.OAK_CHILD_ORDER, 
nodeBuilder.getChildNodeNames()));
-        }
-    }
-
 }
-
-

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/AbstractTree.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/AbstractTree.java?rev=1579349&r1=1579348&r2=1579349&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/AbstractTree.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/AbstractTree.java
 Wed Mar 19 18:50:07 2014
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.jackrabbit.oak.plugins.tree;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -24,18 +23,21 @@ import static com.google.common.base.Pre
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.size;
 import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newLinkedHashSet;
 import static org.apache.jackrabbit.oak.api.Tree.Status.MODIFIED;
 import static org.apache.jackrabbit.oak.api.Tree.Status.NEW;
 import static org.apache.jackrabbit.oak.api.Tree.Status.UNCHANGED;
-import static org.apache.jackrabbit.oak.api.Type.NAME;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static 
org.apache.jackrabbit.oak.plugins.tree.TreeConstants.OAK_CHILD_ORDER;
 import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;
 
-import java.util.Iterator;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
@@ -117,42 +119,24 @@ public abstract class AbstractTree imple
      *         {@code false} otherwise.
      */
     protected boolean hasOrderableChildren() {
-        return nodeBuilder.hasProperty(TreeConstants.OAK_CHILD_ORDER);
+        return nodeBuilder.hasProperty(OAK_CHILD_ORDER);
     }
 
     /**
      * Returns the list of child names considering its ordering
-     * when the {@link 
org.apache.jackrabbit.oak.plugins.tree.TreeConstants#OAK_CHILD_ORDER} property 
is set.
+     * when the {@link TreeConstants#OAK_CHILD_ORDER} property is set.
      *
      * @return the list of child names.
      */
     @Nonnull
     protected Iterable<String> getChildNames() {
-        if (hasOrderableChildren()) {
-            return new Iterable<String>() {
-                @Override
-                public Iterator<String> iterator() {
-                    return new Iterator<String>() {
-                        final PropertyState childOrder = 
nodeBuilder.getProperty(TreeConstants.OAK_CHILD_ORDER);
-                        int index;
-
-                        @Override
-                        public boolean hasNext() {
-                            return index < childOrder.count();
-                        }
-
-                        @Override
-                        public String next() {
-                            return childOrder.getValue(NAME, index++);
-                        }
-
-                        @Override
-                        public void remove() {
-                            throw new UnsupportedOperationException();
-                        }
-                    };
-                }
-            };
+        PropertyState order = nodeBuilder.getProperty(OAK_CHILD_ORDER);
+        if (order != null && order.getType() == NAMES) {
+            Set<String> names = 
newLinkedHashSet(nodeBuilder.getChildNodeNames());
+            Set<String> ordered = newLinkedHashSet(order.getValue(NAMES));
+            ordered.retainAll(names); // remove names of missing child nodes
+            ordered.addAll(names);    // insert names of unordered child nodes
+            return ordered;
         } else {
             return nodeBuilder.getChildNodeNames();
         }


Reply via email to