Index: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (date 1377246655000) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (date 1376925502000) @@ -146,7 +146,7 @@ @Override public NodeStoreBranch branch() { - return new KernelNodeStoreBranch(this, getRoot(), mergeLock); + return new KernelNodeStoreBranch(this, mergeLock, getRoot()); } /** @@ -184,12 +184,7 @@ //-----------------------------------------------------------< internal >--- - @Nonnull - MicroKernel getKernel() { - return kernel; - } - - KernelNodeState getRootState(String revision) { + private KernelNodeState getRootState(String revision) { try { return cache.get(revision + "/"); } catch (ExecutionException e) { @@ -197,12 +192,20 @@ } } - NodeState commit(String jsop, String baseRevision) { - return getRootState(kernel.commit("", jsop, baseRevision, null)); + KernelNodeState commit(String jsop, KernelNodeState base) { + return getRootState(kernel.commit("", jsop, base.getRevision(), null)); } - NodeState merge(String headRevision) { - return getRootState(kernel.merge(headRevision, null)); + KernelNodeState branch(KernelNodeState base) { + return getRootState(kernel.branch(base.getRevision())); + } + + KernelNodeState rebase(KernelNodeState branchHead, KernelNodeState base) { + return getRootState(kernel.rebase(branchHead.getRevision(), base.getRevision())); + } + + NodeState merge(KernelNodeState branchHead) { + return getRootState(kernel.merge(branchHead.getRevision(), null)); } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (date 1377246655000) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (date 1376925502000) @@ -16,15 +16,15 @@ */ package org.apache.jackrabbit.oak.kernel; -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 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.util.concurrent.locks.Lock; +import javax.annotation.Nonnull; + import org.apache.jackrabbit.mk.api.MicroKernel; import org.apache.jackrabbit.mk.api.MicroKernelException; import org.apache.jackrabbit.oak.api.CommitFailedException; @@ -39,73 +39,56 @@ /** * {@code NodeStoreBranch} based on {@link MicroKernel} branching and merging. * This implementation keeps changes in memory up to a certain limit and writes - * them back when the to the Microkernel branch when the limit is exceeded. + * them back to the Microkernel branch when the limit is exceeded. */ class KernelNodeStoreBranch extends AbstractNodeStoreBranch { /** The underlying store to which this branch belongs */ private final KernelNodeStore store; + /** Lock for coordinating concurrent merge operations */ + private final Lock mergeLock; + /** - * Lock for coordinating concurrent merge operations + * State of the this branch. Either {@link Unmodified}, {@link InMemory}, {@link Persisted} + * or {@link Merged}. + * @see BranchState */ - private final Lock mergeLock; + private BranchState branchState; - /** Root state of the base revision of this branch */ - private KernelNodeState base; + public KernelNodeStoreBranch(KernelNodeStore kernelNodeStore, Lock mergeLock, + KernelNodeState base) { - /** Root state of the transient head revision on top of persisted branch, null if merged. */ - private NodeState head; + this.store = checkNotNull(kernelNodeStore); + this.mergeLock = checkNotNull(mergeLock); + branchState = new Unmodified(checkNotNull(base)); + } - /** Head revision of persisted branch, null if not yet branched*/ - private String headRevision; - - /** Number of updates to this branch via {@link #setRoot(NodeState)} */ - private int updates = 0; - - KernelNodeStoreBranch(KernelNodeStore store, KernelNodeState root, Lock mergeLock) { - this.store = store; - this.base = root; - this.head = root; - this.mergeLock = mergeLock; + @Override + public String toString() { + return branchState.toString(); } + @Nonnull @Override public NodeState getBase() { - return base; + return branchState.getBase(); } + @Nonnull @Override public NodeState getHead() { - checkNotMerged(); - return head; + return branchState.getHead(); } @Override public void setRoot(NodeState newRoot) { - checkNotMerged(); - if (!head.equals(newRoot)) { - NodeState oldHead = head; - head = newRoot; - if (++updates > 1) { - // persist unless this is the first update - boolean success = false; - try { - persistTransientHead(); - success = true; - } finally { - if (!success) { - head = oldHead; + branchState.setRoot(checkNotNull(newRoot)); - } + } - } - } - } - } @Override public boolean move(String source, String target) { - checkNotMerged(); - if (PathUtils.isAncestor(source, target)) { + if (PathUtils.isAncestor(checkNotNull(source), checkNotNull(target))) { return false; } else if (source.equals(target)) { return true; @@ -124,19 +107,17 @@ // destination exists already return false; } - - commit(">\"" + source + "\":\"" + target + '"'); + branchState.persist().commit(">\"" + source + "\":\"" + target + '"'); return true; } @Override public boolean copy(String source, String target) { - checkNotMerged(); - if (!getNode(source).exists()) { + if (!getNode(checkNotNull(source)).exists()) { // source does not exist return false; } - NodeState destParent = getNode(getParentPath(target)); + NodeState destParent = getNode(getParentPath(checkNotNull(target))); if (!destParent.exists()) { // parent of destination does not exist return false; @@ -145,149 +126,312 @@ // destination exists already return false; } - - commit("*\"" + source + "\":\"" + target + '"'); + branchState.persist().commit("*\"" + source + "\":\"" + target + '"'); return true; } + @Nonnull @Override - public NodeState merge(CommitHook hook, PostCommitHook committed) throws CommitFailedException { - checkNotMerged(); - NodeState oldRoot = head; - mergeLock.lock(); - try { - rebase(); - NodeState toCommit = checkNotNull(hook).processCommit(base, head); - head = toCommit; - if (head.equals(base)) { - // Nothing was written to this branch: return base state - head = null; // Mark as merged + public NodeState merge(@Nonnull CommitHook hook, PostCommitHook committed) throws CommitFailedException { + return branchState.merge(checkNotNull(hook), checkNotNull(committed)); + } + + @Override + public void rebase() { + branchState.rebase(); + } + + private NodeState getNode(String path) { + NodeState node = getHead(); + for (String name : elements(path)) { + node = node.getChildNode(name); + } + return node; + } + + /** + * Sub classes of this class represent a state a branch can be in. See the individual + * sub classes for permissible state transitions. + */ + private abstract class BranchState { + /** Root state of the base revision of this branch */ + protected KernelNodeState base; + + protected BranchState(KernelNodeState base) { + this.base = base; + } + + /** + * Persist this branch to an underlying branch in the {@code MicroKernel}. + */ + Persisted persist() { + branchState = new Persisted(base, getHead()); + return (Persisted) branchState; + } + + KernelNodeState getBase(){ + return base; + } + + @Nonnull + abstract NodeState getHead(); + + abstract void setRoot(NodeState root); + + abstract void rebase(); + + @Nonnull + abstract NodeState merge(@Nonnull CommitHook hook, PostCommitHook committed) throws CommitFailedException; + } + + /** + * Instances of this class represent a branch whose base and head are the same. + *
+ * Transitions to: + *
+ * Transitions to: + *
+ * Transitions to: + *
+ * Transitions to: none. + */ + private class Merged extends BranchState { + protected Merged(KernelNodeState base) { + super(base); } - // persist transient changes first - persistTransientHead(); + @Override + public String toString() { + return "Merged[" + base + ']'; + } - headRevision = kernel.commit("", jsop, headRevision, null); - head = store.getRootState(headRevision); + @Override + NodeState getHead() { + throw new IllegalStateException("Branch has already been merged"); - } + } - private void persistTransientHead() { - NodeState oldHead = head; - String oldHeadRevision = headRevision; - boolean success = false; - try { - MicroKernel kernel = store.getKernel(); - JsopDiff diff = new JsopDiff(store); - if (headRevision == null) { - // no persistent branch yet - if (head.equals(base)) { - // nothing to persist - success = true; - return; - } else { - // create branch - headRevision = kernel.branch(base.getRevision()); - head.compareAgainstBaseState(base, diff); + @Override + void setRoot(NodeState root) { + throw new IllegalStateException("Branch has already been merged"); - } + } - } else { - // compare against head of branch - NodeState branchHead = store.getRootState(headRevision); - if (head.equals(branchHead)) { - // nothing to persist - success = true; - return; - } else { - head.compareAgainstBaseState(branchHead, diff); + + @Override + void rebase() { + throw new IllegalStateException("Branch has already been merged"); - } + } - } - // if we get here we have something to persist - // and a branch exists - headRevision = kernel.commit("", diff.toString(), headRevision, null); - head = store.getRootState(headRevision); - success = true; - } finally { - // revert to old state if unsuccessful - if (!success) { - head = oldHead; - headRevision = oldHeadRevision; - } + + @Override + NodeState merge(CommitHook hook, PostCommitHook committed) throws CommitFailedException { + throw new IllegalStateException("Branch has already been merged"); } } }