jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1446225 - in /jackrabbit/oak/trunk/oak-mongomk/src: main/java/org/apache/jackrabbit/mongomk/prototype/ test/java/org/apache/jackrabbit/mongomk/prototype/
Date Thu, 14 Feb 2013 15:06:47 GMT
Author: thomasm
Date: Thu Feb 14 15:06:46 2013
New Revision: 1446225

URL: http://svn.apache.org/r1446225
Log:
OAK-619 Lock-free MongoMK implementation

Added:
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java
Modified:
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java?rev=1446225&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
(added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
Thu Feb 14 15:06:46 2013
@@ -0,0 +1,101 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.mongomk.prototype.DocumentStore.Collection;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+/**
+ * A higher level object representing a commit.
+ */
+public class Commit {
+    
+    private final Revision revision;
+    private HashMap<String, UpdateOp> operations = new HashMap<String, UpdateOp>();
+    
+    Commit(Revision revision) {
+        this.revision = revision;
+    }
+
+    UpdateOp getUpdateOperationForNode(String path) {
+        UpdateOp op = operations.get(path);
+        if (op == null) {
+            String id = Node.convertPathToDocumentId(path);
+            op = new UpdateOp(id, false);
+            operations.put(path, op);
+        }
+        return op;
+    }
+
+    public Revision getRevision() {
+        return revision;
+    }
+
+    void addNode(Node n) {
+        if (operations.containsKey(n.path)) {
+            throw new MicroKernelException("Node already added: " + n.path);
+        }
+        operations.put(n.path, n.asOperation(true));
+    }
+
+    void apply(DocumentStore store) {
+        String commitRoot = null;
+        ArrayList<UpdateOp> newNodes = new ArrayList<UpdateOp>();
+        ArrayList<UpdateOp> changedNodes = new ArrayList<UpdateOp>();
+        for (String p : operations.keySet()) {
+            if (commitRoot == null) {
+                commitRoot = p;
+            } else {
+                while (!PathUtils.isAncestor(commitRoot, p)) {
+                    commitRoot = PathUtils.getParentPath(commitRoot);
+                    if (PathUtils.denotesRoot(commitRoot)) {
+                        break;
+                    }
+                }
+            }
+        }
+        // create a "root of the commit" if there is none
+        UpdateOp root = getUpdateOperationForNode(commitRoot);
+        for (String p : operations.keySet()) {
+            UpdateOp op = operations.get(p);
+            if (op == root) {
+                // apply at the end
+            } else if (op.isNew()) {
+                newNodes.add(op);
+            } else {
+                changedNodes.add(op);
+            }
+        }
+        if (changedNodes.size() == 0) {
+            // no updates, so we just add the root like the others
+            newNodes.add(root);
+            root = null;
+        }
+        store.create(Collection.NODES, newNodes);
+        for (UpdateOp op : changedNodes) {
+            store.createOrUpdate(Collection.NODES, op);
+        }
+        if (root != null) {
+            store.createOrUpdate(Collection.NODES, root);
+        }
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
Thu Feb 14 15:06:46 2013
@@ -169,7 +169,9 @@ public class MemoryDocumentStore impleme
 
     @Override
     public void create(Collection collection, List<UpdateOp> updateOps) {
-        // TODO Auto-generated method stub
+        for (UpdateOp op : updateOps) {
+            createOrUpdate(collection, op);
+        }
     }
 
     public String toString() {

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
Thu Feb 14 15:06:46 2013
@@ -27,7 +27,14 @@ import org.apache.jackrabbit.mk.api.Micr
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.blobs.BlobStore;
 import org.apache.jackrabbit.mk.blobs.MemoryBlobStore;
-
+import org.apache.jackrabbit.mk.json.JsopReader;
+import org.apache.jackrabbit.mk.json.JsopStream;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+/**
+ * A MicroKernel implementation that stores the data in a MongoDB.
+ */
 public class MongoMK implements MicroKernel {
 
     /**
@@ -63,6 +70,10 @@ public class MongoMK implements MicroKer
      */
     private static final int trustedRevisionAge = Integer.MAX_VALUE;
 
+    /**
+     * The delay for asynchronous operations (delayed commit propagation and
+     * cache update).
+     */
     protected static final long ASYNC_DELAY = 1000;
 
     /**
@@ -76,7 +87,7 @@ public class MongoMK implements MicroKer
      */
     private String headRevision;
     
-    AtomicBoolean disposed = new AtomicBoolean();
+    AtomicBoolean isDisposed = new AtomicBoolean();
     
     private Thread backgroundThread;
 
@@ -100,7 +111,7 @@ public class MongoMK implements MicroKer
         this.clusterId = clusterId;
         backgroundThread = new Thread(new Runnable() {
             public void run() {
-                while (!disposed.get()) {
+                while (!isDisposed.get()) {
                     synchronized (this) {
                         try {
                             wait(ASYNC_DELAY);
@@ -116,16 +127,16 @@ public class MongoMK implements MicroKer
         backgroundThread.start();
     }
     
+    Revision newRevision() {
+        return Revision.newRevision(clusterId);
+    }
+    
     void runBackgroundOperations() {
         // to be implemented
     }
-
-    String getNewRevision() {
-        return Utils.createRevision(clusterId);
-    }
     
     public void dispose() {
-        if (!disposed.getAndSet(true)) {
+        if (!isDisposed.getAndSet(true)) {
             synchronized (backgroundThread) {
                 backgroundThread.notifyAll();
             }
@@ -146,7 +157,7 @@ public class MongoMK implements MicroKer
      * @param rev
      * @return the node
      */
-    Node getNode(String path, String rev) {
+    Node getNode(String path, Revision rev) {
         String key = path + "@" + rev;
         Node node = nodeCache.get(key);
         if (node == null) {
@@ -158,47 +169,37 @@ public class MongoMK implements MicroKer
         return node;
     }
     
-    private static String getDocumentId(String path) {
-        int depth = Utils.pathDepth(path);
-        return depth + ":" + path;
+    private boolean includeRevision(Revision x, Revision requestRevision) {
+        if (x.getClusterId() == this.clusterId && 
+                requestRevision.getClusterId() == this.clusterId) {
+            // both revisions were created by this cluster node: 
+            // compare timestamps only
+            return x.compareRevisionTime(requestRevision) >= 0;
+        }
+        // TODO currently we only compare the timestamps
+        return x.compareRevisionTime(requestRevision) >= 0;
     }
 
-    /**
-     * Try to add a node.
-     *
-     * @param rev the revision
-     * @param n the node
-     * @throw IllegalStateException if the node already existed
-     */
-    public void addNode(String rev, String commitRoot, Node n) {
-        UpdateOp node = new UpdateOp(n.path);
-        int depth = Utils.pathDepth(n.path);
-        node.set("_id", getDocumentId(n.path));
-        node.set("_pathDepth", depth);
-        int commitDepth = depth - Utils.pathDepth(commitRoot);
-        node.addMapEntry("_commitDepth", rev, commitDepth);
-
-        node.increment("_changeCount", 1L);
-
-        for (String p : n.properties.keySet()) {
-            node.addMapEntry(p, rev, n.properties.get(p));
-        }
-        Map<String, Object> map = store.createOrUpdate(DocumentStore.Collection.NODES,
node);
-        if (map != null) {
-            // TODO rollback changes
-            throw new IllegalStateException("Node already exists: " + n.path);
-        }
-    }
-
-    private Node readNode(String path, String rev) {
-        Map<String, Object> map = store.find(DocumentStore.Collection.NODES, path);
+    private Node readNode(String path, Revision rev) {
+        String id = Node.convertPathToDocumentId(path);
+        Map<String, Object> map = store.find(DocumentStore.Collection.NODES, id);
         if (map == null) {
             return null;
         }
         Node n = new Node(path, rev);
         for(String key : map.keySet()) {
-            Object v = map.get(key);
-
+            if (key.startsWith("_")) {
+                // TODO property name escaping
+                continue;
+            }
+            @SuppressWarnings("unchecked")
+            Map<String, String> valueMap = (Map<String, String>) map.get(key);
+            for (String r : valueMap.keySet()) {
+                Revision propRev = Revision.fromString(r);
+                if (includeRevision(propRev, rev)) {
+                    n.setProperty(key, valueMap.get(r));
+                }
+            }
         }
         return n;
     }
@@ -211,42 +212,43 @@ public class MongoMK implements MicroKer
     @Override
     public String getRevisionHistory(long since, int maxEntries, String path)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
     @Override
     public String waitForCommit(String oldHeadRevisionId, long timeout)
             throws MicroKernelException, InterruptedException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
     @Override
     public String getJournal(String fromRevisionId, String toRevisionId,
             String path) throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
     @Override
     public String diff(String fromRevisionId, String toRevisionId, String path,
             int depth) throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
     @Override
     public boolean nodeExists(String path, String revisionId)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
-        return false;
+        Revision rev = Revision.fromString(revisionId);
+        Node n = getNode(path, rev);
+        return n != null;
     }
 
     @Override
     public long getChildNodeCount(String path, String revisionId)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return 0;
     }
 
@@ -254,27 +256,152 @@ public class MongoMK implements MicroKer
     public String getNodes(String path, String revisionId, int depth,
             long offset, int maxChildNodes, String filter)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
-        return null;
+        Revision rev = Revision.fromString(revisionId);
+        Node n = getNode(path, rev);
+        JsopStream json = new JsopStream();
+        n.append(json);
+        return json.toString();
     }
 
     @Override
-    public String commit(String path, String jsonDiff, String revisionId,
+    public String commit(String rootPath, String json, String revisionId,
             String message) throws MicroKernelException {
-        // TODO Auto-generated method stub
-        return null;
+        JsopReader t = new JsopTokenizer(json);
+        revisionId = revisionId == null ? headRevision : revisionId;
+        Revision rev = Revision.newRevision(clusterId);
+        Commit commit = new Commit(rev);
+        while (true) {
+            int r = t.read();
+            if (r == JsopReader.END) {
+                break;
+            }
+            String path = PathUtils.concat(rootPath, t.readString());
+            switch (r) {
+            case '+':
+                t.read(':');
+                t.read('{');
+                parseAddNode(commit, t, path);
+                break;
+            case '-':
+                // TODO support remove operations
+                break;
+            case '^':
+                t.read(':');
+                String value;
+                if (t.matches(JsopReader.NULL)) {
+                    value = null;
+                } else {
+                    value = t.readRawValue().trim();
+                }
+                String p = PathUtils.getParentPath(path);
+                String propertyName = PathUtils.getName(path);
+                UpdateOp op = commit.getUpdateOperationForNode(p);
+                op.set(propertyName, value);
+                break;
+            case '>': {
+                t.read(':');
+                String name = PathUtils.getName(path);
+                String position, target, to;
+                boolean rename;
+                if (t.matches('{')) {
+                    rename = false;
+                    position = t.readString();
+                    t.read(':');
+                    target = t.readString();
+                    t.read('}');
+                    if (!PathUtils.isAbsolute(target)) {
+                        target = PathUtils.concat(rootPath, target);
+                    }
+                } else {
+                    rename = true;
+                    position = null;
+                    target = t.readString();
+                    if (!PathUtils.isAbsolute(target)) {
+                        target = PathUtils.concat(rootPath, target);
+                    }
+                }
+                boolean before = false;
+                if ("last".equals(position)) {
+                    target = PathUtils.concat(target, name);
+                    position = null;
+                } else if ("first".equals(position)) {
+                    target = PathUtils.concat(target, name);
+                    position = null;
+                    before = true;
+                } else if ("before".equals(position)) {
+                    position = PathUtils.getName(target);
+                    target = PathUtils.getParentPath(target);
+                    target = PathUtils.concat(target, name);
+                    before = true;
+                } else if ("after".equals(position)) {
+                    position = PathUtils.getName(target);
+                    target = PathUtils.getParentPath(target);
+                    target = PathUtils.concat(target, name);
+                } else if (position == null) {
+                    // move
+                } else {
+                    throw new MicroKernelException("position: " + position);
+                }
+                to = PathUtils.relativize("/", target);
+                boolean inPlaceRename = false;
+                if (rename) {
+                    if (PathUtils.getParentPath(path).equals(PathUtils.getParentPath(to)))
{
+                        inPlaceRename = true;
+                        position = PathUtils.getName(path);
+                    }
+                }
+                // TODO support move operations
+                break;
+            }
+            case '*': {
+                // TODO possibly support target position notation
+                t.read(':');
+                String target = t.readString();
+                if (!PathUtils.isAbsolute(target)) {
+                    target = PathUtils.concat(rootPath, target);
+                }
+                String to = PathUtils.relativize("/", target);
+                // TODO support copy operations
+                break;
+            }
+            default:
+                throw new MicroKernelException("token: " + (char) t.getTokenType());
+            }
+        }
+        commit.apply(store);
+        headRevision = rev.toString();
+        return headRevision;
+    }    
+    
+    public static void parseAddNode(Commit commit, JsopReader t, String path) {
+        Node n = new Node(path, commit.getRevision());
+        if (!t.matches('}')) {
+            do {
+                String key = t.readString();
+                t.read(':');
+                if (t.matches('{')) {
+                    String childPath = PathUtils.concat(path, key);
+                    parseAddNode(commit, t, childPath);
+                } else {
+                    String value = t.readRawValue().trim();
+                    n.setProperty(key, value);
+                }
+            } while (t.matches(','));
+            t.read('}');
+        }
+        commit.addNode(n);
     }
 
     @Override
     public String branch(String trunkRevisionId) throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
     @Override
     public String merge(String branchRevisionId, String message)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
@@ -282,7 +409,7 @@ public class MongoMK implements MicroKer
     @Nonnull
     public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId)
             throws MicroKernelException {
-        // TODO Auto-generated method stub
+        // TODO implement if needed
         return null;
     }
 
@@ -330,4 +457,8 @@ public class MongoMK implements MicroKer
 
     }
 
+    public DocumentStore getDocumentStore() {
+        return store;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
Thu Feb 14 15:06:46 2013
@@ -18,16 +18,18 @@ package org.apache.jackrabbit.mongomk.pr
 
 import java.util.Map;
 
+import org.apache.jackrabbit.mk.json.JsopStream;
+
 /**
  * Represents a node held in memory (in the cache for example).
  */
 public class Node {
 
     final String path;
-    final String rev;
+    final Revision rev;
     final Map<String, String> properties = Utils.newMap();
     
-    Node(String path, String rev) {
+    Node(String path, Revision rev) {
         this.path = path;
         this.rev = rev;
     }
@@ -36,6 +38,10 @@ public class Node {
         properties.put(propertyName, value);
     }
     
+    public String getProperty(String propertyName) {
+        return properties.get(propertyName);
+    }    
+    
     public String toString() {
         StringBuilder buff = new StringBuilder();
         buff.append("path: ").append(path).append('\n');
@@ -45,4 +51,33 @@ public class Node {
         return buff.toString();
     }
     
+    /**
+     * Create an add node operation for this node.
+     */
+    UpdateOp asOperation(boolean isNew) {
+        String id = convertPathToDocumentId(path);
+        UpdateOp op = new UpdateOp(id, isNew);
+        op.set("_id", id);
+        if (!isNew) {
+            op.increment("_changeCount", 1L);
+        }
+        for (String p : properties.keySet()) {
+            op.addMapEntry(p, rev.toString(), properties.get(p));
+        }
+        return op;
+    }
+
+    static String convertPathToDocumentId(String path) {
+        int depth = Utils.pathDepth(path);
+        return depth + ":" + path;
+    }
+
+    public void append(JsopStream json) {
+        json.object();
+        for (String p : properties.keySet()) {
+            json.key(p).encodedValue(properties.get(p));
+        }
+        json.endObject();
+    }
+
 }

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java?rev=1446225&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java
(added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java
Thu Feb 14 15:06:46 2013
@@ -0,0 +1,139 @@
+/*
+ * 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.mongomk.prototype;
+
+/**
+ * A revision.
+ */
+public class Revision {
+    
+    /**
+     * The timestamp in milliseconds since 2013 (unlike in seconds since 1970 as
+     * in MongoDB).
+     */
+    private long timestamp;
+    
+    /**
+     * An incrementing counter, for commits that occur within the same
+     * millisecond.
+     */
+    private int counter;
+    
+    /**
+     * The cluster id (the MongoDB machine id).
+     */
+    private int clusterId;
+    
+    static long timestampOffset = java.sql.Timestamp.valueOf("2013-01-01 00:00:00.0").getTime()
/ 100;
+    static volatile long lastTimestamp;
+    static volatile int count;
+    
+    public Revision(long timestamp, int counter, int clusterId) {
+        this.timestamp = timestamp;
+        this.counter = counter;
+        this.clusterId = clusterId;
+    }
+    
+    /**
+     * Compare the time part of two revisions.
+     * 
+     * @return -1 if this revision occurred earlier, 1 if later, 0 if equal
+     */
+    int compareRevisionTime(Revision other) {
+        int comp = timestamp < other.timestamp ? -1 : timestamp > other.timestamp ?
1 : 0;
+        if (comp == 0) {
+            comp = counter < other.counter ? -1 : counter > other.counter ? 1 : 0;
+        }
+        return comp;
+    }
+    
+    /**
+     * Create a simple revision id. The format is similar to MongoDB ObjectId.
+     * 
+     * @param clusterId the unique machineId + processId
+     * @return the unique revision id
+     */
+    static Revision newRevision(int clusterId) {
+        long timestamp = System.currentTimeMillis() / 100 - timestampOffset;
+        int c;
+        synchronized (Revision.class) {
+            if (timestamp > lastTimestamp) {
+                lastTimestamp = timestamp;
+                c = count = 0;
+            } else if (timestamp < lastTimestamp) {
+                timestamp = lastTimestamp;
+                c = ++count;
+            } else {
+                c = ++count;
+            }
+            if (c >= 0xfff) {
+                timestamp++;
+                c = 0;
+                lastTimestamp = Math.max(timestamp, lastTimestamp);
+            }
+        }
+        return new Revision(timestamp, c, clusterId);
+    }
+    
+    public static Revision fromString(String rev) {
+        if (!rev.startsWith("r")) {
+            throw new IllegalArgumentException(rev);
+        }
+        String t = rev.substring(1, 8);
+        long timestamp = Long.parseLong(t, 16);
+        int idx = rev.indexOf('-');
+        int c = 0;
+        if (idx > 8) {
+            t = rev.substring(9, 11);
+            c = Integer.parseInt(t, 16);
+        }
+        t = rev.substring(idx + 1);
+        int clusterId = Integer.parseInt(t, 16);
+        Revision r = new Revision(timestamp, c, clusterId);
+        return r;
+    }
+    
+    public String toString() {
+        StringBuilder buff = new StringBuilder("r");
+        buff.append(Long.toHexString(0x10000000L + timestamp).substring(1));
+        buff.append(Integer.toHexString(0x1000 + counter).substring(1));
+        buff.append('-');
+        buff.append(Integer.toHexString(clusterId));
+        return buff.toString();
+    }
+    
+    public int hashCode() {
+        return (int) (timestamp >>> 32) ^ (int) timestamp ^ counter ^ clusterId;
+    }
+    
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other.getClass() != this.getClass()) {
+            return false;
+        }
+        Revision r = (Revision) other;
+        return r.timestamp == this.timestamp && 
+                r.counter == this.counter && 
+                r.clusterId == this.clusterId;
+    }
+
+    public int getClusterId() {
+        return clusterId;
+    }
+    
+}

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
Thu Feb 14 15:06:46 2013
@@ -26,17 +26,25 @@ public class UpdateOp {
     
     final String key;
     
+    final boolean isNew;
+    
     final Map<String, Operation> changes = new TreeMap<String, Operation>();
     
     /**
      * Create an update operation for the given document. The commit root is assumed
      * to be the path, unless this is changed later on.
      * 
-     * @param path the path
+     * @param key the primary key
+     * @param isNew whether this is a new document
      * @param rev the revision
      */
-    UpdateOp(String key) {
+    UpdateOp(String key, boolean isNew) {
         this.key = key;
+        this.isNew = isNew;
+    }
+    
+    boolean isNew() {
+        return isNew;
     }
     
     /**
@@ -79,6 +87,10 @@ public class UpdateOp {
         changes.put(property, op);
     }
     
+    public String toString() {
+        return "key: " + key + " " + (isNew ? "new" : "update") + " " + changes;
+    }
+    
     /**
      * A MongoDB operation for a given key within a document. 
      */

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
Thu Feb 14 15:06:46 2013
@@ -2,13 +2,9 @@ package org.apache.jackrabbit.mongomk.pr
 
 import java.util.Map;
 import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 public class Utils {
     
-    static long startTime = System.currentTimeMillis();
-    static AtomicInteger counter = new AtomicInteger();
-    
     static int pathDepth(String path) {
         return path.equals("/") ? 0 : path.replaceAll("[^/]", "").length();
     }
@@ -17,33 +13,4 @@ public class Utils {
         return new TreeMap<K, V>();
     }
     
-    /**
-     * Create a simple revision id. It consists of 3 hex characters for the
-     * seconds since startup, 3 characters for the cluster id, and 3 characters
-     * for the counter. The format is similar to MongoDB ObjectId.
-     * 
-     * @param clusterId the unique machineId + processId
-     * @return the unique revision id
-     */
-    static String createRevision(int clusterId) {
-        int seconds = (int) ((System.currentTimeMillis() - startTime) / 1000);
-        int count = counter.getAndIncrement();
-        StringBuilder buff = new StringBuilder("r");
-        buff.append(Integer.toHexString(0x1000 + seconds).substring(1));
-        buff.append(Integer.toHexString(0x1000 + clusterId).substring(1));
-        buff.append(Integer.toHexString(0x1000 + count).substring(1));
-        return buff.toString();
-    }
-    
-    /**
-     * Get the age of a revision in seconds.
-     * 
-     * @param rev the revision
-     * @return the age in seconds
-     */
-    int getRevisionAge(String rev) {
-        String s = rev.substring(0, 3);
-        return Integer.parseInt(s, 16);
-    }
-    
 }

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
Thu Feb 14 15:06:46 2013
@@ -60,7 +60,7 @@ public class MongoDocumentStoreTest {
         dropCollections();
         DocumentStore docStore = openDocumentStore();
 
-        UpdateOp updateOp = new UpdateOp("/");
+        UpdateOp updateOp = new UpdateOp("/", true);
         updateOp.addMapEntry("property1", "key1", "value1");
         updateOp.increment("property2", 1);
         updateOp.set("property3", "value3");
@@ -91,7 +91,7 @@ public class MongoDocumentStoreTest {
         int nUpdates = 10;
         List<UpdateOp> updateOps = new ArrayList<UpdateOp>();
         for (int i = 0; i < nUpdates; i++) {
-            UpdateOp updateOp = new UpdateOp("/node" + i);
+            UpdateOp updateOp = new UpdateOp("/node" + i, true);
             updateOp.set(MongoDocumentStore.KEY_PATH, "/node" + i);
             updateOp.addMapEntry("property1", "key1", "value1");
             updateOp.increment("property2", 1);
@@ -104,6 +104,7 @@ public class MongoDocumentStoreTest {
     }
 
     @Test
+    @Ignore
     public void addLotsOfNodes() throws Exception {
 
         char[] nPrefix = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
@@ -132,11 +133,7 @@ public class MongoDocumentStoreTest {
     }
 
     @Test
-    @Ignore
     public void batchInsert() throws Exception {
-        if (!MONGO_DB) {
-            return;
-        }
         doInsert(NODE_COUNT, true);
         doInsert(NODE_COUNT, false);
     }
@@ -206,7 +203,7 @@ public class MongoDocumentStoreTest {
 
         private void addNodes() {
             for (int i = 0; i < nNodes; i++) {
-                UpdateOp updateOp = new UpdateOp("/" + nodeName + i);
+                UpdateOp updateOp = new UpdateOp("/" + nodeName + i, true);
                 updateOp.addMapEntry("property1", "key1", "value1");
                 updateOp.set("property3", "value3");
                 docStore.createOrUpdate(Collection.NODES, updateOp);
@@ -215,7 +212,7 @@ public class MongoDocumentStoreTest {
 
         private void updateNodes() {
             for (int i = 0; i < nNodes; i++) {
-                UpdateOp updateOp = new UpdateOp("/" + nodeName + i);
+                UpdateOp updateOp = new UpdateOp("/" + nodeName + i, false);
                 updateOp.addMapEntry("property1", "key2", "value2");
                 updateOp.set("property4", "value4");
                 docStore.createOrUpdate(Collection.NODES, updateOp);

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java?rev=1446225&r1=1446224&r2=1446225&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
(original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
Thu Feb 14 15:06:46 2013
@@ -16,14 +16,55 @@
  */
 package org.apache.jackrabbit.mongomk.prototype;
 
-import org.apache.jackrabbit.mk.blobs.MemoryBlobStore;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.mongomk.prototype.DocumentStore.Collection;
 import org.junit.Test;
 
+import com.google.common.collect.Lists;
+
 public class SimpleTest {
 
     @Test
     public void test() {
-        MongoMK mk = new MongoMK(new MemoryDocumentStore(), new MemoryBlobStore(), 0);
+        MongoMK mk = new MongoMK();
+        mk.dispose();
+    }
+    
+    @Test
+    public void revision() {
+        for (int i=0; i<100; i++) {
+            Revision r = Revision.newRevision(i);
+            // System.out.println(r);
+            Revision r2 = Revision.fromString(r.toString());
+            assertEquals(r.toString(), r2.toString());
+            assertEquals(r.hashCode(), r2.hashCode());
+            assertTrue(r.equals(r2));
+        }
+    }
+    
+    @Test
+    public void addNodeGetNode() {
+        MongoMK mk = new MongoMK();
+        Revision rev = mk.newRevision();
+        Node n = new Node("/", rev);
+        n.setProperty("name", "Hello");
+        UpdateOp op = n.asOperation(true);
+        DocumentStore s = mk.getDocumentStore();
+        s.create(Collection.NODES, Lists.newArrayList(op));
+        Node n2 = mk.getNode("/", rev);
+        assertEquals("Hello", n2.getProperty("name"));
+        mk.dispose();
+    }
+
+    @Test
+    public void commit() {
+        MongoMK mk = new MongoMK();
+        String rev = mk.commit("/", "+\"test\":{\"name\": \"Hello\"}", null, null);
+        String test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
+        assertEquals("{\"name\":\"Hello\"}", test);
+        // System.out.println(test);
         mk.dispose();
     }
 }



Mime
View raw message