jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mdue...@apache.org
Subject svn commit: r1524957 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ oak-core/src/main/java/org...
Date Fri, 20 Sep 2013 09:27:29 GMT
Author: mduerig
Date: Fri Sep 20 09:27:29 2013
New Revision: 1524957

URL: http://svn.apache.org/r1524957
Log:
OAK-659 Move purge logic for transient changes below the NodeBuilder interface
This initial fix still keeps transient changes from commit hooks in memory when running with a MicroKernel. See TODOs in org.apache.jackrabbit.oak.kernel.KernelNodeState.builder.

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeBuilder.java
      - copied, changed from r1524941, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentRootBuilder.java
      - copied, changed from r1524941, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.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/core/SecureNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ReadOnlyBuilder.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/NodeStoreTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingIndexTest.java
    jackrabbit/oak/trunk/oak-parent/pom.xml

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java Fri Sep 20 09:27:29 2013
@@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.core;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAncestor;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -59,17 +60,11 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
-import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 import org.apache.jackrabbit.oak.util.LazyValue;
 
 public abstract class AbstractRoot implements Root {
 
     /**
-     * Number of {@link #updated} calls for which changes are kept in memory.
-     */
-    private static final int PURGE_LIMIT = Integer.getInteger("oak.root.purgeLimit", 1000);
-
-    /**
      * The underlying store to which this root belongs
      */
     private final NodeStore store;
@@ -92,19 +87,19 @@ public abstract class AbstractRoot imple
     private final MutableTree rootTree;
 
     /**
-     * Secured builder for the root tree
+     * Unsecured builder for the root tree
      */
-    private final SecureNodeBuilder secureBuilder;
+    private final NodeBuilder builder;
 
     /**
-     * Unsecured builder for the root tree
+     * Secured builder for the root tree
      */
-    private final NodeBuilder builder;
+    private final SecureNodeBuilder secureBuilder;
 
     /**
-     * Current branch this root operates on
+     * Base state of the root tree
      */
-    private NodeStoreBranch branch;
+    private NodeState base;
 
     /**
      * Sentinel for the next move operation to take place on the this root
@@ -112,8 +107,7 @@ public abstract class AbstractRoot imple
     private Move lastMove = new Move();
 
     /**
-     * Number of {@link #updated} occurred so since the last
-     * purge.
+     * Number of {@link #updated} occurred.
      */
     private long modCount;
 
@@ -149,9 +143,8 @@ public abstract class AbstractRoot imple
         this.securityProvider = checkNotNull(securityProvider);
         this.indexProvider = indexProvider;
 
-        branch = this.store.branch();
-        NodeState root = branch.getHead();
-        builder = root.builder();
+        base = store.getRoot();
+        builder = base.builder();
         secureBuilder = new SecureNodeBuilder(builder, permissionProvider, getAcContext());
         rootTree = new MutableTree(this, secureBuilder, lastMove);
     }
