You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2013/10/29 12:57:13 UTC

svn commit: r1536679 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak: kernel/KernelNodeStore.java kernel/KernelNodeStoreBranch.java spi/state/AbstractNodeStoreBranch.java

Author: mreutegg
Date: Tue Oct 29 11:57:12 2013
New Revision: 1536679

URL: http://svn.apache.org/r1536679
Log:
OAK-1123: Reusable NodeStoreBranch base implementation

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java   (contents, props changed)
      - copied, changed from r1536363, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java

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=1536679&r1=1536678&r2=1536679&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 Oct 29 11:57:12 2013
@@ -248,8 +248,8 @@ public class KernelNodeStore implements 
         }
     }
 
-    NodeStoreBranch createBranch(NodeState base) {
-        return new KernelNodeStoreBranch(this, mergeLock, (KernelNodeState) base);
+    NodeStoreBranch createBranch(KernelNodeState base) {
+        return new KernelNodeStoreBranch(this, changeDispatcher, mergeLock, base);
     }
 
     MicroKernel getKernel() {
@@ -257,6 +257,10 @@ public class KernelNodeStore implements 
     }
 
     KernelNodeState commit(String jsop, KernelNodeState base) {
+        if (jsop.isEmpty()) {
+            // nothing to commit
+            return base;
+        }
         KernelNodeState rootState = getRootState(kernel.commit("", jsop, base.getRevision(), null));
         if (base.isBranch()) {
             rootState.setBranch();
@@ -272,20 +276,7 @@ public class KernelNodeStore implements 
         return getRootState(kernel.rebase(branchHead.getRevision(), base.getRevision())).setBranch();
     }
 
-    NodeState merge(KernelNodeState branchHead) {
+    KernelNodeState merge(KernelNodeState branchHead) {
         return getRootState(kernel.merge(branchHead.getRevision(), null));
     }
-
-    void beforeCommit(NodeState root) {
-        changeDispatcher.beforeCommit(root);
-    }
-
-    void localCommit(NodeState root, CommitInfo info) {
-        changeDispatcher.localCommit(root, info);
-    }
-
-    void afterCommit(NodeState root) {
-        changeDispatcher.afterCommit(root);
-    }
-
 }

Added: 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=1536679&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Tue Oct 29 11:57:12 2013
@@ -0,0 +1,134 @@
+/*
+ * 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 java.io.IOException;
+import java.util.concurrent.locks.Lock;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.observation.ChangeDispatcher;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeStoreBranch;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@code NodeStoreBranch} based on {@link MicroKernel} branching and merging.
+ * This implementation keeps changes in memory up to a certain limit and writes
+ * them back to the Microkernel branch when the limit is exceeded.
+ */
+public class KernelNodeStoreBranch extends
+        AbstractNodeStoreBranch<KernelNodeStore, KernelNodeState> {
+
+    /** Lock for coordinating concurrent merge operations */
+    private final Lock mergeLock;
+
+    private final BlobSerializer blobs = new BlobSerializer() {
+        @Override
+        public String serialize(Blob blob) {
+            KernelBlob kernelBlob;
+            if (blob instanceof KernelBlob) {
+                kernelBlob = (KernelBlob) blob;
+            } else {
+                try {
+                    kernelBlob = store.createBlob(blob.getNewStream());
+                } catch (IOException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+            return kernelBlob.getBinaryID();
+        }
+    };
+
+    public KernelNodeStoreBranch(KernelNodeStore kernelNodeStore,
+                                 ChangeDispatcher dispatcher,
+                                 Lock mergeLock,
+                                 KernelNodeState base) {
+        super(kernelNodeStore, dispatcher, base);
+        this.mergeLock = checkNotNull(mergeLock);
+    }
+
+    //----------------------< AbstractNodeStoreBranch >-------------------------
+
+    @Override
+    public KernelNodeState createBranch(KernelNodeState state) {
+        return store.branch(state);
+    }
+
+    @Override
+    public KernelNodeState getRoot() {
+        return store.getRoot();
+    }
+
+    @Override
+    protected KernelNodeState rebase(KernelNodeState branchHead,
+                                     KernelNodeState base) {
+        return store.rebase(branchHead, base);
+    }
+
+    @Override
+    protected KernelNodeState merge(KernelNodeState branchHead) {
+        return store.merge(branchHead);
+    }
+
+    @Override
+    protected KernelNodeState persist(NodeState toPersist, KernelNodeState base) {
+        JsopDiff diff = new JsopDiff(blobs);
+        toPersist.compareAgainstBaseState(base, diff);
+        return store.commit(diff.toString(), base);
+    }
+
+    @Override
+    protected KernelNodeState copy(String source,
+                                   String target,
+                                   KernelNodeState base) {
+        return store.commit("*\"" + source + "\":\"" + target + '"', base);
+    }
+
+    @Override
+    protected KernelNodeState move(String source,
+                                   String target,
+                                   KernelNodeState base) {
+        return store.commit(">\"" + source + "\":\"" + target + '"', base);
+    }
+
+//------------------------< NodeStoreBranch >-------------------------------
+
+    @Nonnull
+    @Override
+    public NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info)
+            throws CommitFailedException {
+        mergeLock.lock();
+        try {
+            return super.merge(hook, info);
+        } catch (MicroKernelException e) {
+            throw new CommitFailedException(
+                    "Kernel", 1,
+                    "Failed to merge changes to the underlying MicroKernel", e);
+        } finally {
+            mergeLock.unlock();
+        }
+    }
+}

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

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

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java (from r1536363, 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/spi/state/AbstractNodeStoreBranch.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java&r1=1536363&r2=1536679&rev=1536679&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/spi/state/AbstractNodeStoreBranch.java Tue Oct 29 11:57:12 2013
@@ -14,43 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.kernel;
+package org.apache.jackrabbit.oak.spi.state;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
 
-import java.io.IOException;
-import java.util.concurrent.locks.Lock;
-
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.apache.jackrabbit.mk.api.MicroKernel;
-import org.apache.jackrabbit.mk.api.MicroKernelException;
-import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.observation.ChangeDispatcher;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
-import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 
 /**
- * {@code NodeStoreBranch} based on {@link MicroKernel} branching and merging.
+ * A base implementation of a node store branch, which supports partially
+ * persisted branches.
+ * <p>
  * This implementation keeps changes in memory up to a certain limit and writes
- * them back to the Microkernel branch when the limit is exceeded.
+ * them back to the underlying branch when the limit is exceeded.
  */
-class KernelNodeStoreBranch implements NodeStoreBranch {
+public abstract class AbstractNodeStoreBranch<S extends NodeStore, N extends NodeState>
+        implements NodeStoreBranch {
 
     /** The underlying store to which this branch belongs */
-    private final KernelNodeStore store;
+    protected final S store;
 
-    /** Lock for coordinating concurrent merge operations */
-    private final Lock mergeLock;
+    /** The dispatcher to report changes */
+    protected final ChangeDispatcher dispatcher;
 
     /**
      * State of the this branch. Either {@link Unmodified}, {@link InMemory}, {@link Persisted}
@@ -59,31 +53,97 @@ class KernelNodeStoreBranch implements N
      */
     private BranchState branchState;
 
-    private final BlobSerializer blobs = new BlobSerializer() {
-        @Override
-        public String serialize(Blob blob) {
-            KernelBlob kernelBlob;
-            if (blob instanceof KernelBlob) {
-                kernelBlob = (KernelBlob) blob;
-            } else {
-                try {
-                    kernelBlob = store.createBlob(blob.getNewStream());
-                } catch (IOException e) {
-                    throw new IllegalStateException(e);
-                }
-            }
-            return kernelBlob.getBinaryID();
-        }
-    };
-
-    public KernelNodeStoreBranch(KernelNodeStore kernelNodeStore, Lock mergeLock,
-            KernelNodeState base) {
+    public AbstractNodeStoreBranch(S kernelNodeStore,
+                                   ChangeDispatcher dispatcher,
+                                   N base) {
 
         this.store = checkNotNull(kernelNodeStore);
-        this.mergeLock = checkNotNull(mergeLock);
+        this.dispatcher = dispatcher;
         branchState = new Unmodified(checkNotNull(base));
     }
 
+    /**
+     * @return the current root of the underlying store.
+     */
+    protected abstract N getRoot();
+
+    /**
+     * Create a new branch state from the given state.
+     *
+     * @param state the state from where to create a branch from.
+     * @return the branch state.
+     */
+    protected abstract N createBranch(N state);
+
+    /**
+     * Rebases the branch head to the given base.
+     *
+     * @param branchHead the head state of a branch.
+     * @param base the new base state for the branch.
+     * @return the rebased branch head.
+     */
+    protected abstract N rebase(N branchHead, N base);
+
+    /**
+     * Merges the branch head and returns the result state of the merge.
+     *
+     * @param branchHead the head of the branch to merge.
+     * @return the result state of the merge.
+     */
+    protected abstract N merge(N branchHead);
+
+    /**
+     * Persists the changes between <code>toPersist</code> and <code>base</code>
+     * to the underlying store.
+     * <p>
+     * While this method does not declare any exceptions to be thrown, an
+     * implementation may still throw a runtime exception specific to the
+     * concrete implementation of this node store branch.
+     *
+     * @param toPersist the state with the changes on top of <code>base</code>.
+     * @param base the base state.
+     * @return the state with the persisted changes.
+     */
+    protected abstract N persist(NodeState toPersist, N base);
+
+    /**
+     * Perform a potentially optimized copy operation directly on the underlying
+     * store.
+     * <p>
+     * This base class ensures that preconditions are met (e.g. the source
+     * exists), which means an implementation of this method just needs to
+     * perform the copy operation.
+     * <p>
+     * While this method does not declare any exceptions to be thrown, an
+     * implementation may still throw a runtime exception specific to the
+     * concrete implementation of this node store branch.
+     *
+     * @param source the source of the copy operation.
+     * @param target the destination of the copy operation.
+     * @param base the base state.
+     * @return the result of the copy operation.
+     */
+    protected abstract N copy(String source, String target, N base);
+
+    /**
+     * Perform a potentially optimized move operation directly on the underlying
+     * store.
+     * <p>
+     * This base class ensures that preconditions are met (e.g. the source
+     * exists), which means an implementation of this method just needs to
+     * perform the move operation.
+     * <p>
+     * While this method does not declare any exceptions to be thrown, an
+     * implementation may still throw a runtime exception specific to the
+     * concrete implementation of this node store branch.
+     *
+     * @param source the source of the move operation.
+     * @param target the destination of the move operation.
+     * @param base the base state.
+     * @return the result of the move operation.
+     */
+    protected abstract N move(String source, String target, N base);
+
     @Override
     public String toString() {
         return branchState.toString();
@@ -127,7 +187,7 @@ class KernelNodeStoreBranch implements N
             // destination exists already
             return false;
         }
-        branchState.persist().commit(">\"" + source + "\":\"" + target + '"');
+        branchState.persist().move(source, target);
         return true;
     }
 
@@ -146,7 +206,7 @@ class KernelNodeStoreBranch implements N
             // destination exists already
             return false;
         }
-        branchState.persist().commit("*\"" + source + "\":\"" + target + '"');
+        branchState.persist().copy(source, target);
         return true;
     }
 
@@ -162,6 +222,8 @@ class KernelNodeStoreBranch implements N
         branchState.rebase();
     }
 
+    //----------------------------< internal >----------------------------------
+
     private NodeState getNode(String path) {
         NodeState node = getHead();
         for (String name : elements(path)) {
@@ -176,9 +238,9 @@ class KernelNodeStoreBranch implements N
      */
     private abstract class BranchState {
         /** Root state of the base revision of this branch */
-        protected KernelNodeState base;
+        protected N base;
 
-        protected BranchState(KernelNodeState base) {
+        protected BranchState(N base) {
             this.base = base;
         }
 
@@ -186,11 +248,12 @@ class KernelNodeStoreBranch implements N
          * Persist this branch to an underlying branch in the {@code MicroKernel}.
          */
         Persisted persist() {
-            branchState = new Persisted(base, getHead());
-            return (Persisted) branchState;
+            Persisted p = new Persisted(base, getHead());
+            branchState = p;
+            return p;
         }
 
-        KernelNodeState getBase(){
+        N getBase(){
             return base;
         }
 
@@ -201,6 +264,21 @@ class KernelNodeStoreBranch implements N
 
         abstract void rebase();
 
+        /**
+         * Runs the commit hook on the changes tracked with this branch state
+         * merges the result.
+         * <p>
+         * In addition to the {@link CommitFailedException}, an implementation
+         * may also throw an unchecked exception when an error occurs while
+         * persisting the changes. This exception is implementation specific
+         * and it is the responsibility of the caller to convert it into a
+         * {@link CommitFailedException}.
+         *
+         * @param hook the commit hook to run.
+         * @param info the associated commit info.
+         * @return the result of the merge.
+         * @throws CommitFailedException if a commit hook rejected the changes.
+         */
         @Nonnull
         abstract NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info)
                 throws CommitFailedException;
@@ -217,7 +295,7 @@ class KernelNodeStoreBranch implements N
      * </ul>
      */
     private class Unmodified extends BranchState {
-        Unmodified(KernelNodeState base) {
+        Unmodified(N base) {
             super(base);
         }
 
@@ -227,6 +305,7 @@ class KernelNodeStoreBranch implements N
         }
 
         @Override
+        @Nonnull
         NodeState getHead() {
             return base;
         }
@@ -240,11 +319,12 @@ class KernelNodeStoreBranch implements N
 
         @Override
         void rebase() {
-            base = store.getRoot();
+            base = getRoot();
         }
 
         @Override
-        NodeState merge(CommitHook hook, CommitInfo info) throws CommitFailedException {
+        @Nonnull
+        NodeState merge(@Nonnull CommitHook hook, CommitInfo info) {
             branchState = new Merged(base);
             return base;
         }
@@ -271,12 +351,13 @@ class KernelNodeStoreBranch implements N
             return "InMemory[" + base + ", " + head + ']';
         }
 
-        InMemory(KernelNodeState base, NodeState head) {
+        InMemory(N base, NodeState head) {
             super(base);
             this.head = head;
         }
 
         @Override
+        @Nonnull
         NodeState getHead() {
             return head;
         }
@@ -293,7 +374,7 @@ class KernelNodeStoreBranch implements N
 
         @Override
         void rebase() {
-            KernelNodeState root = store.getRoot();
+            N root = getRoot();
             NodeBuilder builder = root.builder();
             head.compareAgainstBaseState(base, new ConflictAnnotatingRebaseDiff(builder));
             head = builder.getNodeState();
@@ -301,25 +382,19 @@ class KernelNodeStoreBranch implements N
         }
 
         @Override
-        NodeState merge(CommitHook hook, CommitInfo info) throws CommitFailedException {
-            mergeLock.lock();
+        @Nonnull
+        NodeState merge(@Nonnull CommitHook hook, CommitInfo info)
+                throws CommitFailedException {
             try {
                 rebase();
-                store.beforeCommit(base);
+                dispatcher.beforeCommit(base);
                 NodeState toCommit = checkNotNull(hook).processCommit(base, head);
-                JsopDiff diff = new JsopDiff(blobs);
-                toCommit.compareAgainstBaseState(base, diff);
-                NodeState newHead = store.commit(diff.toString(), base);
-                store.localCommit(newHead, info);
+                NodeState newHead = AbstractNodeStoreBranch.this.persist(toCommit, base);
+                dispatcher.localCommit(newHead, info);
                 branchState = new Merged(base);
                 return newHead;
-            } catch (MicroKernelException e) {
-                throw new CommitFailedException(
-                        "Kernel", 1,
-                        "Failed to merge changes to the underlying MicroKernel", e);
             } finally {
-                store.afterCommit(store.getRoot());
-                mergeLock.unlock();
+                dispatcher.afterCommit(getRoot());
             }
         }
     }
@@ -337,26 +412,29 @@ class KernelNodeStoreBranch implements N
      */
     private class Persisted extends BranchState {
         /** Root state of the transient head, top of persisted branch. */
-        private KernelNodeState head;
+        private N head;
 
         @Override
         public String toString() {
             return "Persisted[" + base + ", " + head + ']';
         }
 
-        Persisted(KernelNodeState base, NodeState head) {
+        Persisted(N base, NodeState head) {
             super(base);
-            this.head = store.branch(base);
+            this.head = createBranch(base);
             persistTransientHead(head);
         }
 
-        void commit(String jsop) {
-            if (!jsop.isEmpty()) {
-                head = store.commit(jsop, head);
-            }
+        void move(String source, String target) {
+            head = AbstractNodeStoreBranch.this.move(source, target, head);
+        }
+
+        void copy(String source, String target) {
+            head = AbstractNodeStoreBranch.this.copy(source, target, head);
         }
 
         @Override
+        @Nonnull
         NodeState getHead() {
             return head;
         }
@@ -372,52 +450,43 @@ class KernelNodeStoreBranch implements N
 
         @Override
         void rebase() {
-            KernelNodeState root = store.getRoot();
+            N root = getRoot();
             if (head.equals(root)) {
                 // Nothing was written to this branch: set new base revision
                 head = root;
                 base = root;
             } else {
-                // perform rebase in kernel
-                head = store.rebase(head, root);
+                // perform rebase in store
+                head = AbstractNodeStoreBranch.this.rebase(head, root);
                 base = root;
             }
         }
 
         @Override
-        NodeState merge(CommitHook hook, CommitInfo info) throws CommitFailedException {
-            mergeLock.lock();
+        @Nonnull
+        NodeState merge(@Nonnull CommitHook hook, CommitInfo info) throws CommitFailedException {
             try {
                 rebase();
-                store.beforeCommit(base);
+                dispatcher.beforeCommit(base);
                 NodeState toCommit = checkNotNull(hook).processCommit(base, head);
                 if (toCommit.equals(base)) {
                     branchState = new Merged(base);
                     return base;
                 } else {
-                    JsopDiff diff = new JsopDiff(blobs);
-                    toCommit.compareAgainstBaseState(head, diff);
-                    commit(diff.toString());
-                    NodeState newRoot = store.merge(head);
-                    store.localCommit(newRoot, info);
+                    head = AbstractNodeStoreBranch.this.persist(toCommit, head);
+                    NodeState newRoot = AbstractNodeStoreBranch.this.merge(head);
+                    dispatcher.localCommit(newRoot, info);
                     branchState = new Merged(base);
                     return newRoot;
                 }
-            } catch (MicroKernelException e) {
-                throw new CommitFailedException(
-                        "Kernel", 1,
-                        "Failed to merge changes to the underlying MicroKernel", e);
             } finally {
-                store.afterCommit(store.getRoot());
-                mergeLock.unlock();
+                dispatcher.afterCommit(getRoot());
             }
         }
 
         private void persistTransientHead(NodeState newHead) {
             if (!newHead.equals(head)) {
-                JsopDiff diff = new JsopDiff(blobs);
-                newHead.compareAgainstBaseState(head, diff);
-                head = store.commit(diff.toString(), head);
+                head = AbstractNodeStoreBranch.this.persist(newHead, head);
             }
         }
     }
@@ -429,7 +498,7 @@ class KernelNodeStoreBranch implements N
      * Transitions to: none.
      */
     private class Merged extends BranchState {
-        protected Merged(KernelNodeState base) {
+        protected Merged(N base) {
             super(base);
         }
 
@@ -439,6 +508,7 @@ class KernelNodeStoreBranch implements N
         }
 
         @Override
+        @Nonnull
         NodeState getHead() {
             throw new IllegalStateException("Branch has already been merged");
         }
@@ -454,7 +524,8 @@ class KernelNodeStoreBranch implements N
         }
 
         @Override
-        NodeState merge(CommitHook hook, CommitInfo info) throws CommitFailedException {
+        @Nonnull
+        NodeState merge(@Nonnull CommitHook hook, CommitInfo info) {
             throw new IllegalStateException("Branch has already been merged");
         }
     }

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java
------------------------------------------------------------------------------
    svn:eol-style = native