jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mdue...@apache.org
Subject svn commit: r1513469 - 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 Tue, 13 Aug 2013 13:03:05 GMT
Author: mduerig
Date: Tue Aug 13 13:03:04 2013
New Revision: 1513469

URL: http://svn.apache.org/r1513469
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. When running on SegmentMK the relevant implementations in SegmentNodeStore fall back to their defaults, might not be optimal.

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java   (with props)
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/kernel/KernelNodeStoreBranch.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/SegmentNodeStore.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/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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -18,6 +18,11 @@
  */
 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;
 import java.security.PrivilegedAction;
@@ -56,18 +61,9 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 import org.apache.jackrabbit.oak.util.LazyValue;
 
-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;
-
 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;
@@ -90,14 +86,19 @@ public abstract class AbstractRoot imple
     private final MutableTree rootTree;
 
     /**
+     * Unsecured builder for the root tree
+     */
+    private final NodeBuilder builder;
+
+    /**
      * Secured builder for the root tree
      */
     private final SecureNodeBuilder secureBuilder;
 
     /**
-     * Unsecured builder for the root tree
+     * Base state of the root tree
      */
-    private final NodeBuilder builder;
+    private NodeState base;
 
     /**
      * Current branch this root operates on
@@ -110,8 +111,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;
 
@@ -147,9 +147,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);
     }
@@ -169,22 +168,27 @@ public abstract class AbstractRoot imple
 
     @Override
     public boolean move(String sourcePath, String destPath) {
-        if (PathUtils.isAncestor(sourcePath, destPath)) {
+        if (isAncestor(sourcePath, destPath)) {
             return false;
         }
 
         checkLive();
-        MutableTree destParent = rootTree.getTree(getParentPath(destPath));
-        if (!destParent.exists()) {
+        MutableTree source = rootTree.getTree(sourcePath);
+        if (!source.exists()) {
             return false;
         }
-        purgePendingChanges();
-        boolean success = branch.move(sourcePath, destPath);
-        reset();
+
+        String newName = getName(destPath);
+        MutableTree newParent = rootTree.getTree(getParentPath(destPath));
+        if (!newParent.exists() || newParent.hasChild(newName)) {
+            return false;
+        }
+
+        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;
@@ -193,9 +197,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();
@@ -213,9 +226,7 @@ public abstract class AbstractRoot imple
     public void rebase() {
         checkLive();
         if (!store.getRoot().equals(getBaseState())) {
-            purgePendingChanges();
-            branch.rebase();
-            reset();
+            secureBuilder.reset(store.rebase(builder));
             if (permissionProvider != null) {
                 permissionProvider.get().refresh();
             }
@@ -225,8 +236,8 @@ public abstract class AbstractRoot imple
     @Override
     public final void refresh() {
         checkLive();
-        branch = store.branch();
-        reset();
+        base = store.reset(builder);
+        secureBuilder.reset(base);
         modCount = 0;
         if (permissionProvider != null) {
             permissionProvider.get().refresh();
@@ -236,13 +247,17 @@ public abstract class AbstractRoot imple
     @Override
     public void commit() throws CommitFailedException {
         checkLive();
-        purgePendingChanges();
         CommitFailedException exception = Subject.doAs(
                 getCommitSubject(), new PrivilegedAction<CommitFailedException>() {
             @Override
             public CommitFailedException run() {
                 try {
-                    branch.merge(getCommitHook(), postHook);
+                    base = store.merge(builder, getCommitHook(), postHook);
+                    secureBuilder.reset(base);
+                    modCount = 0;
+                    if (permissionProvider != null) {
+                        permissionProvider.get().refresh();
+                    }
                     return null;
                 } catch (CommitFailedException e) {
                     return e;
@@ -252,7 +267,6 @@ public abstract class AbstractRoot imple
         if (exception != null) {
             throw exception;
         }
-        refresh();
     }
 
     /**
@@ -351,7 +365,7 @@ public abstract class AbstractRoot imple
      */
     @Nonnull
     NodeState getBaseState() {
-        return branch.getBase();
+        return base;
     }
 
     /**
@@ -360,15 +374,11 @@ public abstract class AbstractRoot imple
      * @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 >---
@@ -384,22 +394,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();
@@ -462,7 +456,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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -371,14 +371,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
-     */
-    void moveTo(MutableTree destParent, String destName) {
-        name = destName;
-        parent = destParent;
+     * {@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
+     */
+    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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -150,6 +150,17 @@ class SecureNodeBuilder implements NodeB
         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);

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=1513469&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 Tue Aug 13 13:03:04 2013
@@ -0,0 +1,72 @@
+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;
+
+/**
+ * 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;
+
+    public 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);
+    }
+
+    @Override
+    protected void updated() {
+        root.updated();
+    }
+
+    /**
+     * 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);
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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>
+     * TODO: 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) {
@@ -421,9 +430,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. See {@link KernelNodeState#builder()}.
+     */
     @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);