@@ -171,22 +164,29 @@ public abstract class AbstractRoot imple
 
     @Override
     public boolean move(String sourcePath, String destPath) {
-        if (PathUtils.isAncestor(sourcePath, destPath)) {
+        if (isAncestor(checkNotNull(sourcePath), checkNotNull(destPath))) {
             return false;
+        } else if (sourcePath.equals(destPath)) {
+            return true;
         }
 
         checkLive();
-        MutableTree destParent = rootTree.getTree(getParentPath(destPath));
-        if (!destParent.exists()) {
+        MutableTree source = rootTree.getTree(sourcePath);
+        if (!source.exists()) {
+            return false;
+        }
+
+        String newName = getName(destPath);
+        MutableTree newParent = rootTree.getTree(getParentPath(destPath));
+        if (!newParent.exists() || newParent.hasChild(newName)) {
             return false;
         }
-        purgePendingChanges();
-        boolean success = branch.move(sourcePath, destPath);
-        reset();
+
+        boolean success = source.moveTo(newParent, newName);
         if (success) {
             getTree(getParentPath(sourcePath)).updateChildOrder();
             getTree(getParentPath(destPath)).updateChildOrder();
-            lastMove = lastMove.setMove(sourcePath, destParent, getName(destPath));
+            lastMove = lastMove.setMove(sourcePath, newParent, newName);
             updated();
         }
         return success;
@@ -195,9 +195,18 @@ public abstract class AbstractRoot imple
     @Override
     public boolean copy(String sourcePath, String destPath) {
         checkLive();
-        purgePendingChanges();
-        boolean success = branch.copy(sourcePath, destPath);
-        reset();
+        MutableTree source = rootTree.getTree(sourcePath);
+        if (!source.exists()) {
+            return false;
+        }
+
+        String newName = getName(destPath);
+        MutableTree newParent = rootTree.getTree(getParentPath(destPath));
+        if (!newParent.exists() || newParent.hasChild(newName)) {
+            return false;
+        }
+
+        boolean success = source.copyTo(newParent, newName);
         if (success) {
             getTree(getParentPath(destPath)).updateChildOrder();
             updated();
@@ -215,9 +224,8 @@ public abstract class AbstractRoot imple
     public void rebase() {
         checkLive();
         if (!store.getRoot().equals(getBaseState())) {
-            purgePendingChanges();
-            branch.rebase();
-            reset();
+            store.rebase(builder);
+            secureBuilder.baseChanged();
             if (permissionProvider.hasValue()) {
                 permissionProvider.get().refresh();
             }
@@ -227,8 +235,8 @@ public abstract class AbstractRoot imple
     @Override
     public final void refresh() {
         checkLive();
-        branch = store.branch();
-        reset();
+        base = store.reset(builder);
+        secureBuilder.baseChanged();
         modCount = 0;
         if (permissionProvider.hasValue()) {
             permissionProvider.get().refresh();
@@ -238,9 +246,12 @@ public abstract class AbstractRoot imple
     @Override
     public void commit(final CommitHook... hooks) throws CommitFailedException {
         checkLive();
-        purgePendingChanges();
-        branch.merge(getCommitHook(hooks), postHook);
-        refresh();
+        base = store.merge(builder, getCommitHook(hooks), postHook);
+        secureBuilder.baseChanged();
+        modCount = 0;
+        if (permissionProvider.hasValue()) {
+            permissionProvider.get().refresh();
+        }
     }
 
     /**
@@ -335,30 +346,27 @@ public abstract class AbstractRoot imple
     //-----------------------------------------------------------< internal >---
 
     /**
-     * Returns the node state from which the current branch was created.
+     * Returns the node state from the time this root was created, that
+     * is this root's base state.
      *
      * @return base node state
      */
     @Nonnull
     NodeState getBaseState() {
-        return branch.getBase();
+        return base;
     }
 
     /**
-     * Returns the secure view of the base state from which the current branch was creates.
+     * Returns the secure view of this root's base state.
      *
      * @return secure base node state
      */
     NodeState getSecureBase() {
-        NodeState root = branch.getBase();
-        return new SecureNodeState(root, permissionProvider.get(), getAcContext());
+        return new SecureNodeState(base, permissionProvider.get(), getAcContext());
     }
 
-    // TODO better way to determine purge limit. See OAK-175
     void updated() {
-        if (++modCount % PURGE_LIMIT == 0) {
-            purgePendingChanges();
-        }
+        modCount++;
     }
 
     //------------------------------------------------------------< private >---
@@ -374,22 +382,6 @@ public abstract class AbstractRoot imple
         return builder.getNodeState();
     }
 
-    /**
-     * Purge all pending changes to the underlying {@link NodeStoreBranch}.
-     */
-    private void purgePendingChanges() {
-        branch.setRoot(getRootState());
-        reset();
-    }
-
-    /**
-     * Reset the root builder to the branch's current root state
-     */
-    private void reset() {
-        NodeState root = branch.getHead();
-        secureBuilder.reset(root);
-    }
-
     @Nonnull
     private Context getAcContext() {
         return getAcConfig().getContext();
@@ -452,7 +444,7 @@ public abstract class AbstractRoot imple
             Move move = this;
             while (move.next != null) {
                 if (move.source.equals(tree.getPathInternal())) {
-                    tree.moveTo(move.destParent, move.destName);
+                    tree.setParentAndName(move.destParent, move.destName);
                 }
                 move = move.next;
             }

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=1524957&r1=1524956&r2=1524957&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 Fri Sep 20 09:27:29 2013
@@ -18,8 +18,22 @@
  */
 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.Tree.Status.EXISTING;
+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.Type.STRING;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
+import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;
+
 import java.util.Collections;
 import java.util.Set;
+
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
@@ -36,19 +50,6 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.PropertyBuilder;
 
-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.Tree.Status.EXISTING;
-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.Type.STRING;
-import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
-import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
-import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;
-
 public class MutableTree extends AbstractTree {
 
     /**
@@ -372,14 +373,37 @@ public class MutableTree extends Abstrac
     //-----------------------------------------------------------< internal >---
 
     /**
+     * Set the parent and name of this tree.
+     * @param parent  parent of this tree
+     * @param name  name of this tree
+     */
+    void setParentAndName(MutableTree parent, String name) {
+        this.name = name;
+        this.parent = parent;
+    }
+
+    /**
      * Move this tree to the parent at {@code destParent} with the new name
-     * {@code destName}.
-     * @param destParent new parent for this tree
-     * @param destName   new name for this tree
+     * {@code newName}.
+     * @param newParent new parent for this tree
+     * @param newName   new name for this tree
+     */
+    boolean moveTo(MutableTree newParent, String newName) {
+        name = newName;
+        parent = newParent;
+        // FIXME this falls back to MemoryNodeBuilder#moveTo if newParent is a SecureNodeState
+        return nodeBuilder.moveTo(newParent.nodeBuilder, newName);
+    }
+
+    /**
+     * Copy this tree to the parent at {@code destParent} with the new name
+     * {@code newName}.
+     * @param newParent new parent for this tree
+     * @param newName   new name for this tree
      */
-    void moveTo(MutableTree destParent, String destName) {
-        name = destName;
-        parent = destParent;
+    boolean copyTo(MutableTree newParent, String newName) {
+        // FIXME this falls back to MemoryNodeBuilder#copyTo if newParent is a SecureNodeState
+        return nodeBuilder.copyTo(newParent.nodeBuilder, newName);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java Fri Sep 20 09:27:29 2013
@@ -145,11 +145,27 @@ class SecureNodeBuilder implements NodeB
         securityContext = null;
     }
 
+    public void baseChanged() {
+        baseRevision++;
+        securityContext = null;
+    }
+
     @Override
     public boolean remove() {
         return exists() && builder.remove();
     }
 
+
+    @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        return exists() && builder.moveTo(newParent, newName);
+    }
+
+    @Override
+    public boolean copyTo(NodeBuilder newParent, String newName) {
+        return exists() && builder.copyTo(newParent, newName);
+    }
+
     @Override @CheckForNull
     public PropertyState getProperty(String name) {
         PropertyState property = builder.getProperty(name);
@@ -272,13 +288,13 @@ class SecureNodeBuilder implements NodeB
 
     @Override @Nonnull
     public NodeBuilder setChildNode(@Nonnull String name) {
-        NodeBuilder child = builder.setChildNode(name);
+        builder.setChildNode(name);
         return new SecureNodeBuilder(this, name);
     }
 
     @Override @Nonnull
     public NodeBuilder setChildNode(String name, @Nonnull NodeState nodeState) {
-        NodeBuilder child = builder.setChildNode(name, nodeState);
+        builder.setChildNode(name, nodeState);
         return new SecureNodeBuilder(this, name);
     }
 
@@ -307,7 +323,7 @@ class SecureNodeBuilder implements NodeB
     }
 
     /**
-     * Security context of this subtree. This accessor memoizes the security context
+     * Security context of this subtree. This accessor memoises the security context
      * as long as {@link #reset(NodeState)} has not been called.
      */
     private SecurityContext getSecurityContext() {

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java?rev=1524957&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java Fri Sep 20 09:27:29 2013
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.kernel;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * This class refines move and copy operations by delegating
+ * them to the underlying store if possible.
+ * @see KernelRootBuilder
+ */
+public class KernelNodeBuilder extends MemoryNodeBuilder {
+
+    private final KernelRootBuilder root;
+
+    KernelNodeBuilder(MemoryNodeBuilder parent, String name, KernelRootBuilder root) {
+        super(parent, name);
+        this.root = checkNotNull(root);
+    }
+
+    //--------------------------------------------------< MemoryNodeBuilder >---
+
+    @Override
+    protected MemoryNodeBuilder createChildBuilder(String name) {
+        return new KernelNodeBuilder(this, name, root);
+    }
+
+    // TODO optimise this by caching similar to what we do in MemoryNodeBuilder
+    @Override
+    public NodeState getBaseState() {
+        return getParent().getBaseState().getChildNode(getName());
+    }
+
+    @Override
+    public void reset(NodeState newBase) {
+        throw new IllegalStateException("Cannot reset a non-root builder");
+    }
+
+    /**
+     * If {@code newParent} is a {@link KernelNodeBuilder} this implementation
+     * purges all pending changes before applying the move operation. This allows the
+     * underlying store to better optimise move operations instead of just seeing
+     * them as an added and a removed node.
+     * If {@code newParent} is not a {@code KernelNodeBuilder} the implementation
+     * falls back to the super class.
+     */
+    @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        if (newParent instanceof KernelNodeBuilder) {
+            String source = getPath();
+            String target = PathUtils.concat(((KernelNodeBuilder) newParent).getPath(), checkNotNull(newName));
+            return root.move(source, target);
+        } else {
+            return super.moveTo(newParent, newName);
+        }
+    }
+
+    /**
+     * If {@code newParent} is a {@link KernelNodeBuilder} this implementation
+     * purges all pending changes before applying the copy operation. This allows the
+     * underlying store to better optimise copy operations instead of just seeing
+     * them as an added node.
+     * If {@code newParent} is not a {@code KernelNodeBuilder} the implementation
+     * falls back to the super class.
+     */
+    @Override
+    public boolean copyTo(NodeBuilder newParent, String newName) {
+        if (newParent instanceof KernelNodeBuilder) {
+            String source = getPath();
+            String target = PathUtils.concat(((KernelNodeBuilder) newParent).getPath(), checkNotNull(newName));
+            return root.copy(source, target);
+        } else {
+            return super.copyTo(newParent, newName);
+        }
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java Fri Sep 20 09:27:29 2013
@@ -24,9 +24,6 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -100,15 +97,9 @@ public final class KernelNodeState exten
      * path and revision. This object is only used internally and never leaves
      * this {@link KernelNodeState}.
      */
-    private static final KernelNodeState NULL = new KernelNodeState(
-            (MicroKernel) Proxy.newProxyInstance(MicroKernel.class.getClassLoader(),
-                    new Class[]{MicroKernel.class}, new InvocationHandler() {
-        @Override
-        public Object invoke(Object proxy, Method method, Object[] args)
-                throws Throwable {
-            throw new UnsupportedOperationException();
-        }
-    }), "null", "null", DUMMY_CACHE);
+    private static final KernelNodeState NULL = new KernelNodeState();
+
+    private final KernelNodeStore store;
 
     private final MicroKernel kernel;
 
@@ -128,6 +119,15 @@ public final class KernelNodeState exten
 
     private Set<String> childNames;
 
+    /**
+     * {@code true} is this is a node state from a branch. {@code false}
+     * otherwise.
+     * <p>
+     * FIXME: this is a workaround to avoid creating branches from a branch
+     * until this is supported by the MicroKernel. See {@link KernelNodeState#builder()}.
+     */
+    private boolean isBranch;
+
     private final LoadingCache<String, KernelNodeState> cache;
 
     /**
@@ -135,20 +135,29 @@ public final class KernelNodeState exten
      * given {@code path} and {@code revision}. It is an error if the
      * underlying Microkernel does not contain such a node.
      *
-     * @param kernel the underlying MicroKernel
+     * @param store the underlying KernelNodeStore
      * @param path the path of this KernelNodeState
      * @param revision the revision of the node to read from the kernel.
      * @param cache the KernelNodeState cache
      */
     public KernelNodeState(
-            MicroKernel kernel, String path, String revision,
+            KernelNodeStore store, String path, String revision,
             LoadingCache<String, KernelNodeState> cache) {
-        this.kernel = checkNotNull(kernel);
+        this.store = store;
+        this.kernel = store.getKernel();
         this.path = checkNotNull(path);
         this.revision = checkNotNull(revision);
         this.cache = checkNotNull(cache);
     }
 
+    private KernelNodeState() {
+        this.store = null;
+        this.kernel = null;
+        this.path = "null";
+        this.revision = "null";
+        this.cache = DUMMY_CACHE;
+    }
+
     private void init() {
         boolean initialized = false;
         synchronized (this) {
@@ -417,9 +426,23 @@ public final class KernelNodeState exten
         };
     }
 
+    /**
+     * This implementation returns a {@link KernelNodeBuilder} unless this is not
+     * a root node or {@link #isBranch} is {@code true} in which case a
+     * {@link MemoryNodeBuilder} is returned.
+     * <p>
+     * TODO: this is a workaround to avoid creating branches from a branch
+     * until this is supported by the MicroKernel.
+     */
     @Override
     public NodeBuilder builder() {
-        return new MemoryNodeBuilder(this);
+        if (isBranch) {
+            return new MemoryNodeBuilder(this);
+        } else if ("/".equals(path)) {
+            return new KernelRootBuilder(this, store);
+        } else {
+            return new MemoryNodeBuilder(this);
+        }
     }
 
     /**
@@ -516,6 +539,26 @@ public final class KernelNodeState exten
     }
 
     /**
+     * Mark this instance as from being on branch.
+     * <p>
+     * TODO this is a workaround to avoid creating branches from a branch
+     * until this is supported by the MicroKernel. See {@link KernelNodeState#builder()}.
+     * @return {@code this}
+     */
+    KernelNodeState setBranch() {
+        isBranch = true;
+        return this;
+    }
+
+    /**
+     * @return  {@code true} if this instance has been marked as being on a branch by a call
+     * to {@link #setBranch()}
+     */
+    boolean isBranch() {
+        return isBranch;
+    }
+
+    /**
      * @return the approximate memory usage of this node state.
      */
     synchronized int getMemory() {
@@ -557,7 +600,7 @@ public final class KernelNodeState exten
 
     //------------------------------------------------------------< private >---
 
-    private boolean hasChanges(String journal) {
+    private static boolean hasChanges(String journal) {
         return !journal.trim().isEmpty();
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java Fri Sep 20 09:27:29 2013
@@ -36,10 +36,14 @@ import com.google.common.util.concurrent
 import com.google.common.util.concurrent.SettableFuture;
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.EmptyObserver;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 
@@ -94,7 +98,7 @@ public class KernelNodeStore extends Abs
                         int slash = key.indexOf('/');
                         String revision = key.substring(0, slash);
                         String path = key.substring(slash);
-                        return new KernelNodeState(kernel, path, revision, cache);
+                        return new KernelNodeState(KernelNodeStore.this, path, revision, cache);
                     }
 
                     @Override
@@ -144,6 +148,58 @@ public class KernelNodeStore extends Abs
         return root;
     }
 
+    /**
+     * This implementation delegates to {@link KernelRootBuilder#merge(CommitHook, PostCommitHook)}
+     * if {@code builder} is a {@link KernelNodeBuilder} instance. Otherwise it falls
+     * back to the default implementation of its super class.
+     */
+    @Override
+    public NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook,
+            PostCommitHook committed) throws CommitFailedException {
+        if (builder instanceof KernelRootBuilder) {
+            return ((KernelRootBuilder) builder).merge(commitHook, committed);
+        } else {
+            mergeLock.lock();
+            try {
+                return super.merge(builder, commitHook, committed);
+            } finally {
+                mergeLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * This implementation delegates to {@link KernelRootBuilder#rebase()} if {@code builder}
+     * is a {@link KernelNodeBuilder} instance. Otherwise it falls back to the default
+     * implementation of its super class.
+     * @param builder  the builder to rebase
+     * @return
+     */
+    @Override
+    public NodeState rebase(@Nonnull NodeBuilder builder) {
+        if (builder instanceof KernelRootBuilder) {
+            return ((KernelRootBuilder) builder).rebase();
+        } else {
+            return super.rebase(builder);
+        }
+    }
+
+    /**
+     * This implementation delegates to {@link KernelRootBuilder#reset()} if {@code builder}
+     * is a {@link KernelNodeBuilder} instance. Otherwise it falls back to the default
+     * implementation of its super class.
+     * @param builder  the builder to rebase
+     * @return
+     */
+    @Override
+    public NodeState reset(@Nonnull NodeBuilder builder) {
+        if (builder instanceof KernelRootBuilder) {
+            return ((KernelRootBuilder) builder).reset();
+        } else {
+            return super.reset(builder);
+        }
+    }
+
     @Override
     public NodeStoreBranch branch() {
         return new KernelNodeStoreBranch(this, mergeLock, getRoot());
@@ -192,16 +248,28 @@ public class KernelNodeStore extends Abs
         }
     }
 
+    MicroKernel getKernel() {
+        return kernel;
+    }
+
+    NodeStoreBranch createBranch(KernelNodeState base) {
+        return new KernelNodeStoreBranch(this, mergeLock, base);
+    }
+
     KernelNodeState commit(String jsop, KernelNodeState base) {
-        return getRootState(kernel.commit("", jsop, base.getRevision(), null));
+        KernelNodeState rootState = getRootState(kernel.commit("", jsop, base.getRevision(), null));
+        if (base.isBranch()) {
+            rootState.setBranch();
+        }
+        return rootState;
     }
 
     KernelNodeState branch(KernelNodeState base) {
-        return getRootState(kernel.branch(base.getRevision()));
+        return getRootState(kernel.branch(base.getRevision())).setBranch();
     }
 
     KernelNodeState rebase(KernelNodeState branchHead, KernelNodeState base) {
-        return getRootState(kernel.rebase(branchHead.getRevision(), base.getRevision()));
+        return getRootState(kernel.rebase(branchHead.getRevision(), base.getRevision())).setBranch();
     }
 
     NodeState merge(KernelNodeState branchHead) {

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java?rev=1524957&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java Fri Sep 20 09:27:29 2013
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.kernel;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
+
+/**
+ * This implementation tracks the number of pending changes and purges them to
+ * a private branch of the underlying store if a certain threshold is met.
+ */
+class KernelRootBuilder extends MemoryNodeBuilder {
+
+    /**
+     * Number of content updates that need to happen before the updates
+     * are automatically purged to the private branch.
+     */
+    private static final int UPDATE_LIMIT = Integer.getInteger("update.limit", 1000);
+
+    /**
+     * The underlying store
+     */
+    private final NodeStore store;
+
+    /**
+     * The base state of this builder, possibly non-existent if this builder
+     * represents a new node that didn't yet exist in the base content tree.
+     * This differs from the base state of super since the latter one reflects
+     * the base created by the last purge.
+     */
+    private NodeState base;
+
+    /**
+     * Private branch used to hold pending changes exceeding {@link #UPDATE_LIMIT}
+     */
+    private NodeStoreBranch branch;
+
+    /**
+     * Number of updated not yet persisted to the private {@link #branch}
+     */
+    private int updates = 0;
+
+    KernelRootBuilder(KernelNodeState base, KernelNodeStore store) {
+        super(checkNotNull(base));
+        this.base = base;
+        this.store = store;
+        this.branch = store.createBranch(base);
+    }
+
+    //--------------------------------------------------< MemoryNodeBuilder >---
+
+
+    @Override
+    public NodeState getBaseState() {
+        return base;
+    }
+
+    @Override
+    public void reset(NodeState newBase) {
+        base = newBase;
+        super.reset(newBase);
+    }
+
+    @Override
+    protected MemoryNodeBuilder createChildBuilder(String name) {
+        return new KernelNodeBuilder(this, name, this);
+    }
+
+    @Override
+    protected void updated() {
+        if (updates++ > UPDATE_LIMIT) {
+            purge();
+        }
+    }
+
+    //------------------------------------------------------------< internal >---
+
+    /**
+     * Rebase this builder on top of the head of the underlying store
+     */
+    NodeState rebase() {
+        purge();
+        branch.rebase();
+        NodeState head = branch.getHead();
+        reset(head);
+        return head;
+    }
+
+    /**
+     * Reset this builder by creating a new branch and setting the head
+     * state of that branch as the new base state of this builder.
+     */
+    NodeState reset() {
+        branch = store.branch();
+        NodeState head = branch.getHead();
+        reset(head);
+        return head;
+    }
+
+    /**
+     * Merge all changes tracked in this builder into the underlying store.
+     */
+    NodeState merge(CommitHook hook, PostCommitHook committed) throws CommitFailedException {
+        purge();
+        branch.merge(hook, committed);
+        return reset();
+    }
+
+    /**
+     * Applied all pending changes to the underlying branch and then
+     * move the node as a separate operation on the underlying store.
+     * This allows stores to optimise move operations instead of
+     * seeing them as an added node followed by a deleted node.
+     */
+    boolean move(String source, String target) {
+        purge();
+        boolean success = branch.move(source, target);
+        super.reset(branch.getHead());
+        return success;
+    }
+
+    /**
+     * Applied all pending changes to the underlying branch and then
+     * copy the node as a separate operation on the underlying store.
+     * This allows stores to optimise copy operations instead of
+     * seeing them as an added node.
+     */
+    boolean copy(String source, String target) {
+        purge();
+        boolean success = branch.copy(source, target);
+        super.reset(branch.getHead());
+        return success;
+    }
+
+    private void purge() {
+        branch.setRoot(getNodeState());
+        super.reset(branch.getHead());
+        updates = 0;
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java Fri Sep 20 09:27:29 2013
@@ -119,7 +119,7 @@ public class MemoryNodeBuilder implement
      * @param parent parent builder
      * @param name name of this node
      */
-    private MemoryNodeBuilder(MemoryNodeBuilder parent, String name) {
+    protected MemoryNodeBuilder(MemoryNodeBuilder parent, String name) {
         this.parent = parent;
         this.name = name;
         this.rootBuilder = parent.rootBuilder;
@@ -189,11 +189,28 @@ public class MemoryNodeBuilder implement
      * Called whenever <em>this</em> node is modified, i.e. a property is
      * added, changed or removed, or a child node is added or removed. Changes
      * inside child nodes or the subtrees below are not reported. The default
-     * implementation does nothing, but subclasses may override this method
-     * to better track changes.
+     * implementation triggers an {@link #updated()} call on the root builder
+     * (unless this is already the root builder), which subclasses can use
+     * to capture aggregate update information across the whole tree.
      */
     protected void updated() {
-        // do nothing
+        if (this != rootBuilder) {
+            rootBuilder.updated();
+        }
+    }
+
+    /**
+     * Accessor for parent builder
+     */
+    protected final MemoryNodeBuilder getParent() {
+        return parent;
+    }
+
+    /**
+     * Accessor for name
+     */
+    protected final String getName() {
+        return name;
     }
 
     //--------------------------------------------------------< NodeBuilder >---
@@ -285,6 +302,33 @@ public class MemoryNodeBuilder implement
     }
 
     @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        if (isRoot()) {
+            return false;
+        } else {
+            NodeState nodeState = getNodeState();
+            remove();
+            if (newParent.exists()) {
+                checkNotNull(newParent).setChildNode(checkNotNull(newName), nodeState);
+                return true;
+            } else {
+                // Move to descendant
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean copyTo(NodeBuilder newParent, String newName) {
+        if (isRoot()) {
+            return false;
+        } else {
+            checkNotNull(newParent).setChildNode(checkNotNull(newName), getNodeState());
+            return true;
+        }
+    }
+
+    @Override
     public long getPropertyCount() {
         return head().getCurrentNodeState().getPropertyCount();
     }
@@ -371,9 +415,9 @@ public class MemoryNodeBuilder implement
     }
 
     /**
-     * @return path of this builder. For debugging purposes only
+     * @return path of this builder.
      */
-    private String getPath() {
+    protected final String getPath() {
         return parent == null ? "/" : getPath(new StringBuilder()).toString();
     }
 

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeBuilder.java (from r1524941, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeBuilder.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeBuilder.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java&r1=1524941&r2=1524957&rev=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeBuilder.java Fri Sep 20 09:27:29 2013
@@ -6,7 +6,7 @@
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,21 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.spi.state;
+package org.apache.jackrabbit.oak.plugins.segment;
 
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
 
-/**
- * Abstract base class for {@link NodeStore} implementations.
- */
-public abstract class AbstractNodeStore implements NodeStore {
+class SegmentNodeBuilder extends MemoryNodeBuilder {
+
+    protected SegmentNodeBuilder(SegmentNodeState base) {
+        super(base);
+    }
 
-    //------------------------------------------------------------< Object >--
+    private SegmentNodeBuilder(SegmentNodeBuilder parent, String name) {
+        super(parent, name);
+    }
 
-    /**
-     * Returns a string representation the head state of this node store.
-     */
-    public String toString() {
-        return getRoot().toString();
+    @Override
+    protected SegmentNodeBuilder createChildBuilder(String name) {
+        return new SegmentNodeBuilder(this, name);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java Fri Sep 20 09:27:29 2013
@@ -25,7 +25,6 @@ import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -34,6 +33,13 @@ import org.apache.jackrabbit.oak.spi.sta
 
 public class SegmentNodeState extends AbstractNodeState {
 
+    static boolean fastEquals(NodeState a, NodeState b) {
+        return a instanceof SegmentNodeState
+                && b instanceof SegmentNodeState
+                && ((SegmentNodeState) a).recordId.equals(
+                        ((SegmentNodeState) b).recordId);
+    }
+
     private final SegmentStore store;
 
     private final RecordId recordId;
@@ -125,7 +131,7 @@ public class SegmentNodeState extends Ab
 
     @Override @Nonnull
     public NodeBuilder builder() {
-        return new MemoryNodeBuilder(this);
+        return new SegmentRootBuilder(this);
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java Fri Sep 20 09:27:29 2013
@@ -26,9 +26,14 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.EmptyObserver;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStore;
+import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 public class SegmentNodeStore extends AbstractNodeStore {
@@ -74,6 +79,30 @@ public class SegmentNodeStore extends Ab
         return getHead().getChildNode(ROOT);
     }
 
+    @Override
+    public synchronized NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook, PostCommitHook committed) throws CommitFailedException {
+        return super.merge(builder, commitHook, committed);    // TODO implement merge
+    }
+
+    @Override @Nonnull
+    public NodeState rebase(@Nonnull NodeBuilder builder) {
+        NodeState oldBase = builder.getBaseState();
+        NodeState newBase = getRoot();
+        if (!SegmentNodeState.fastEquals(oldBase, newBase)) {
+            NodeState head = builder.getNodeState();
+            builder.reset(newBase);
+            head.compareAgainstBaseState(oldBase, new ConflictAnnotatingRebaseDiff(builder));
+        }
+        return builder.getNodeState();
+    }
+
+    @Override @Nonnull
+    public NodeState reset(@Nonnull NodeBuilder builder) {
+        NodeState state = getRoot();
+        checkNotNull(builder).reset(state);
+        return state;
+    }
+
     @Override @Nonnull
     public SegmentNodeStoreBranch branch() {
         return new SegmentNodeStoreBranch(

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java Fri Sep 20 09:27:29 2013
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.segment;
 
+import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -24,6 +26,7 @@ import java.util.Dictionary;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
+import com.mongodb.Mongo;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.ConfigurationPolicy;
@@ -31,10 +34,14 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
 import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
 import org.apache.jackrabbit.oak.plugins.segment.mongo.MongoStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
@@ -42,10 +49,6 @@ import org.apache.jackrabbit.oak.spi.whi
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.osgi.service.component.ComponentContext;
 
-import com.mongodb.Mongo;
-
-import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
-
 @Component(policy = ConfigurationPolicy.REQUIRE)
 @Service(NodeStore.class)
 public class SegmentNodeStoreService extends AbstractNodeStore {
@@ -166,6 +169,23 @@ public class SegmentNodeStoreService ext
         return getDelegate().getRoot();
     }
 
+    @Nonnull
+    @Override
+    public NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook,
+            PostCommitHook committed) throws CommitFailedException {
+        return getDelegate().merge(builder, commitHook, committed);
+    }
+
+    @Override
+    public NodeState rebase(@Nonnull NodeBuilder builder) {
+        return getDelegate().rebase(builder);
+    }
+
+    @Override
+    public NodeState reset(@Nonnull NodeBuilder builder) {
+        return getDelegate().reset(builder);
+    }
+
     @Override @Nonnull
     public NodeStoreBranch branch() {
         return getDelegate().branch();

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentRootBuilder.java (from r1524941, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentRootBuilder.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentRootBuilder.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java&r1=1524941&r2=1524957&rev=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentRootBuilder.java Fri Sep 20 09:27:29 2013
@@ -6,7 +6,7 @@
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,21 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.spi.state;
+package org.apache.jackrabbit.oak.plugins.segment;
 
-
-/**
- * Abstract base class for {@link NodeStore} implementations.
- */
-public abstract class AbstractNodeStore implements NodeStore {
-
-    //------------------------------------------------------------< Object >--
+class SegmentRootBuilder extends SegmentNodeBuilder {
 
     /**
-     * Returns a string representation the head state of this node store.
+     * Number of content updates that need to happen before the updates
+     * are automatically purged to the private branch.
      */
-    public String toString() {
-        return getRoot().toString();
+    private static final int UPDATE_LIMIT =
+            Integer.getInteger("update.limit", 1000);
+
+    private long updateCount = 0;
+
+    SegmentRootBuilder(SegmentNodeState base) {
+        super(base);
+    }
+
+    @Override
+    protected void updated() {
+        updateCount++;
+        if (updateCount > UPDATE_LIMIT) {
+            // TODO: flush
+            updateCount = 0;
+        }
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStore.java Fri Sep 20 09:27:29 2013
@@ -17,12 +17,73 @@
 package org.apache.jackrabbit.oak.spi.state;
 
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
+
 /**
  * Abstract base class for {@link NodeStore} implementations.
  */
 public abstract class AbstractNodeStore implements NodeStore {
 
-    //------------------------------------------------------------< Object >--
+    /**
+     * This default implementation is equal to first rebasing the builder
+     * and then applying it to a new branch and immediately merging it back.
+     * <p>
+     * <em>Note:</em> it is the caller's responsibility to ensure atomicity.
+     *
+     * @param builder  the builder whose changes to apply
+     * @param commitHook the commit hook to apply while merging changes
+     * @param committed  the pos commit hook
+     * @return the node state resulting from the merge.
+     * @throws CommitFailedException
+     */
+    @Override
+    public NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook,
+            PostCommitHook committed) throws CommitFailedException {
+        checkNotNull(commitHook);
+        rebase(checkNotNull(builder));
+        NodeStoreBranch branch = branch();
+        branch.setRoot(builder.getNodeState());
+        NodeState merged = branch.merge(commitHook, committed);
+        builder.reset(merged);
+        return merged;
+    }
+
+    /**
+     * This default implementation is equal to applying the differences between
+     * the builders base state and its head state to a fresh builder on the
+     * stores root state using {@link ConflictAnnotatingRebaseDiff} for resolving
+     * conflicts.
+     * @param builder  the builder to rebase
+     * @return the node state resulting from the rebase.
+     */
+    @Override
+    public NodeState rebase(@Nonnull NodeBuilder builder) {
+        NodeState head = checkNotNull(builder).getNodeState();
+        NodeState base = builder.getBaseState();
+        builder.reset(getRoot());
+        head.compareAgainstBaseState(base, new ConflictAnnotatingRebaseDiff(builder));
+        return builder.getNodeState();
+    }
+
+    /**
+     * This default implementation is equal resetting the builder to the root of
+     * the store and returning the resulting node state from the builder.
+     * @param builder the builder to reset
+     * @return the node state resulting from the reset.
+     */
+    @Override
+    public NodeState reset(@Nonnull NodeBuilder builder) {
+        builder.reset(getRoot());
+        return builder.getNodeState();
+    }
+
+//------------------------------------------------------------< Object >--
 
     /**
      * Returns a string representation the head state of this node store.

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeBuilder.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeBuilder.java Fri Sep 20 09:27:29 2013
@@ -224,6 +224,22 @@ public interface NodeBuilder {
     boolean remove();
 
     /**
+     * Move this child to a new parent with a new name.
+     * @param newParent  builder for the new parent.
+     * @param newName  name of this child at the new parent
+     * @return  {@code true} on success, {@code false} otherwise
+     */
+    boolean moveTo(@Nonnull NodeBuilder newParent, @Nonnull String newName);
+
+    /**
+     * Copy this child to a new parent with a new name.
+     * @param newParent  builder for the new parent.
+     * @param newName  name of this child at the new parent
+     * @return  {@code true} on success, {@code false} otherwise
+     */
+    boolean copyTo(@Nonnull NodeBuilder newParent, @Nonnull String newName);
+
+    /**
      * Returns the current number of properties.
      *
      * @return number of properties

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java Fri Sep 20 09:27:29 2013
@@ -23,6 +23,9 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
 
 /**
  * Storage abstraction for trees. At any given point in time the stored
@@ -43,6 +46,36 @@ public interface NodeStore {
     NodeState getRoot();
 
     /**
+     * Merges the changes from the passed {@code builder} into
+     * the store.
+     *
+     * @param builder  the builder whose changes to apply
+     * @param commitHook the commit hook to apply while merging changes
+     * @param committed  the post commit hook
+     * @return the node state resulting from the merge.
+     * @throws CommitFailedException if the merge failed
+     */
+    @Nonnull
+    NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook,
+            PostCommitHook committed) throws CommitFailedException;
+
+    /**
+     * Rebase the changes in the passed {@code builder} on top of the current root state.
+     * @param builder  the builder to rebase
+     * @return the node state resulting from the rebase.
+     */
+    @Nonnull
+    NodeState rebase(@Nonnull NodeBuilder builder);
+
+    /**
+     * Reset the passed {@code builder} by throwing away all its changes and
+     * setting its base state to the current root state.
+     * @param builder the builder to reset
+     * @return the node state resulting from the reset.
+     */
+    NodeState reset(@Nonnull NodeBuilder builder);
+
+    /**
      * Creates a new branch of the tree to which transient changes can be applied.
      *
      * @return branch

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ReadOnlyBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ReadOnlyBuilder.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ReadOnlyBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ReadOnlyBuilder.java Fri Sep 20 09:27:29 2013
@@ -98,6 +98,16 @@ public class ReadOnlyBuilder implements 
     }
 
     @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        throw unsupported();
+    }
+
+    @Override
+    public boolean copyTo(NodeBuilder newParent, String newName) {
+        throw unsupported();
+    }
+
+    @Override
     public long getPropertyCount() {
         return state.getPropertyCount();
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/NodeStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/NodeStoreTest.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/NodeStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/NodeStoreTest.java Fri Sep 20 09:27:29 2013
@@ -31,6 +31,8 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
+import javax.annotation.Nonnull;
+
 import org.apache.jackrabbit.oak.NodeStoreFixture;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -351,6 +353,44 @@ public class NodeStoreTest {
     }
 
     @Test
+    public void merge() throws CommitFailedException {
+        NodeStoreBranch branch1 = store.branch();
+        NodeBuilder builder1 = branch1.getHead().builder();
+
+        NodeStoreBranch branch2 = store.branch();
+        NodeBuilder builder2 = branch1.getHead().builder();
+
+        builder1.setChildNode("node1");
+        branch1.setRoot(builder1.getNodeState());
+        builder2.setChildNode("node2");
+        branch2.setRoot(builder2.getNodeState());
+
+        store.merge(builder1, EmptyHook.INSTANCE, new PostCommitHook() {
+            @Override
+            public void contentChanged(@Nonnull NodeState before, @Nonnull NodeState after) {
+                assertFalse(before.hasChildNode("node1"));
+                assertFalse(before.hasChildNode("node2"));
+                assertTrue(after.hasChildNode("node1"));
+                assertFalse(after.hasChildNode("node2"));
+            }
+        });
+        assertTrue(store.getRoot().hasChildNode("node1"));
+        assertFalse(store.getRoot().hasChildNode("node2"));
+
+        store.merge(builder2, EmptyHook.INSTANCE, new PostCommitHook() {
+            @Override
+            public void contentChanged(@Nonnull NodeState before, @Nonnull NodeState after) {
+                assertTrue(before.hasChildNode("node1"));
+                assertFalse(before.hasChildNode("node2"));
+                assertTrue(after.hasChildNode("node1"));
+                assertTrue(after.hasChildNode("node2"));
+            }
+        });
+        assertTrue(store.getRoot().hasChildNode("node1"));
+        assertTrue(store.getRoot().hasChildNode("node2"));
+    }
+
+    @Test
     public void compareAgainstBaseState0() throws CommitFailedException {
         compareAgainstBaseState(0);
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java Fri Sep 20 09:27:29 2013
@@ -289,6 +289,81 @@ public class MemoryNodeBuilderTest {
     }
 
     @Test
+    public void testMove() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").moveTo(rootBuilder.child("x"), "yy"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertFalse(newRoot.hasChildNode("y"));
+        assertTrue(newRoot.hasChildNode("x"));
+        assertTrue(newRoot.getChildNode("x").hasChildNode("q"));
+        assertTrue(newRoot.getChildNode("x").hasChildNode("yy"));
+    }
+
+    @Test
+    public void testRename() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").moveTo(rootBuilder, "yy"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertFalse(newRoot.hasChildNode("y"));
+        assertTrue(newRoot.hasChildNode("yy"));
+    }
+
+    @Test
+    public void testMoveToSelf() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").moveTo(rootBuilder, "y"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertTrue(newRoot.hasChildNode("y"));
+    }
+
+    @Test
+    public void testMoveToDescendant() {
+        NodeBuilder rootBuilder = base.builder();
+        assertFalse(rootBuilder.getChildNode("x").moveTo(rootBuilder.getChildNode("x"), "xx"));
+    }
+
+    @Test
+    public void testCopy() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").copyTo(rootBuilder.child("x"), "yy"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertTrue(newRoot.hasChildNode("y"));
+        assertTrue(newRoot.hasChildNode("x"));
+        assertTrue(newRoot.getChildNode("x").hasChildNode("q"));
+        assertTrue(newRoot.getChildNode("x").hasChildNode("yy"));
+    }
+
+    @Test
+    public void testDuplicate() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").copyTo(rootBuilder, "yy"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertTrue(newRoot.hasChildNode("y"));
+        assertTrue(newRoot.hasChildNode("yy"));
+    }
+
+    @Test
+    public void testCopyToSelf() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("y").copyTo(rootBuilder, "y"));
+    }
+
+    @Test
+    public void testCopyToDescendant() {
+        NodeBuilder rootBuilder = base.builder();
+        assertTrue(rootBuilder.getChildNode("x").copyTo(rootBuilder.getChildNode("x"), "xx"));
+
+        NodeState newRoot = rootBuilder.getNodeState();
+        assertTrue(rootBuilder.hasChildNode("x"));
+        assertTrue(rootBuilder.getChildNode("x").hasChildNode("xx"));
+    }
+
+    @Test
     public void assertion_OAK781() {
         NodeBuilder rootBuilder = EMPTY_NODE.builder();
         rootBuilder.child("a").setChildNode("b", createBC(false));

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingIndexTest.java?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingIndexTest.java Fri Sep 20 09:27:29 2013
@@ -20,26 +20,28 @@ package org.apache.jackrabbit.oak.query.
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.core.MicroKernelImpl;
 import org.apache.jackrabbit.oak.kernel.KernelNodeState;
+import org.apache.jackrabbit.oak.kernel.KernelNodeStore;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.junit.Test;
 
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-
 /**
  * Tests the TraversingCursor.
  */
 public class TraversingIndexTest {
-
     private final MicroKernel mk = new MicroKernelImpl();
+    private final KernelNodeStore store = new KernelNodeStore(mk);
 
     private final LoadingCache<String, KernelNodeState> cache =
             CacheBuilder.newBuilder().build(new CacheLoader<String, KernelNodeState>() {
@@ -50,14 +52,10 @@ public class TraversingIndexTest {
                     String path = key.substring(slash);
                     // this method is strictly called _after_ the cache is initialized,
                     // when the fields are set
-                    return new KernelNodeState(getMicroKernel(), path, revision, getCache());
+                    return new KernelNodeState(store, path, revision, getCache());
                 }
             });
-    
-    MicroKernel getMicroKernel() {
-        return mk;
-    }
-    
+
     LoadingCache<String, KernelNodeState> getCache() {
         return cache;
     }
@@ -73,7 +71,7 @@ public class TraversingIndexTest {
 
         f.setPath("/");
         List<String> paths = new ArrayList<String>();
-        Cursor c = t.query(f, new KernelNodeState(mk, "/", head, cache));
+        Cursor c = t.query(f, new KernelNodeState(store, "/", head, cache));
         while (c.hasNext()) {
             paths.add(c.next().getPath());
         }
@@ -88,7 +86,7 @@ public class TraversingIndexTest {
         assertFalse(c.hasNext());
 
         f.setPath("/nowhere");
-        c = t.query(f, new KernelNodeState(mk, "/", head, cache));
+        c = t.query(f, new KernelNodeState(store, "/", head, cache));
         assertFalse(c.hasNext());
         // endure it stays false
         assertFalse(c.hasNext());

Modified: jackrabbit/oak/trunk/oak-parent/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-parent/pom.xml?rev=1524957&r1=1524956&r2=1524957&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-parent/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-parent/pom.xml Fri Sep 20 09:27:29 2013
@@ -34,7 +34,7 @@
   <packaging>pom</packaging>
 
   <properties>
-    <test.opts>-Xmx512m -XX:MaxPermSize=48m -Doak.root.purgeLimit=100 -Djava.awt.headless=true</test.opts>
+    <test.opts>-Xmx512m -XX:MaxPermSize=48m -Dupdate.limit=100 -Djava.awt.headless=true</test.opts>
     <skip.deployment>false</skip.deployment>
     <known.issues />
     <project.reporting.outputEncoding>



Mime
View raw message