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 ju...@apache.org on 2013/10/19 16:05:57 UTC

svn commit: r1533766 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-it/mk/src/test/resources/META-INF/services/

Author: jukka
Date: Sat Oct 19 14:05:57 2013
New Revision: 1533766

URL: http://svn.apache.org/r1533766
Log:
OAK-987: Implement the MicroKernel API

Fix the remaining test failures.

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
    jackrabbit/oak/trunk/oak-it/mk/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java?rev=1533766&r1=1533765&r2=1533766&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java Sat Oct 19 14:05:57 2013
@@ -39,10 +39,19 @@ public class JsopDiff implements NodeSta
             JsopBuilder jsop, BlobSerializer blobs, String path, int depth) {
         this.jsop = jsop;
         this.blobs = blobs;
-        this.path = path;
-        this.depth = depth;
-    }
 
+        if (path != null) {
+            this.path = path;
+        } else {
+            this.path = "/";
+        }
+
+        if (depth >= 0) {
+            this.depth = depth;
+        } else {
+            this.depth = Integer.MAX_VALUE;
+        }
+    }
 
     JsopDiff(BlobSerializer blobs, String path) {
         this(new JsopBuilder(), blobs, path, Integer.MAX_VALUE);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java?rev=1533766&r1=1533765&r2=1533766&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java Sat Oct 19 14:05:57 2013
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.kernel;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.newLinkedList;
 import static com.google.common.collect.Maps.newConcurrentMap;
 import static com.google.common.collect.Maps.newLinkedHashMap;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
@@ -24,12 +25,16 @@ import static org.apache.jackrabbit.oak.
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.zip.CheckedInputStream;
 import java.util.zip.Checksum;
 
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.json.JsopBuilder;
@@ -40,8 +45,12 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.AbstractBlob;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
-import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -56,12 +65,36 @@ import com.google.common.io.ByteStreams;
  */
 public class NodeStoreKernel implements MicroKernel {
 
+    private static final CommitHook CONFLICT_HOOK =
+            new EditorHook(new ValidatorProvider() {
+                @Override
+                protected Validator getRootValidator(
+                        NodeState before, NodeState after) {
+                    return new DefaultValidator() {
+                        @Override
+                        public Validator childNodeAdded(
+                                String name, NodeState after)
+                                throws CommitFailedException {
+                            if (name.equals(":conflict")) {
+                                throw new CommitFailedException(
+                                        CommitFailedException.STATE, 0, "Conflict");
+                            }
+                            return null;
+                        }
+                        @Override
+                        public Validator childNodeChanged(
+                                String name, NodeState before, NodeState after)
+                                throws CommitFailedException {
+                            return this;
+                        }
+                    };
+                }
+            });
+
     private final NodeStore store;
 
     private final Map<String, Revision> revisions = newLinkedHashMap();
 
-    private final Map<String, NodeBuilder> branches = newLinkedHashMap();
-
     private final Map<String, Blob> blobs = newConcurrentMap();
 
     private final BlobSerializer blobSerializer = new BlobSerializer() {
@@ -73,37 +106,39 @@ public class NodeStoreKernel implements 
         }
     };
 
-    private String head;
+    private Revision head;
 
     public NodeStoreKernel(NodeStore store) {
         this.store = store;
-        this.head = UUID.randomUUID().toString();
-        revisions.put(head, new Revision(store.getRoot()));
+        this.head = new Revision(store.getRoot());
+        revisions.put(head.id, head);
     }
 
-    private synchronized NodeState getRoot(String revision)
-            throws MicroKernelException {
-        if (revision == null) {
-            revision = head;
-        }
-
-        Revision r = revisions.get(revision);
-        if (r != null) {
-            return r.root;
-        }
-
-        NodeBuilder builder = branches.get(revision);
-        if (builder != null) {
-            return builder.getNodeState();
+    @Nonnull
+    private synchronized Revision getRevision(@CheckForNull String id) {
+        if (id == null) {
+            return head;
+        } else {
+            Revision revision = revisions.get(id);
+            if (revision != null) {
+                return revision;
+            } else {
+                throw new MicroKernelException("Revision not found: " + id);
+            }
         }
+    }
 
-        throw new MicroKernelException("Revision not found: " + revision);
+    private NodeState getRoot(String id) throws MicroKernelException {
+        return getRevision(id).root;
     }
 
-    private NodeState getNode(String revision, String path) {
+    private NodeState getNode(String revision, String path)
+            throws MicroKernelException {
         NodeState node = getRoot(revision);
-        for (String element : PathUtils.elements(path)) {
-            node = node.getChildNode(element);
+        if (path != null) {
+            for (String element : PathUtils.elements(path)) {
+                node = node.getChildNode(element);
+            }
         }
         return node;
     }
@@ -170,10 +205,15 @@ public class NodeStoreKernel implements 
                 break;
             case '>':
                 tokenizer.read(':');
-                String moveTarget = tokenizer.readString();
-                if (!getNode(builder, path).moveTo(
-                        getNode(builder, getParentPath(moveTarget)),
-                        getName(moveTarget))) {
+                String targetPath = tokenizer.readString();
+                NodeBuilder targetParent =
+                        getNode(builder, getParentPath(targetPath));
+                String targetName = getName(targetPath);
+                if (targetParent.hasChildNode(targetName)) {
+                    throw new MicroKernelException(
+                            "Target node exists: " + targetPath);
+                } else if (!getNode(builder, path).moveTo(
+                        targetParent, targetName)) {
                     throw new MicroKernelException("Move failed");
                 }
                 break;
@@ -256,12 +296,12 @@ public class NodeStoreKernel implements 
     @Override
     public synchronized String getHeadRevision() {
         NodeState root = store.getRoot();
-        if (!root.equals(revisions.get(head).root)) {
-            head = UUID.randomUUID().toString();
-            revisions.put(head, new Revision(root, "external"));
+        if (!root.equals(head.root)) {
+            head = new Revision(head, root, "external");
+            revisions.put(head.id, head);
             notifyAll();
         }
-        return head;
+        return head.id;
     }
 
     @Override
@@ -270,98 +310,85 @@ public class NodeStoreKernel implements 
     }
 
     @Override
-    public synchronized String getRevisionHistory(
+    public String getRevisionHistory(
             long since, int maxEntries, String path)
             throws MicroKernelException {
         if (maxEntries < 0) {
             maxEntries = Integer.MAX_VALUE;
         }
 
+        LinkedList<Revision> list = newLinkedList();
+        Revision revision = getRevision(null);
+        while (revision != null && revision.base != null
+                && revision.timestamp >= since) {
+            list.addFirst(revision);
+            revision = revision.base;
+        }
+
         JsopBuilder json = new JsopBuilder();
         json.array();
         int count = 0;
-        NodeState before = null;
-        for (Map.Entry<String, Revision> entry : revisions.entrySet()) {
-            Revision rev = entry.getValue();
-            if (before != null && rev.timestamp >= since
-                    && !getPathChanges(before, rev.root, path).isEmpty()) {
+        for (Revision rev : list) {
+            if (!rev.hasPathChanged(path)) {
                 if (count++ > maxEntries) {
                     break;
                 }
                 json.object();
-                json.key("id").value(entry.getKey());
+                json.key("id").value(rev.id);
                 json.key("ts").value(rev.timestamp);
                 json.key("msg").value(rev.message);
                 json.endObject();
             }
-            before = rev.root;
         }
         json.endArray();
         return json.toString();
     }
 
-    private String getPathChanges(
-            NodeState before, NodeState after, String path) {
-        if (path != null) {
-            for (String element : PathUtils.elements(path)) {
-                before = before.getChildNode(element);
-                after = after.getChildNode(element);
-            }
-        } else {
-            path = "/";
-        }
-
-        JsopDiff diff = new JsopDiff(blobSerializer, path);
-        after.compareAgainstBaseState(before, diff);
-        return diff.toString();
-    }
-
     @Override
     public synchronized String waitForCommit(
             String oldHeadRevisionId, long timeout)
             throws MicroKernelException, InterruptedException {
         long stop = System.currentTimeMillis() + timeout;
-        while (head.equals(oldHeadRevisionId) && timeout > 0) {
+        while (head.id.equals(oldHeadRevisionId) && timeout > 0) {
             wait(timeout);
             timeout = stop - System.currentTimeMillis();
         }
-        return head;
+        return head.id;
     }
 
     @Override
-    public synchronized String getJournal(
+    public String getJournal(
             String fromRevisionId, String toRevisionId, String path)
             throws MicroKernelException {
-        if (!revisions.containsKey(fromRevisionId)) {
-            throw new  MicroKernelException(
-                    "Revision not found: " + fromRevisionId);
+        LinkedList<Revision> list = newLinkedList();
+        Revision revision = getRevision(toRevisionId);
+        while (revision != null) {
+            list.addFirst(revision);
+            if (revision.id.equals(fromRevisionId)) {
+                break;
+            } else if (revision.base == null) {
+                if (getRevision(fromRevisionId).branch != null) {
+                    throw new MicroKernelException();
+                }
+                list.clear();
+                break;
+            }
+            revision = revision.base;
         }
 
         JsopBuilder json = new JsopBuilder();
         json.array();
-        NodeState before = null;
-        boolean active = false;
-        for (Map.Entry<String, Revision> entry : revisions.entrySet()) {
-            String id = entry.getKey();
-            Revision rev = entry.getValue();
-            active = active || id.equals(fromRevisionId);
-            if (before != null && active) {
-                String jsop = getPathChanges(before, rev.root, path);
-                if (!jsop.isEmpty()) {
-                    json.object();
-                    json.key("id").value(id);
-                    json.key("ts").value(rev.timestamp);
-                    json.key("msg").value(rev.message);
-                    json.key("changes").value(jsop);
-                    json.endObject();
-                }
-            }
-            if (id.equals(toRevisionId)) {
-                break;
+        for (Revision rev : list) {
+            String jsop = rev.getPathChanges(path, blobSerializer);
+            if (!jsop.isEmpty()) {
+                json.object();
+                json.key("id").value(rev.id);
+                json.key("ts").value(rev.timestamp);
+                json.key("msg").value(rev.message);
+                json.key("changes").value(jsop);
+                json.endObject();
             }
-            before = rev.root;
         }
-
         json.endArray();
         return json.toString();
     }
@@ -370,21 +397,8 @@ public class NodeStoreKernel implements 
     public String diff(
             String fromRevisionId, String toRevisionId, String path, int depth)
             throws MicroKernelException {
-        NodeState before = getRoot(fromRevisionId);
-        NodeState after = getRoot(toRevisionId);
-
-        if (path != null) {
-            for (String element : PathUtils.elements(path)) {
-                before = before.getChildNode(element);
-                after = after.getChildNode(element);
-            }
-        } else {
-            path = "/";
-        }
-
-        if (depth < 0) {
-            depth = Integer.MAX_VALUE;
-        }
+        NodeState before = getNode(fromRevisionId, path);
+        NodeState after = getNode(toRevisionId, path);
 
         JsopDiff diff = new JsopDiff(path, depth);
         after.compareAgainstBaseState(before, diff);
@@ -414,11 +428,7 @@ public class NodeStoreKernel implements 
             String path, String revisionId, int depth,
             long offset, int maxChildNodes, String filter)
             throws MicroKernelException {
-        NodeState node = getRoot(revisionId);
-        for (String element : PathUtils.elements(path)) {
-            node = node.getChildNode(element);
-        }
-
+        NodeState node = getNode(revisionId, path);
         if (node.exists()) {
             if (maxChildNodes < 0) {
                 maxChildNodes = Integer.MAX_VALUE;
@@ -439,76 +449,67 @@ public class NodeStoreKernel implements 
     public synchronized String commit(
             String path, String jsonDiff, String revisionId, String message)
             throws MicroKernelException {
-        if (revisionId == null) {
-            revisionId = head;
+        Revision revision = getRevision(revisionId);
+
+        NodeBuilder builder = revision.branch;
+        if (builder == null) {
+            builder = revision.root.builder();
         }
 
-        Revision r = revisions.get(revisionId);
-        if (r != null) {
+        applyJsop(builder, path, jsonDiff);
+
+        if (revision.branch != null) {
+            revision = new Revision(revision, builder.getNodeState(), message);
+        } else {
             try {
-                NodeBuilder builder = r.root.builder();
-                applyJsop(builder, path, jsonDiff);
                 NodeState newRoot = store.merge(
-                        builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-
-                Revision old = revisions.get(head);
-                if (!newRoot.equals(old.root)) {
-                    String uuid = UUID.randomUUID().toString();
-                    revisions.put(uuid, new Revision(newRoot, message));
-                    head = uuid;
+                        builder, CONFLICT_HOOK, CommitInfo.EMPTY);
+                if (!newRoot.equals(head.root)) {
+                    revision = new Revision(head, newRoot, message);
+                    head = revision;
                     notifyAll();
+                } else {
+                    return head.id;
                 }
-
-                return head;
             } catch (CommitFailedException e) {
                 throw new MicroKernelException(e);
             }
         }
 
-        NodeBuilder builder = branches.remove(revisionId);
-        if (builder != null) {
-            applyJsop(builder, path, jsonDiff);
-            String uuid = UUID.randomUUID().toString();
-            branches.put(uuid, builder);
-            return uuid;
-        }
-
-        throw new MicroKernelException("Revision not found: " + revisionId);
+        revisions.put(revision.id, revision);
+        return revision.id;
     }
 
     @Override
     public synchronized String branch(String trunkRevisionId)
             throws MicroKernelException {
-        String uuid = UUID.randomUUID().toString();
-        branches.put(uuid, getRoot(trunkRevisionId).builder());
-        return uuid;
+        Revision branch = new Revision(getRevision(trunkRevisionId));
+        revisions.put(branch.id, branch);
+        return branch.id;
     }
 
     @Override
     public synchronized String merge(String branchRevisionId, String message)
             throws MicroKernelException {
-        NodeBuilder builder = branches.remove(branchRevisionId);
-        if (builder != null) {
-            try {
-                NodeState newRoot = store.merge(
-                        builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-
-                Revision old = revisions.get(head);
-                if (!newRoot.equals(old.root)) {
-                    String uuid = UUID.randomUUID().toString();
-                    revisions.put(uuid, new Revision(newRoot, message));
-                    head = uuid;
-                    notifyAll();
-                }
-
-                return head;
-            } catch (CommitFailedException e) {
-                throw new MicroKernelException(e);
-            }
-        } else {
+        Revision revision = getRevision(branchRevisionId);
+        if (revision.branch == null) {
             throw new MicroKernelException(
                     "Branch not found: " + branchRevisionId);
         }
+
+        try {
+            NodeState newRoot = store.merge(
+                    revision.branch, CONFLICT_HOOK, CommitInfo.EMPTY);
+            if (!newRoot.equals(head.root)) {
+                head = new Revision(head, newRoot, message);
+                revisions.put(head.id, head);
+                notifyAll();
+            }
+        } catch (CommitFailedException e) {
+            throw new MicroKernelException(e);
+        }
+
+        return head.id;
     }
 
     @Override
@@ -598,24 +599,65 @@ public class NodeStoreKernel implements 
 
     private static class Revision {
 
+        private final Revision base;
+
+        private final NodeBuilder branch;
+
+        private final String id = UUID.randomUUID().toString();
+
         private final NodeState root;
 
         private final String message;
 
         private final long timestamp;
 
-        Revision(NodeState root, String message) {
+        Revision(NodeState root) {
+            this.base = null;
+            this.branch = null;
             this.root = root;
-            this.message = message;
+            this.message = "start";
+            this.timestamp = 0;
+        }
+
+        Revision(Revision base) {
+            this.base = base;
+            this.branch = base.root.builder();
+            this.root = base.root;
+            this.message = "branch";
             this.timestamp = System.currentTimeMillis();
         }
 
-        Revision(NodeState root) {
+        public Revision(Revision base, NodeState root, String message) {
+            this.base = base;
+            this.branch = base.branch;
             this.root = root;
-            this.message = "origin";
-            this.timestamp = -1;
+            this.message = message;
+            this.timestamp = System.currentTimeMillis();
         }
 
+        boolean hasPathChanged(String path) {
+            return base != null
+                    && getNode(root, path).equals(getNode(base.root, path));
+        }
+
+        String getPathChanges(String path, BlobSerializer blobs) {
+            JsopDiff diff = new JsopDiff(blobs, path);
+            if (base != null) {
+                getNode(root, path).compareAgainstBaseState(
+                        getNode(base.root, path), diff);
+            }
+            return diff.toString();
+        }
+
+    }
+
+    private static NodeState getNode(NodeState node, String path) {
+        if (path != null) {
+            for (String element : PathUtils.elements(path)) {
+                node = node.getChildNode(element);
+            }
+        }
+        return node;
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-it/mk/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/mk/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture?rev=1533766&r1=1533765&r2=1533766&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-it/mk/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture (original)
+++ jackrabbit/oak/trunk/oak-it/mk/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture Sat Oct 19 14:05:57 2013
@@ -17,4 +17,4 @@ org.apache.jackrabbit.mk.test.MicroKerne
 org.apache.jackrabbit.mk.test.ClientServerFixture
 #org.apache.jackrabbit.mk.test.MongoMicroKernelFixture
 #org.apache.jackrabbit.mk.test.MongoGridFSMicroKernelFixture
-#org.apache.jackrabbit.mk.test.SegmentMicroKernelFixture
+org.apache.jackrabbit.mk.test.SegmentMicroKernelFixture