+        }
     }
 
     /**
@@ -520,6 +543,18 @@ 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}
+     */
+    NodeState setBranch() {
+        isBranch = true;
+        return this;
+    }
+
+    /**
      * @return the approximate memory usage of this node state.
      */
     synchronized int getMemory() {

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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -37,9 +37,13 @@ import com.google.common.util.concurrent
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.oak.cache.CacheStats;
+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.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,53 @@ 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 {
+            return super.merge(builder, commitHook, committed);
+        }
+    }
+
+    /**
+     * 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, getRoot(), mergeLock);
@@ -201,6 +252,10 @@ public class KernelNodeStore extends Abs
         return getRootState(kernel.commit("", jsop, baseRevision, null));
     }
 
+    NodeStoreBranch branch(KernelNodeState base) {
+        return new KernelNodeStoreBranch(this, base, mergeLock);
+    }
+
     NodeState merge(String headRevision) {
         return getRootState(kernel.merge(headRevision, null));
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1513469&r1=1513468&r2=1513469&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Tue Aug 13 13:03:04 2013
@@ -238,7 +238,7 @@ class KernelNodeStoreBranch extends Abst
         persistTransientHead();
 
         headRevision = kernel.commit("", jsop, headRevision, null);
-        head = store.getRootState(headRevision);
+        head = store.getRootState(headRevision).setBranch();
     }
 
     private void persistTransientHead() {
@@ -261,7 +261,7 @@ class KernelNodeStoreBranch extends Abst
                 }
             } else {
                 // compare against head of branch
-                NodeState branchHead = store.getRootState(headRevision);
+                NodeState branchHead = store.getRootState(headRevision).setBranch();
                 if (head.equals(branchHead)) {
                     // nothing to persist
                     success = true;
@@ -273,7 +273,7 @@ class KernelNodeStoreBranch extends Abst
             // if we get here we have something to persist
             // and a branch exists
             headRevision = kernel.commit("", diff.toString(), headRevision, null);
-            head = store.getRootState(headRevision);
+            head = store.getRootState(headRevision).setBranch();
             success = true;
         } finally {
             // revert to old state if unsuccessful

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=1513469&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 Tue Aug 13 13:03:04 2013
@@ -0,0 +1,125 @@
+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;
+
+    /**
+     * 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;
+
+    public KernelRootBuilder(KernelNodeState base, KernelNodeStore store) {
+        super(checkNotNull(base));
+        this.store = store;
+        this.branch = store.branch(base);
+    }
+
+    //--------------------------------------------------< MemoryNodeBuilder >---
+
+    @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);
+        branch = store.branch();  // This keeps builders valid across merges (in contrast to branches)
+        return branch.getHead();
+    }
+
+    /**
+     * 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);
+        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);
+        reset(branch.getHead());
+        return success;
+    }
+
+    private void purge() {
+        branch.setRoot(getNodeState());
+        reset(branch.getHead());
+        updates = 0;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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;
@@ -285,6 +285,27 @@ public class MemoryNodeBuilder implement
     }
 
     @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        if (isRoot()) {
+            return false;
+        } else {
+            checkNotNull(newParent).setChildNode(checkNotNull(newName), getNodeState());
+            remove();
+            return true;
+        }
+    }
+
+    @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 +392,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();
     }
 

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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -26,11 +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.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 
 public class SegmentNodeStore extends AbstractNodeStore {
 
@@ -75,6 +78,21 @@ public class SegmentNodeStore extends Ab
         return getHead().getChildNode(ROOT);
     }
 
+    @Override
+    public NodeState merge(@Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook, PostCommitHook committed) throws CommitFailedException {
+        return super.merge(builder, commitHook, committed);    // TODO implement merge
+    }
+
+    @Override
+    public NodeState rebase(@Nonnull NodeBuilder builder) {
+        return super.rebase(builder);    // TODO implement rebase
+    }
+
+    @Override
+    public NodeState reset(@Nonnull NodeBuilder builder) {
+        return super.reset(builder);    // TODO implement reset
+    }
+
     @Override @Nonnull
     public SegmentNodeStoreBranch branch() {
         return new SegmentNodeStoreBranch(

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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -17,12 +17,72 @@
 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 atomically first rebase the builder
+     * and then applying it to a new branch and immediately merging it back.
+     *
+     * @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(builder);
+        checkNotNull(commitHook);
+        synchronized (this) {
+            rebase(builder);
+            NodeStoreBranch branch = branch();
+            branch.setRoot(builder.getNodeState());
+            return branch.merge(commitHook, committed);
+        }
+    }
+
+    /**
+     * 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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 2013
@@ -30,6 +30,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;
@@ -322,6 +324,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/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=1513469&r1=1513468&r2=1513469&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 Tue Aug 13 13:03:04 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=1513469&r1=1513468&r2=1513469&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-parent/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-parent/pom.xml Tue Aug 13 13:03:04 2013
@@ -34,7 +34,7 @@
   <packaging>pom</packaging>
 
   <properties>
-    <test.opts>-Xmx512m -XX:MaxPermSize=48m -Doak.root.purgeLimit=100</test.opts>
+    <test.opts>-Xmx512m -XX:MaxPermSize=32m -Dupdate.limit=100</test.opts>
     <skip.deployment>false</skip.deployment>
     <known.issues />
     <project.reporting.outputEncoding>



Mime
View raw message