Index: src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (revision 1536617) +++ src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (working copy) @@ -248,8 +248,8 @@ } } - 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 @@ } 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 @@ 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); - } - } Index: src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (revision 1536363) +++ src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (working copy) @@ -1,462 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.jackrabbit.oak.kernel; - -import static com.google.common.base.Preconditions.checkNotNull; -import 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.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. - * This implementation keeps changes in memory up to a certain limit and writes - * them back to the Microkernel branch when the limit is exceeded. - */ -class KernelNodeStoreBranch implements NodeStoreBranch { - - /** The underlying store to which this branch belongs */ - private final KernelNodeStore store; - - /** Lock for coordinating concurrent merge operations */ - private final Lock mergeLock; - - /** - * State of the this branch. Either {@link Unmodified}, {@link InMemory}, {@link Persisted} - * or {@link Merged}. - * @see BranchState - */ - 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) { - - this.store = checkNotNull(kernelNodeStore); - this.mergeLock = checkNotNull(mergeLock); - branchState = new Unmodified(checkNotNull(base)); - } - - @Override - public String toString() { - return branchState.toString(); - } - - @Nonnull - @Override - public NodeState getBase() { - return branchState.getBase(); - } - - @Nonnull - @Override - public NodeState getHead() { - return branchState.getHead(); - } - - @Override - public void setRoot(NodeState newRoot) { - branchState.setRoot(checkNotNull(newRoot)); - } - - @Override - public boolean move(String source, String target) { - if (PathUtils.isAncestor(checkNotNull(source), checkNotNull(target))) { - return false; - } else if (source.equals(target)) { - return true; - } - - if (!getNode(source).exists()) { - // source does not exist - return false; - } - NodeState destParent = getNode(getParentPath(target)); - if (!destParent.exists()) { - // parent of destination does not exist - return false; - } - if (destParent.getChildNode(getName(target)).exists()) { - // destination exists already - return false; - } - branchState.persist().commit(">\"" + source + "\":\"" + target + '"'); - return true; - } - - @Override - public boolean copy(String source, String target) { - if (!getNode(checkNotNull(source)).exists()) { - // source does not exist - return false; - } - NodeState destParent = getNode(getParentPath(checkNotNull(target))); - if (!destParent.exists()) { - // parent of destination does not exist - return false; - } - if (destParent.getChildNode(getName(target)).exists()) { - // destination exists already - return false; - } - branchState.persist().commit("*\"" + source + "\":\"" + target + '"'); - return true; - } - - @Nonnull - @Override - public NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info) - throws CommitFailedException { - return branchState.merge(checkNotNull(hook), info); - } - - @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, @Nullable CommitInfo info) - 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);
- }
-
- @Override
- public String toString() {
- return "Merged[" + base + ']';
- }
-
- @Override
- NodeState getHead() {
- throw new IllegalStateException("Branch has already been merged");
- }
-
- @Override
- void setRoot(NodeState root) {
- throw new IllegalStateException("Branch has already been merged");
- }
-
- @Override
- void rebase() {
- throw new IllegalStateException("Branch has already been merged");
- }
-
- @Override
- NodeState merge(CommitHook hook, CommitInfo info) throws CommitFailedException {
- throw new IllegalStateException("Branch has already been merged");
- }
- }
-
-}
Index: src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (revision 1536363)
@@ -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
* 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
+ * 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
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 @@
// destination exists already
return false;
}
- branchState.persist().commit(">\"" + source + "\":\"" + target + '"');
+ branchState.persist().move(source, target);
return true;
}
@@ -146,7 +206,7 @@
// destination exists already
return false;
}
- branchState.persist().commit("*\"" + source + "\":\"" + target + '"');
+ branchState.persist().copy(source, target);
return true;
}
@@ -162,6 +222,8 @@
branchState.rebase();
}
+ //----------------------------< internal >----------------------------------
+
private NodeState getNode(String path) {
NodeState node = getHead();
for (String name : elements(path)) {
@@ -176,9 +238,9 @@
*/
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 @@
* 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 @@
abstract void rebase();
+ /**
+ * Runs the commit hook on the changes tracked with this branch state
+ * merges the result.
+ *
+ * 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 @@
*
*/
private class Unmodified extends BranchState {
- Unmodified(KernelNodeState base) {
+ Unmodified(N base) {
super(base);
}
@@ -227,6 +305,7 @@
}
@Override
+ @Nonnull
NodeState getHead() {
return base;
}
@@ -240,11 +319,12 @@
@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 @@
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 @@
@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 @@
}
@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 @@
*/
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 @@
@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 @@
* Transitions to: none.
*/
private class Merged extends BranchState {
- protected Merged(KernelNodeState base) {
+ protected Merged(N base) {
super(base);
}
@@ -439,6 +508,7 @@
}
@Override
+ @Nonnull
NodeState getHead() {
throw new IllegalStateException("Branch has already been merged");
}
@@ -454,7 +524,8 @@
}
@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");
}
}
Property changes on: src\main\java\org\apache\jackrabbit\oak\spi\state\AbstractNodeStoreBranch.java
___________________________________________________________________
Added: svn:eol-style
+ native
+ 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}
* or {@link Merged}.
* @see BranchState
*/
- private BranchState branchState;
+ protected 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 AbstractNodeStoreBranch(S kernelNodeStore,
+ ChangeDispatcher dispatcher,
+ N base) {
- public KernelNodeStoreBranch(KernelNodeStore kernelNodeStore, Lock mergeLock,
- KernelNodeState 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 toPersist and base
+ * to the underlying store.
+ * base.
+ * @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.
+ *