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

svn commit: r1537423 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/kernel/ main/java/org/apache/jackrabbit/oak/plugins/mongomk/ test/java/org/apache/jackrabbit/oak/ test/java/org/apache/jackrabbit/oak/plugins/mongomk/

Author: mreutegg
Date: Thu Oct 31 10:09:43 2013
New Revision: 1537423

URL: http://svn.apache.org/r1537423
Log:
OAK-1080: MongoMK: improved concurrency

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/BlobSerializer.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/CommitDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixture.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/SimpleTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/BlobSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/BlobSerializer.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/BlobSerializer.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/BlobSerializer.java Thu Oct 31 10:09:43 2013
@@ -22,7 +22,7 @@ import org.apache.jackrabbit.oak.api.Blo
  * Customizable mechanism for mapping {@link Blob} instances to corresponding
  * serialization identifiers.
  */
-class BlobSerializer {
+public class BlobSerializer {
 
     public String serialize(Blob blob) {
         return "Blob{" + blob + '}';

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java Thu Oct 31 10:09:43 2013
@@ -35,7 +35,7 @@ import org.apache.jackrabbit.oak.spi.sta
 /**
  * Utility class for serializing node and property states to JSON.
  */
-class JsonSerializer {
+public class JsonSerializer {
 
     private final JsopBuilder json;
 
@@ -67,7 +67,7 @@ class JsonSerializer {
                 new JsonFilter(filter), blobs);
     }
 
-    JsonSerializer(JsopBuilder json, BlobSerializer blobs) {
+    public JsonSerializer(JsopBuilder json, BlobSerializer blobs) {
         this(json, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
                 new JsonFilter("{\"properties\":[\"*\", \"-:childNodeCount\"]}"),
                 blobs);
@@ -116,7 +116,7 @@ class JsonSerializer {
         json.endObject();
     }
 
-    void serialize(PropertyState property) {
+    public void serialize(PropertyState property) {
         Type<?> type = property.getType();
         if (!type.isArray()) {
             serialize(property, type, 0);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/CommitDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/CommitDiff.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/CommitDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/CommitDiff.java Thu Oct 31 10:09:43 2013
@@ -18,11 +18,17 @@ package org.apache.jackrabbit.oak.plugin
 
 import javax.annotation.Nonnull;
 
+import org.apache.jackrabbit.mk.json.JsopBuilder;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.kernel.BlobSerializer;
+import org.apache.jackrabbit.oak.kernel.JsonSerializer;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 
 /**
  * Implementation of a {@link NodeStateDiff}, which translates the diffs into
@@ -32,45 +38,73 @@ class CommitDiff implements NodeStateDif
 
     private final Commit commit;
 
-    CommitDiff(@Nonnull Commit commit) {
-        this.commit = checkNotNull(commit);
+    private final String path;
+
+    private final JsopBuilder builder;
+
+    private final BlobSerializer blobs;
+
+    CommitDiff(@Nonnull Commit commit, @Nonnull BlobSerializer blobs) {
+        this(checkNotNull(commit), "/", new JsopBuilder(), checkNotNull(blobs));
+    }
+
+    private CommitDiff(Commit commit, String path,
+               JsopBuilder builder, BlobSerializer blobs) {
+        this.commit = commit;
+        this.path = path;
+        this.builder = builder;
+        this.blobs = blobs;
     }
 
     @Override
     public boolean propertyAdded(PropertyState after) {
-        // TODO: implement
-        return false;
+        setProperty(after);
+        return true;
     }
 
     @Override
     public boolean propertyChanged(PropertyState before, PropertyState after) {
-        // TODO: implement
-        return false;
+        setProperty(after);
+        return true;
     }
 
     @Override
     public boolean propertyDeleted(PropertyState before) {
-        // TODO: implement
-        return false;
+        commit.updateProperty(path, before.getName(), null);
+        return true;
     }
 
     @Override
     public boolean childNodeAdded(String name, NodeState after) {
-        // TODO: implement
-        return false;
+        String p = PathUtils.concat(path, name);
+        commit.addNode(new Node(p, commit.getRevision()));
+        return after.compareAgainstBaseState(EMPTY_NODE,
+                new CommitDiff(commit, p, builder, blobs));
     }
 
     @Override
     public boolean childNodeChanged(String name,
                                     NodeState before,
                                     NodeState after) {
-        // TODO: implement
-        return false;
+        String p = PathUtils.concat(path, name);
+        return after.compareAgainstBaseState(before,
+                new CommitDiff(commit, p, builder, blobs));
     }
 
     @Override
     public boolean childNodeDeleted(String name, NodeState before) {
-        // TODO: implement
-        return false;
+        String p = PathUtils.concat(path, name);
+        commit.removeNode(p);
+        return MISSING_NODE.compareAgainstBaseState(before,
+                new CommitDiff(commit, p, builder, blobs));
+    }
+
+    //----------------------------< internal >----------------------------------
+
+    private void setProperty(PropertyState property) {
+        builder.resetWriter();
+        JsonSerializer serializer = new JsonSerializer(builder, blobs);
+        serializer.serialize(property);
+        commit.updateProperty(path, property.getName(), serializer.toString());
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeBuilder.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeBuilder.java Thu Oct 31 10:09:43 2013
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.plugins.mongomk;
 
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 
 /**
  * A node builder implementation for MongoMK.
@@ -35,4 +36,19 @@ class MongoNodeBuilder extends MemoryNod
     protected MongoNodeBuilder createChildBuilder(String name) {
         return new MongoNodeBuilder(this, name);
     }
+
+    @Override
+    public boolean moveTo(NodeBuilder newParent, String newName) {
+        if (newParent instanceof MongoNodeBuilder) {
+            // check if this builder is an ancestor of newParent or newParent
+            MongoNodeBuilder parent = (MongoNodeBuilder) newParent;
+            while (parent != null) {
+                if (parent == this) {
+                    return false;
+                }
+                parent = (MongoNodeBuilder) parent.getParent();
+            }
+        }
+        return super.moveTo(newParent, newName);
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeState.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeState.java Thu Oct 31 10:09:43 2013
@@ -17,48 +17,81 @@
 package org.apache.jackrabbit.oak.plugins.mongomk;
 
 import java.util.Collections;
+import java.util.List;
 
 import javax.annotation.Nonnull;
+import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.mk.json.JsopReader;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.kernel.StringCache;
+import org.apache.jackrabbit.oak.kernel.TypeCodes;
+import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
+import org.apache.jackrabbit.oak.plugins.value.Conversions;
+import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.emptyList;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 
 /**
  * A {@link NodeState} implementation for the {@link MongoNodeStore}.
+ * TODO: merge MongoNodeState with Node
+ * TODO: implement more methods for efficient access (getProperty(), etc.)
  */
-class MongoNodeState extends AbstractNodeState {
+final class MongoNodeState extends AbstractNodeState {
 
     private final MongoNodeStore store;
 
-    private final String path;
+    private final Node node;
 
-    private final Revision revision;
+    /**
+     * TODO: OAK-1056
+     */
+    private boolean isBranch;
 
     MongoNodeState(@Nonnull MongoNodeStore store,
-                   @Nonnull String path,
-                   @Nonnull Revision revision) {
+                   @Nonnull Node node) {
         this.store = checkNotNull(store);
-        this.path = checkNotNull(path);
-        this.revision = checkNotNull(revision);
+        this.node = checkNotNull(node);
     }
 
     String getPath() {
-        return path;
+        return node.getPath();
     }
 
     Revision getRevision() {
-        return revision;
+        return node.getReadRevision();
     }
 
-    //--------------------------< NodeState >-----------------------------------
+    MongoNodeState setBranch() {
+        isBranch = true;
+        return this;
+    }
 
+    boolean isBranch() {
+        return isBranch;
+    }
+
+    //--------------------------< NodeState >-----------------------------------
 
     @Override
     public boolean equals(Object that) {
@@ -66,7 +99,7 @@ class MongoNodeState extends AbstractNod
             return true;
         } else if (that instanceof MongoNodeState) {
             MongoNodeState other = (MongoNodeState) that;
-            if (revision.equals(other.revision) && path.equals(other.path)) {
+            if (node.getLastRevision().equals(other.node.getLastRevision()) && getPath().equals(other.getPath())) {
                 return true;
             } else {
                 // TODO: optimize equals check for this case
@@ -81,37 +114,176 @@ class MongoNodeState extends AbstractNod
 
     @Override
     public boolean exists() {
-        return true;
+        return node != null;
     }
 
     @Nonnull
     @Override
     public Iterable<? extends PropertyState> getProperties() {
-        // TODO: implement
-        return Collections.emptyList();
+        if (node == null) {
+            return Collections.emptyList();
+        }
+        return Iterables.transform(node.getPropertyNames(), new Function<String, PropertyState>() {
+            @Override
+            public PropertyState apply(String name) {
+                String value = node.getProperty(name);
+                JsopReader reader = new JsopTokenizer(value);
+                if (reader.matches('[')) {
+                    return readArrayProperty(name, reader);
+                } else {
+                    return readProperty(name, reader);
+                }
+            }
+        });
     }
 
     @Nonnull
     @Override
     public NodeState getChildNode(@Nonnull String name) {
-        // TODO: implement
-        return EmptyNodeState.MISSING_NODE;
+        String p = PathUtils.concat(getPath(), name);
+        Node child = store.getNode(p, node.getLastRevision());
+        if (child == null) {
+            return EmptyNodeState.MISSING_NODE;
+        } else {
+            return new MongoNodeState(store, child);
+        }
     }
 
     @Nonnull
     @Override
     public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
-        // TODO: implement
-        return Collections.emptyList();
+        // TODO: handle many child nodes better
+        Node.Children children = store.getChildren(getPath(),
+                node.getLastRevision(), Integer.MAX_VALUE);
+        return Iterables.transform(children.children, new Function<String, ChildNodeEntry>() {
+            @Override
+            public ChildNodeEntry apply(String path) {
+                final String name = PathUtils.getName(path);
+                return new AbstractChildNodeEntry() {
+                    @Nonnull
+                    @Override
+                    public String getName() {
+                        return name;
+                    }
+
+                    @Nonnull
+                    @Override
+                    public NodeState getNodeState() {
+                        return getChildNode(name);
+                    }
+                };
+            }
+        });
     }
 
     @Nonnull
     @Override
     public NodeBuilder builder() {
-        // TODO: implement
-        return new MemoryNodeBuilder(this);
+        if (isBranch) {
+            return new MemoryNodeBuilder(this);
+        } else if ("/".equals(getPath())) {
+            return new MongoRootBuilder(this, store);
+        } else {
+            return new MemoryNodeBuilder(this);
+        }
     }
 
     //------------------------------< internal >--------------------------------
 
+    /**
+     * FIXME: copied from KernelNodeState.
+     *
+     * Read a {@code PropertyState} from a {@link JsopReader}
+     * @param name  The name of the property state
+     * @param reader  The reader
+     * @return new property state
+     */
+    private PropertyState readProperty(String name, JsopReader reader) {
+        if (reader.matches(JsopReader.NUMBER)) {
+            String number = reader.getToken();
+            try {
+                return new LongPropertyState(name, Long.parseLong(number));
+            } catch (NumberFormatException e) {
+                return new DoublePropertyState(name, Double.parseDouble(number));
+            }
+        } else if (reader.matches(JsopReader.TRUE)) {
+            return BooleanPropertyState.booleanProperty(name, true);
+        } else if (reader.matches(JsopReader.FALSE)) {
+            return BooleanPropertyState.booleanProperty(name, false);
+        } else if (reader.matches(JsopReader.STRING)) {
+            String jsonString = reader.getToken();
+            if (jsonString.startsWith(TypeCodes.EMPTY_ARRAY)) {
+                int type = PropertyType.valueFromName(jsonString.substring(TypeCodes.EMPTY_ARRAY.length()));
+                return PropertyStates.createProperty(name, emptyList(), Type.fromTag(type, true));
+            }
+            int split = TypeCodes.split(jsonString);
+            if (split != -1) {
+                int type = TypeCodes.decodeType(split, jsonString);
+                String value = TypeCodes.decodeName(split, jsonString);
+                if (type == PropertyType.BINARY) {
+                    return  BinaryPropertyState.binaryProperty(name, store.getBlob(value));
+                } else {
+                    return createProperty(name, StringCache.get(value), type);
+                }
+            } else {
+                return StringPropertyState.stringProperty(name, StringCache.get(jsonString));
+            }
+        } else {
+            throw new IllegalArgumentException("Unexpected token: " + reader.getToken());
+        }
+    }
+
+    /**
+     * FIXME: copied from KernelNodeState.
+     *
+     * Read a multi valued {@code PropertyState} from a {@link JsopReader}
+     * @param name  The name of the property state
+     * @param reader  The reader
+     * @return new property state
+     */
+    private PropertyState readArrayProperty(String name, JsopReader reader) {
+        int type = PropertyType.STRING;
+        List<Object> values = Lists.newArrayList();
+        while (!reader.matches(']')) {
+            if (reader.matches(JsopReader.NUMBER)) {
+                String number = reader.getToken();
+                try {
+                    type = PropertyType.LONG;
+                    values.add(Long.parseLong(number));
+                } catch (NumberFormatException e) {
+                    type = PropertyType.DOUBLE;
+                    values.add(Double.parseDouble(number));
+                }
+            } else if (reader.matches(JsopReader.TRUE)) {
+                type = PropertyType.BOOLEAN;
+                values.add(true);
+            } else if (reader.matches(JsopReader.FALSE)) {
+                type = PropertyType.BOOLEAN;
+                values.add(false);
+            } else if (reader.matches(JsopReader.STRING)) {
+                String jsonString = reader.getToken();
+                int split = TypeCodes.split(jsonString);
+                if (split != -1) {
+                    type = TypeCodes.decodeType(split, jsonString);
+                    String value = TypeCodes.decodeName(split, jsonString);
+                    if (type == PropertyType.BINARY) {
+                        values.add(store.getBlob(value));
+                    } else if (type == PropertyType.DOUBLE) {
+                        values.add(Conversions.convert(value).toDouble());
+                    } else if (type == PropertyType.DECIMAL) {
+                        values.add(Conversions.convert(value).toDecimal());
+                    } else {
+                        values.add(StringCache.get(value));
+                    }
+                } else {
+                    type = PropertyType.STRING;
+                    values.add(StringCache.get(jsonString));
+                }
+            } else {
+                throw new IllegalArgumentException("Unexpected token: " + reader.getToken());
+            }
+            reader.matches(',');
+        }
+        return createProperty(name, values, Type.fromTag(type, true));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java Thu Oct 31 10:09:43 2013
@@ -294,7 +294,7 @@ public final class MongoNodeStore
         backgroundThread.start();
     }
 
-    void dispose() {
+    public void dispose() {
         // force background write (with asyncDelay > 0, the root wouldn't be written)
         // TODO make this more obvious / explicit
         // TODO tests should also work if this is not done
@@ -729,7 +729,12 @@ public final class MongoNodeStore
      */
     @Nonnull
     MongoNodeState getRoot(@Nonnull Revision revision) {
-        return new MongoNodeState(this, "/", revision);
+        Node root = getNode("/", revision);
+        if (root == null) {
+            throw new IllegalStateException(
+                    "root node does not exist at revision " + revision);
+        }
+        return new MongoNodeState(this, root);
     }
 
     @Nonnull
@@ -842,6 +847,18 @@ public final class MongoNodeStore
         return rev;
     }
 
+    /**
+     * Returns the {@link Blob} with the given blobId.
+     *
+     * @param blobId the blobId of the blob.
+     * @return the blob.
+     */
+    @Nonnull
+    Blob getBlob(String blobId) {
+        // TODO: implement blob handling
+        return null;
+    }
+
     //------------------------< Observable >------------------------------------
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java Thu Oct 31 10:09:43 2013
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.mongomk;
 
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.kernel.BlobSerializer;
 import org.apache.jackrabbit.oak.plugins.observation.ChangeDispatcher;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStoreBranch;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -26,6 +28,14 @@ import org.apache.jackrabbit.oak.spi.sta
 public class MongoNodeStoreBranch
         extends AbstractNodeStoreBranch<MongoNodeStore, MongoNodeState> {
 
+    private final BlobSerializer blobs = new BlobSerializer() {
+        @Override
+        public String serialize(Blob blob) {
+            // TODO: implement correct blob handling
+            return super.serialize(blob);
+        }
+    };
+
     public MongoNodeStoreBranch(MongoNodeStore store,
                                 MongoNodeState base) {
         super(store, new ChangeDispatcher(store), base);
@@ -38,13 +48,13 @@ public class MongoNodeStoreBranch
 
     @Override
     protected MongoNodeState createBranch(MongoNodeState state) {
-        return store.getRoot(state.getRevision().asBranchRevision());
+        return store.getRoot(state.getRevision().asBranchRevision()).setBranch();
     }
 
     @Override
     protected MongoNodeState rebase(MongoNodeState branchHead,
                                     MongoNodeState base) {
-        return store.getRoot(store.rebase(branchHead.getRevision(), base.getRevision()));
+        return store.getRoot(store.rebase(branchHead.getRevision(), base.getRevision())).setBranch();
     }
 
     @Override
@@ -55,12 +65,16 @@ public class MongoNodeStoreBranch
     @Override
     protected MongoNodeState persist(final NodeState toPersist,
                                      final MongoNodeState base) {
-        return persist(new Changes() {
+        MongoNodeState state = persist(new Changes() {
             @Override
             public void with(Commit c) {
-                toPersist.compareAgainstBaseState(base, new CommitDiff(c));
+                toPersist.compareAgainstBaseState(base, new CommitDiff(c, blobs));
             }
         }, base);
+        if (base.isBranch()) {
+            state.setBranch();
+        }
+        return state;
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java Thu Oct 31 10:09:43 2013
@@ -105,6 +105,14 @@ public class Node implements CacheValue 
         return op;
     }
 
+    String getPath() {
+        return path;
+    }
+
+    Revision getReadRevision() {
+        return rev;
+    }
+
     public String getId() {
         return path + "@" + lastRevision;        
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java Thu Oct 31 10:09:43 2013
@@ -408,6 +408,7 @@ public class NodeDocument extends Docume
         }
         String path = Utils.getPathFromId(getId());
         Node n = new Node(path, readRevision, hasChildren());
+        Revision lastRevision = min;
         for (String key : keySet()) {
             if (!Utils.isPropertyName(key)) {
                 continue;
@@ -423,11 +424,19 @@ public class NodeDocument extends Docume
             String propertyName = Utils.unescapePropertyName(key);
             String v = value != null ? value.value : null;
             n.setProperty(propertyName, v);
+            // keep track of when this node was last modified
+            if (value != null && isRevisionNewer(context, value.revision, lastRevision)) {
+                lastRevision = value.revision;
+            }
         }
 
+        // lastRevision now points to the revision when this node was
+        // last modified directly. but it may also have been 'modified'
+        // by an operation on a descendant node, which is tracked in
+        // _lastRev.
+
         // when was this node last modified?
         Branch branch = context.getBranches().getBranch(readRevision);
-        Revision lastRevision = null;
         Map<Integer, Revision> lastRevs = Maps.newHashMap(getLastRev());
         // overlay with unsaved last modified from this instance
         if (lastModified != null) {
@@ -447,9 +456,15 @@ public class NodeDocument extends Docume
         for (Revision r : lastRevs.values()) {
             // ignore if newer than readRevision
             if (isRevisionNewer(context, r, readRevision)) {
+                // the node has a _lastRev which is newer than readRevision
+                // this means we don't know when if this node was
+                // modified by an operation on a descendant node between
+                // current lastRevision and readRevision. therefore we have
+                // to stay on the safe side and use readRevision
+                lastRevision = readRevision;
                 continue;
             }
-            if (lastRevision == null || isRevisionNewer(context, r, lastRevision)) {
+            if (isRevisionNewer(context, r, lastRevision)) {
                 lastRevision = r;
             }
         }
@@ -461,10 +476,6 @@ public class NodeDocument extends Docume
                 lastRevision = r;
             }
         }
-        if (lastRevision == null) {
-            // use readRevision if none found
-            lastRevision = readRevision;
-        }
         n.setLastRevision(lastRevision);
         return n;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixture.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixture.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixture.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixture.java Thu Oct 31 10:09:43 2013
@@ -24,6 +24,7 @@ import java.io.IOException;
 import org.apache.jackrabbit.mk.core.MicroKernelImpl;
 import org.apache.jackrabbit.oak.plugins.mongomk.MongoMK;
 import org.apache.jackrabbit.oak.kernel.KernelNodeStore;
+import org.apache.jackrabbit.oak.plugins.mongomk.MongoNodeStore;
 import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
 import org.apache.jackrabbit.oak.plugins.segment.memory.MemoryStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -72,6 +73,25 @@ public abstract class NodeStoreFixture {
         }
     };
 
+    public static final NodeStoreFixture MONGO_NS = new NodeStoreFixture() {
+        @Override
+        public String toString() {
+            return "MongoNS Fixture";
+        }
+
+        @Override
+        public NodeStore createNodeStore() {
+            return new MongoMK.Builder().getNodeStore();
+        }
+
+        @Override
+        public void dispose(NodeStore nodeStore) {
+            if (nodeStore instanceof MongoNodeStore) {
+                ((MongoNodeStore) nodeStore).dispose();
+            }
+        }
+    };
+
     public static final NodeStoreFixture MK_IMPL = new NodeStoreFixture() {
         @Override
         public String toString() {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/SimpleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/SimpleTest.java?rev=1537423&r1=1537422&r2=1537423&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/SimpleTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/SimpleTest.java Thu Oct 31 10:09:43 2013
@@ -96,7 +96,7 @@ public class SimpleTest {
         String rev4 = mk.commit("/test", "^\"a/x\":1", null, null);
         
         String r0 = mk.getNodes("/", rev0, 0, 0, Integer.MAX_VALUE, ":id");
-        assertEquals("{\":id\":\"/@r1-0-1\",\":childNodeCount\":0}", r0);
+        assertEquals("{\":id\":\"/@r0-0-1\",\":childNodeCount\":0}", r0);
         String r1 = mk.getNodes("/", rev1, 0, 0, Integer.MAX_VALUE, ":id");
         assertEquals("{\":id\":\"/@r2-0-1\",\"test\":{},\":childNodeCount\":1}", r1);
         String r2 = mk.getNodes("/", rev2, 0, 0, Integer.MAX_VALUE, ":id");