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 2012/09/17 14:54:06 UTC

svn commit: r1386591 [4/7] - in /jackrabbit/oak/trunk: ./ oak-mongomk-api/ oak-mongomk-api/src/ oak-mongomk-api/src/main/ oak-mongomk-api/src/main/java/ oak-mongomk-api/src/main/java/org/ oak-mongomk-api/src/main/java/org/apache/ oak-mongomk-api/src/ma...

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/NodeStoreMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/NodeStoreMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/NodeStoreMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/NodeStoreMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,206 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.jackrabbit.mk.json.JsopBuilder;
+import org.apache.jackrabbit.mongomk.api.NodeStore;
+import org.apache.jackrabbit.mongomk.api.command.Command;
+import org.apache.jackrabbit.mongomk.api.command.CommandExecutor;
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.command.CommitCommandMongo;
+import org.apache.jackrabbit.mongomk.command.GetHeadRevisionCommandMongo;
+import org.apache.jackrabbit.mongomk.command.GetNodesCommandMongo;
+import org.apache.jackrabbit.mongomk.command.NodeExistsCommandMongo;
+import org.apache.jackrabbit.mongomk.impl.command.CommandExecutorImpl;
+import org.apache.jackrabbit.mongomk.model.CommitMongo;
+import org.apache.jackrabbit.mongomk.model.HeadMongo;
+import org.apache.jackrabbit.mongomk.query.FetchValidCommitsQuery;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+
+import com.mongodb.DBCollection;
+
+/**
+ * Implementation of {@link NodeStore} for the {@code MongoDB}.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class NodeStoreMongo implements NodeStore {
+
+    private static final long WAIT_FOR_COMMIT_POLL_MILLIS = 1000;
+
+    private final CommandExecutor commandExecutor;
+    private final MongoConnection mongoConnection;
+
+    /**
+     * Constructs a new {@code NodeStoreMongo}.
+     *
+     * @param mongoConnection The {@link MongoConnection}.
+     */
+    public NodeStoreMongo(MongoConnection mongoConnection) {
+        this.mongoConnection = mongoConnection;
+        commandExecutor = new CommandExecutorImpl();
+    }
+
+    @Override
+    public String commit(Commit commit) throws Exception {
+        Command<String> command = new CommitCommandMongo(mongoConnection, commit);
+
+        return commandExecutor.execute(command);
+    }
+
+    @Override
+    public String getHeadRevision() throws Exception {
+        Command<String> command = new GetHeadRevisionCommandMongo(mongoConnection);
+
+        return commandExecutor.execute(command);
+    }
+
+    @Override
+    public Node getNodes(String path, String revisionId, int depth, long offset,
+            int maxChildNodes, String filter) throws Exception {
+        Command<Node> command = new GetNodesCommandMongo(mongoConnection, path, revisionId, depth);
+        return commandExecutor.execute(command);
+    }
+
+    @Override
+    public boolean nodeExists(String path, String revId) throws Exception {
+        Command<Boolean> command = new NodeExistsCommandMongo(mongoConnection, path, revId);
+
+        return commandExecutor.execute(command);
+    }
+
+    @Override
+    public String getJournal(String fromRevisionId, String toRevisionId, String path) {
+        path = (path == null || "".equals(path)) ? "/" : path;
+        boolean filtered = !"/".equals(path);
+
+        // FIXME [Mete] There's more work here.
+
+        if (toRevisionId == null) {
+            try {
+                toRevisionId = new GetHeadRevisionCommandMongo(mongoConnection).execute();
+            } catch (Exception e) {
+                // FIXME Handle
+            }
+        }
+
+        List<CommitMongo> commits = new FetchValidCommitsQuery(mongoConnection,
+                fromRevisionId, toRevisionId).execute();
+
+        CommitMongo toCommit = getCommit(commits, toRevisionId);
+
+        CommitMongo fromCommit;
+        if (toRevisionId.equals(fromRevisionId)) {
+            fromCommit = toCommit;
+        } else {
+            fromCommit = getCommit(commits, fromRevisionId);
+            if (fromCommit == null || (fromCommit.getTimestamp() > toCommit.getTimestamp())) {
+                // negative range, return empty journal
+                return "[]";
+            }
+        }
+
+        JsopBuilder commitBuff = new JsopBuilder().array();
+        // iterate over commits in chronological order,
+        // starting with oldest commit
+        for (int i = commits.size() - 1; i >= 0; i--) {
+            CommitMongo commit = commits.get(i);
+            //if (commit.getParentId() == null) {
+            //   continue;
+            //}
+            String diff = commit.getDiff();
+            // FIXME Check that filter really works.
+            if (!filtered || commit.getAffectedPaths().contains(path)) {
+                commitBuff.object()
+                .key("id").value(String.valueOf(commit.getRevisionId()))
+                .key("ts").value(commit.getTimestamp())
+                .key("msg").value(commit.getMessage())
+                .key("changes").value(diff).endObject();
+            }
+        }
+        return commitBuff.endArray().toString();
+    }
+
+    private CommitMongo getCommit(List<CommitMongo> commits, String toRevisionId) {
+        for (CommitMongo commit : commits) {
+            if (String.valueOf(commit.getRevisionId()).equals(toRevisionId)) {
+                return commit;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getRevisionHistory(long since, int maxEntries, String path) {
+      path = (path == null || "".equals(path)) ? "/" : path;
+      boolean filtered = !"/".equals(path);
+      maxEntries = maxEntries < 0 ? Integer.MAX_VALUE : maxEntries;
+
+      List<CommitMongo> history = new FetchValidCommitsQuery(mongoConnection, maxEntries).execute();
+      JsopBuilder buff = new JsopBuilder().array();
+      for (int i = history.size() - 1; i >= 0; i--) {
+          CommitMongo commit = history.get(i);
+          if (commit.getTimestamp() >= since) {
+              // FIXME [Mete] Check that filter really works.
+              if (!filtered || commit.getAffectedPaths().contains(path)) {
+                  buff.object()
+                  .key("id").value(String.valueOf(commit.getRevisionId()))
+                  .key("ts").value(commit.getTimestamp())
+                  .key("msg").value(commit.getMessage())
+                  .endObject();
+              }
+          }
+      }
+
+      return buff.endArray().toString();
+    }
+
+    @Override
+    public String waitForCommit(String oldHeadRevisionId, long timeout) throws InterruptedException {
+        long startTimestamp = System.currentTimeMillis();
+        long initialHeadRevisionId = getHeadRevisionId();
+
+        if (timeout <= 0) {
+            return String.valueOf(initialHeadRevisionId);
+        }
+
+        long oldHeadRevision = MongoUtil.toMongoRepresentation(oldHeadRevisionId);
+        if (oldHeadRevision < initialHeadRevisionId) {
+            return String.valueOf(initialHeadRevisionId);
+        }
+
+        long waitForCommitPollMillis = Math.min(WAIT_FOR_COMMIT_POLL_MILLIS, timeout);
+        while (true) {
+            long headRevisionId = getHeadRevisionId();
+            long now = System.currentTimeMillis();
+            if (headRevisionId != initialHeadRevisionId || now - startTimestamp >= timeout) {
+                return String.valueOf(headRevisionId);
+            }
+            Thread.sleep(waitForCommitPollMillis);
+        }
+    }
+
+    private long getHeadRevisionId() {
+        DBCollection headCollection = mongoConnection.getHeadCollection();
+        HeadMongo headMongo = (HeadMongo)headCollection.findOne();
+        long headRevisionId = headMongo.getHeadRevisionId();
+        return headRevisionId;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/NodeStoreMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,289 @@
+/*
+ * 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.command;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Instruction;
+import org.apache.jackrabbit.mongomk.model.CommitCommandInstructionVisitor;
+import org.apache.jackrabbit.mongomk.model.CommitMongo;
+import org.apache.jackrabbit.mongomk.model.HeadMongo;
+import org.apache.jackrabbit.mongomk.model.NodeMongo;
+import org.apache.jackrabbit.mongomk.query.FetchNodesForRevisionQuery;
+import org.apache.jackrabbit.mongomk.query.ReadAndIncHeadRevisionQuery;
+import org.apache.jackrabbit.mongomk.query.SaveAndSetHeadRevisionQuery;
+import org.apache.jackrabbit.mongomk.query.SaveCommitQuery;
+import org.apache.jackrabbit.mongomk.query.SaveNodesQuery;
+import org.apache.log4j.Logger;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import com.mongodb.WriteResult;
+
+/**
+ * A {@code Command} for committing into {@code MongoDB}.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class CommitCommandMongo extends AbstractCommand<String> {
+
+    private static final Logger logger = Logger.getLogger(CommitCommandMongo.class);
+
+    private final Commit commit;
+    private final MongoConnection mongoConnection;
+
+    private Set<String> affectedPaths;
+    private CommitMongo commitMongo;
+    private List<NodeMongo> existingNodes;
+    private HeadMongo headMongo;
+    private Set<NodeMongo> nodeMongos;
+    private String revisionId;
+
+    /**
+     * Constructs a new {@code CommitCommandMongo}.
+     *
+     * @param mongoConnection {@link MongoConnection}
+     * @param commit {@link Commit}
+     */
+    public CommitCommandMongo(MongoConnection mongoConnection, Commit commit) {
+        this.mongoConnection = mongoConnection;
+        this.commit = commit;
+    }
+
+    @Override
+    public String execute() throws Exception {
+        logger.debug(String.format("Trying to commit: %s", commit.getDiff()));
+
+        readAndIncHeadRevision();
+        createRevision();
+        createMongoNodes();
+        createMongoCommit();
+        readExistingNodes();
+        mergeNodes();
+        prepareMongoNodes();
+        saveNodes();
+        saveCommit();
+        boolean success = saveAndSetHeadRevision();
+
+        logger.debug(String.format("Success was: %b", success));
+
+        if (!success) {
+            markAsFailed();
+
+            throw new ConflictingCommitException();
+        }
+
+        addRevisionId();
+
+        return revisionId;
+    }
+
+    @Override
+    public int getNumOfRetries() {
+        return 10;
+    }
+
+    @Override
+    public boolean needsRetry(Exception e) {
+        return e instanceof ConflictingCommitException;
+    }
+
+    /**
+     * This is protected for testing purposed only.
+     *
+     * @return N/A
+     * @throws Exception
+     */
+    protected boolean saveAndSetHeadRevision() throws Exception {
+        boolean success = true;
+
+        HeadMongo headMongo = new SaveAndSetHeadRevisionQuery(mongoConnection, this.headMongo.getHeadRevisionId(),
+                HeadMongo.toDBRepresentation(revisionId)).execute();
+        if (headMongo == null) {
+            // TODO: Check for conflicts!
+            logger.warn(String
+                    .format("Encounterd a conflicting update, thus can't commit revision %s and will be retried with new revision",
+                            revisionId));
+
+            success = false;
+        }
+
+        return success;
+    }
+
+    private void addRevisionId() {
+        commit.setRevisionId(revisionId);
+    }
+
+    private void createMongoCommit() throws Exception {
+        commitMongo = CommitMongo.fromCommit(commit);
+        commitMongo.setRevisionId(revisionId);
+        commitMongo.setAffectedPaths(new LinkedList<String>(affectedPaths));
+        commitMongo.setBaseRevId(headMongo.getHeadRevisionId());
+    }
+
+    private void createMongoNodes() throws Exception {
+        CommitCommandInstructionVisitor visitor = new CommitCommandInstructionVisitor(mongoConnection,
+                headMongo.getHeadRevisionId());
+        for (Instruction instruction : commit.getInstructions()) {
+            instruction.accept(visitor);
+        }
+
+        Map<String, NodeMongo> pathNodeMap = visitor.getPathNodeMap();
+
+        affectedPaths = pathNodeMap.keySet(); // TODO Original copies and moved nodes must be included!
+        nodeMongos = new HashSet<NodeMongo>(pathNodeMap.values());
+        for (NodeMongo nodeMongo : nodeMongos) {
+            nodeMongo.setRevisionId(revisionId);
+        }
+    }
+
+    private void createRevision() {
+        revisionId = String.valueOf(headMongo.getNextRevisionId() - 1);
+    }
+
+    private void markAsFailed() throws Exception {
+        DBCollection commitCollection = mongoConnection.getCommitCollection();
+        DBObject query = QueryBuilder.start("_id").is(commitMongo.getObjectId("_id")).get();
+        DBObject update = new BasicDBObject("$set", new BasicDBObject(CommitMongo.KEY_FAILED, Boolean.TRUE));
+        WriteResult writeResult = commitCollection.update(query, update);
+        if (writeResult.getError() != null) {
+            throw new Exception(String.format("Update wasn't successful: %s", writeResult)); // TODO now what?
+        }
+    }
+
+    private void mergeNodes() {
+        for (NodeMongo existingNode : existingNodes) {
+            for (NodeMongo committingNode : nodeMongos) {
+                if (existingNode.getPath().equals(committingNode.getPath())) {
+                    logger.debug(String.format("Found existing node to merge: %s", existingNode.getPath()));
+                    logger.debug(String.format("Existing node: %s", existingNode));
+                    logger.debug(String.format("Committing node: %s", committingNode));
+
+                    Map<String, Object> existingProperties = existingNode.getProperties();
+
+                    if (existingProperties != null) {
+                        committingNode.setProperties(existingProperties);
+
+                        logger.debug(String.format("Merged properties for %s: %s", existingNode.getPath(),
+                                existingProperties));
+                    }
+
+                    List<String> existingChildren = existingNode.getChildren();
+
+                    if (existingChildren != null) {
+                        committingNode.setChildren(existingChildren);
+
+                        logger.debug(String.format("Merged children for %s: %s", existingNode.getPath(), existingChildren));
+                    }
+
+                    committingNode.setBaseRevisionId(existingNode.getRevisionId());
+
+                    logger.debug(String.format("Merged node for %s: %s", existingNode.getPath(), committingNode));
+
+                    break;
+                }
+            }
+        }
+    }
+
+    private void prepareMongoNodes() {
+        for (NodeMongo committingNode : nodeMongos) {
+            logger.debug(String.format("Preparing children (added and removed) of %s", committingNode.getPath()));
+            logger.debug(String.format("Committing node: %s", committingNode));
+
+            List<String> children = committingNode.getChildren();
+            if (children == null) {
+                children = new LinkedList<String>();
+            }
+
+            List<String> addedChildren = committingNode.getAddedChildren();
+            if (addedChildren != null) {
+                children.addAll(addedChildren);
+            }
+
+            List<String> removedChildren = committingNode.getRemovedChildren();
+            if (removedChildren != null) {
+                children.removeAll(removedChildren);
+            }
+
+            if (!children.isEmpty()) {
+                Set<String> temp = new HashSet<String>(children); // remove all duplicates
+                committingNode.setChildren(new LinkedList<String>(temp));
+            } else {
+                committingNode.setChildren(null);
+            }
+
+            Map<String, Object> properties = committingNode.getProperties();
+            if (properties == null) {
+                properties = new HashMap<String, Object>();
+            }
+
+            Map<String, Object> addedProperties = committingNode.getAddedProps();
+            if (addedProperties != null) {
+                properties.putAll(addedProperties);
+            }
+
+            Map<String, Object> removedProperties = committingNode.getRemovedProps();
+            if (removedProperties != null) {
+                for (Map.Entry<String, Object> entry : removedProperties.entrySet()) {
+                    properties.remove(entry.getKey());
+                }
+            }
+
+            if (!properties.isEmpty()) {
+                committingNode.setProperties(properties);
+            } else {
+                committingNode.setProperties(null);
+            }
+
+            logger.debug(String.format("Prepared committing node: %s", committingNode));
+        }
+    }
+
+    private void readAndIncHeadRevision() throws Exception {
+        headMongo = new ReadAndIncHeadRevisionQuery(mongoConnection).execute();
+    }
+
+    private void readExistingNodes() {
+        Set<String> paths = new HashSet<String>();
+        for (NodeMongo nodeMongo : nodeMongos) {
+            paths.add(nodeMongo.getPath());
+        }
+
+        existingNodes = new FetchNodesForRevisionQuery(mongoConnection, paths,
+                String.valueOf(headMongo.getHeadRevisionId())).execute();
+    }
+
+    private void saveCommit() throws Exception {
+        new SaveCommitQuery(mongoConnection, commitMongo).execute();
+    }
+
+    private void saveNodes() throws Exception {
+        new SaveNodesQuery(mongoConnection, nodeMongos).execute();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/CommitCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ConflictingCommitException.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ConflictingCommitException.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ConflictingCommitException.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ConflictingCommitException.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,38 @@
+/*
+ * 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.command;
+
+public class ConflictingCommitException extends Exception {
+
+    private static final long serialVersionUID = -5827664000083665577L;
+
+    public ConflictingCommitException() {
+        super();
+    }
+
+    public ConflictingCommitException(String message) {
+        super(message);
+    }
+
+    public ConflictingCommitException(Throwable t) {
+        super(t);
+    }
+
+    public ConflictingCommitException(String message, Throwable t) {
+        super(message, t);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ConflictingCommitException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetBlobLengthCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetBlobLengthCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetBlobLengthCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetBlobLengthCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,45 @@
+/*
+ * 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.command;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.gridfs.GridFS;
+import com.mongodb.gridfs.GridFSDBFile;
+
+public class GetBlobLengthCommandMongo extends AbstractCommand<Long> {
+
+    private final MongoConnection mongoConnection;
+    private final String blobId;
+
+    public GetBlobLengthCommandMongo(MongoConnection mongoConnection, String blobId) {
+        this.mongoConnection = mongoConnection;
+        this.blobId = blobId;
+    }
+
+    @Override
+    public Long execute() throws Exception {
+        GridFS gridFS = mongoConnection.getGridFS();
+        GridFSDBFile gridFSDBFile = gridFS.findOne(new BasicDBObject("md5", blobId));
+        if (gridFSDBFile == null) {
+            throw new Exception("Blob does not exiss");
+        }
+        return gridFSDBFile.getLength();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetBlobLengthCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetHeadRevisionCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetHeadRevisionCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetHeadRevisionCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetHeadRevisionCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,56 @@
+/*
+ * 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.command;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+import org.apache.jackrabbit.mongomk.query.FetchHeadRevisionQuery;
+
+/**
+ * A {@code Command} for getting the head revision from {@code MongoDB}.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class GetHeadRevisionCommandMongo extends AbstractCommand<String> {
+
+    private final MongoConnection mongoConnection;
+
+    /**
+     * Constructs a new {@code GetHeadRevisionCommandMongo}.
+     *
+     * @param mongoConnection The {@link MongoConnection}.
+     */
+    public GetHeadRevisionCommandMongo(MongoConnection mongoConnection) {
+        this.mongoConnection = mongoConnection;
+    }
+
+    @Override
+    public String execute() throws Exception {
+        long headRevision = fetchHeadRevision();
+        String revisionId = convertToRevisionId(headRevision);
+
+        return revisionId;
+    }
+
+    private String convertToRevisionId(long headRevision) {
+        return String.valueOf(headRevision);
+    }
+
+    private long fetchHeadRevision() throws Exception {
+        return new FetchHeadRevisionQuery(mongoConnection).execute();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetHeadRevisionCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetNodesCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetNodesCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetNodesCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetNodesCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,241 @@
+/*
+ * 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.command;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.impl.model.NodeImpl;
+import org.apache.jackrabbit.mongomk.model.CommitMongo;
+import org.apache.jackrabbit.mongomk.model.NodeMongo;
+import org.apache.jackrabbit.mongomk.query.FetchNodesByPathAndDepthQuery;
+import org.apache.jackrabbit.mongomk.query.FetchValidCommitsQuery;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * A {@code Command} for getting nodes from {@code MongoDB}.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class GetNodesCommandMongo extends AbstractCommand<Node> {
+
+    class InconsitentNodeHierarchyException extends Exception {
+        private static final long serialVersionUID = 8155418280936077632L;
+    }
+
+    private static final Logger LOG = Logger.getLogger(GetNodesCommandMongo.class);
+
+    private final MongoConnection mongoConnection;
+    private final String path;
+    private final int depth;
+
+    private String revisionId;
+    private List<CommitMongo> lastCommits;
+    private List<NodeMongo> nodeMongos;
+
+    private Map<String, NodeMongo> pathAndNodeMap;
+    private Map<String, Long> problematicNodes;
+    private Node rootOfPath;
+
+    /**
+     * Constructs a new {@code GetNodesCommandMongo}.
+     *
+     * @param mongoConnection The {@link MongoConnection}.
+     * @param path The root path of the nodes to get.
+     * @param revisionId The {@link RevisionId} or {@code null}.
+     * @param depth The depth.
+     */
+    public GetNodesCommandMongo(MongoConnection mongoConnection, String path,
+            String revisionId, int depth) {
+        this.mongoConnection = mongoConnection;
+        this.path = path;
+        this.revisionId = revisionId;
+        this.depth = depth;
+    }
+
+    @Override
+    public Node execute() throws Exception {
+        ensureRevisionId();
+        readLastCommits();
+        deriveProblematicNodes();
+
+        readNodesByPath();
+        createPathAndNodeMap();
+        boolean verified = verifyProblematicNodes() && verifyNodeHierarchy();
+
+        if (!verified) {
+            throw new InconsitentNodeHierarchyException();
+        }
+
+        this.buildNodeStructure();
+
+        return rootOfPath;
+    }
+
+    @Override
+    public int getNumOfRetries() {
+        return 3;
+    }
+
+    @Override
+    public boolean needsRetry(Exception e) {
+        return e instanceof InconsitentNodeHierarchyException;
+    }
+
+    private void buildNodeStructure() {
+        NodeMongo nodeMongoRootOfPath = pathAndNodeMap.get(path);
+        rootOfPath = this.buildNodeStructure(nodeMongoRootOfPath);
+    }
+
+    private NodeImpl buildNodeStructure(NodeMongo nodeMongo) {
+        NodeImpl node = NodeMongo.toNode(nodeMongo);
+        Set<Node> children = node.getChildren();
+        if (children != null) {
+            for (Node child : children) {
+                NodeMongo nodeMongoChild = pathAndNodeMap.get(child.getPath());
+                if (nodeMongoChild != null) {
+                    NodeImpl nodeChild = this.buildNodeStructure(nodeMongoChild);
+                    node.addChild(nodeChild);
+                }
+            }
+        }
+
+        return node;
+    }
+
+    private void createPathAndNodeMap() {
+        pathAndNodeMap = new HashMap<String, NodeMongo>();
+        for (NodeMongo nodeMongo : nodeMongos) {
+            pathAndNodeMap.put(nodeMongo.getPath(), nodeMongo);
+        }
+    }
+
+    private void deriveProblematicNodes() {
+        problematicNodes = new HashMap<String, Long>();
+
+        for (ListIterator<CommitMongo> iterator = lastCommits.listIterator(); iterator.hasPrevious();) {
+            CommitMongo commitMongo = iterator.previous();
+            long revisionId = commitMongo.getRevisionId();
+            List<String> affectedPath = commitMongo.getAffectedPaths();
+
+            for (String path : affectedPath) {
+                problematicNodes.put(path, revisionId);
+            }
+        }
+    }
+
+    private void ensureRevisionId() throws Exception {
+        if (revisionId == null) {
+            revisionId = new GetHeadRevisionCommandMongo(mongoConnection).execute();
+        }
+    }
+
+    private void readLastCommits() throws Exception {
+        lastCommits = new FetchValidCommitsQuery(mongoConnection, revisionId).execute();
+
+        // TODO Move this into the Query which should throw the exception in case the commit doesn't exist
+        if (revisionId != null) {
+            boolean revisionExists = false;
+            long revId = MongoUtil.toMongoRepresentation(revisionId);
+            for (CommitMongo commitMongo : lastCommits) {
+                if (commitMongo.getRevisionId() == revId) {
+                    revisionExists = true;
+
+                    break;
+                }
+            }
+
+            if (!revisionExists) {
+                throw new Exception(String.format("The revisionId %d could not be found", revId));
+            }
+        }
+    }
+
+    private void readNodesByPath() {
+        FetchNodesByPathAndDepthQuery query = new FetchNodesByPathAndDepthQuery(mongoConnection, path, revisionId,
+                depth);
+        nodeMongos = query.execute();
+    }
+
+    private boolean verifyNodeHierarchy() {
+        boolean verified = false;
+
+        verified = verifyNodeHierarchyRec(path, 0);
+
+        if (!verified) {
+            LOG.error(String.format("Node hierarchy could not be verified because some nodes were inconsistent: %s",
+                    path));
+        }
+
+        return verified;
+    }
+
+    private boolean verifyNodeHierarchyRec(String path, int currentDepth) {
+        boolean verified = false;
+
+        NodeMongo nodeMongo = pathAndNodeMap.get(path);
+        if (nodeMongo != null) {
+            verified = true;
+            if ((depth == -1) || (currentDepth < depth)) {
+                List<String> childNames = nodeMongo.getChildren();
+                if (childNames != null) {
+                    for (String childName : childNames) {
+                        String childPath = PathUtils.concat(path, childName);
+                        verified = verifyNodeHierarchyRec(childPath, ++currentDepth);
+                        if (!verified) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        return verified;
+    }
+
+    private boolean verifyProblematicNodes() {
+        boolean verified = true;
+
+        for (Map.Entry<String, Long> entry : problematicNodes.entrySet()) {
+            String path = entry.getKey();
+            Long revisionId = entry.getValue();
+
+            NodeMongo nodeMongo = pathAndNodeMap.get(path);
+            if (nodeMongo != null) {
+                if (!revisionId.equals(nodeMongo.getRevisionId())) {
+                    verified = false;
+
+                    LOG.error(String
+                            .format("Node could not be verified because the expected revisionId did not match: %d (expected) vs %d (actual)",
+                                    revisionId, nodeMongo.getRevisionId()));
+
+                    break;
+                }
+            }
+        }
+
+        return verified;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/GetNodesCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/NodeExistsCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/NodeExistsCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/NodeExistsCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/NodeExistsCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,92 @@
+/*
+ * 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.command;
+
+import java.util.List;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+import org.apache.jackrabbit.mongomk.model.NodeMongo;
+import org.apache.jackrabbit.mongomk.query.FetchNodeByPathQuery;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+/**
+ * A {@code Command} for determine whether a node exists from {@code MongoDB}.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class NodeExistsCommandMongo extends AbstractCommand<Boolean> {
+    private final MongoConnection mongoConnection;
+    private NodeMongo parentNode;
+    private final String path;
+    private String revisionId;
+
+    /**
+     * Constructs a new {@code NodeExistsCommandMongo}.
+     *
+     * @param mongoConnection The {@link MongoConnection}.
+     * @param path The root path of the nodes to get.
+     * @param revisionId The {@link RevisionId} or {@code null}.
+     */
+    public NodeExistsCommandMongo(MongoConnection mongoConnection, String path, String revisionId) {
+        this.mongoConnection = mongoConnection;
+        this.path = path;
+        this.revisionId = revisionId;
+    }
+
+    @Override
+    public Boolean execute() throws Exception {
+        if (PathUtils.denotesRoot(path)) {
+            return true;
+        }
+        ensureRevisionId();
+        readParentNode();
+        return childExists();
+    }
+
+    private void ensureRevisionId() throws Exception {
+        if (revisionId == null) {
+            revisionId = new GetHeadRevisionCommandMongo(mongoConnection).execute();
+        }
+    }
+
+    private void readParentNode() throws Exception {
+        String parentPath = PathUtils.getParentPath(path);
+        FetchNodeByPathQuery query = new FetchNodeByPathQuery(mongoConnection, parentPath, MongoUtil.toMongoRepresentation(revisionId));
+        parentNode = query.execute();
+    }
+
+    private boolean childExists() {
+        if (parentNode == null) {
+            return false;
+        }
+
+        List<String> children = parentNode.getChildren();
+        if (children == null || children.isEmpty()) {
+            return false;
+        }
+
+        for (String child : children) {
+            if (child.equals(PathUtils.getName(path))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/NodeExistsCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ReadBlobCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ReadBlobCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ReadBlobCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ReadBlobCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,75 @@
+/*
+ * 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.command;
+
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.gridfs.GridFS;
+import com.mongodb.gridfs.GridFSDBFile;
+
+public class ReadBlobCommandMongo extends AbstractCommand<Integer> {
+
+    private final MongoConnection mongoConnection;
+    private final String blobId;
+    private final long blobOffset;
+    private final byte[] buffer;
+    private final int bufferOffset;
+    private final int length;
+
+    public ReadBlobCommandMongo(MongoConnection mongoConnection, String blobId, long blobOffset, byte[] buffer,
+            int bufferOffset, int length) {
+        this.mongoConnection = mongoConnection;
+        this.blobId = blobId;
+        this.blobOffset = blobOffset;
+        this.buffer = buffer;
+        this.bufferOffset = bufferOffset;
+        this.length = length;
+    }
+
+    @Override
+    public Integer execute() throws Exception {
+        return fetchBlobFromMongo();
+    }
+
+    // FIXME [Mete] This takes a long time, see MicroKernelIT#readBlob. See if
+    // it can be improved.
+    private int fetchBlobFromMongo() throws Exception {
+        GridFS gridFS = mongoConnection.getGridFS();
+        GridFSDBFile gridFile = gridFS.findOne(new BasicDBObject("md5", blobId));
+        long fileLength = gridFile.getLength();
+
+        long start = blobOffset;
+        long end = blobOffset + length;
+        if (end > fileLength) {
+            end = fileLength;
+        }
+
+        int totalBytes = -1;
+        if (start < end) {
+            InputStream is = gridFile.getInputStream();
+            IOUtils.skipFully(is, blobOffset);
+            totalBytes = is.read(buffer, bufferOffset, length);
+            is.close();
+        }
+        return totalBytes;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/ReadBlobCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/WriteBlobCommandMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/WriteBlobCommandMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/WriteBlobCommandMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/WriteBlobCommandMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,68 @@
+/*
+ * 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.command;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.gridfs.GridFS;
+import com.mongodb.gridfs.GridFSDBFile;
+import com.mongodb.gridfs.GridFSInputFile;
+
+public class WriteBlobCommandMongo extends AbstractCommand<String> {
+
+    private final MongoConnection mongoConnection;
+    private final InputStream is;
+
+    public WriteBlobCommandMongo(MongoConnection mongoConnection, InputStream is) {
+        this.mongoConnection = mongoConnection;
+        this.is = is;
+    }
+
+    @Override
+    public String execute() throws Exception {
+        return saveBlob();
+    }
+
+    private String saveBlob() throws IOException {
+        GridFS gridFS = mongoConnection.getGridFS();
+        BufferedInputStream bis = new BufferedInputStream(is);
+        String md5 = calculateMd5(bis);
+        GridFSDBFile gridFile = gridFS.findOne(new BasicDBObject("md5", md5));
+        if (gridFile != null) {
+            is.close();
+            return md5;
+        }
+
+        GridFSInputFile gridFSInputFile = gridFS.createFile(bis, true);
+        gridFSInputFile.save();
+        return gridFSInputFile.getMD5();
+    }
+
+    private String calculateMd5(BufferedInputStream bis) throws IOException {
+        bis.mark(Integer.MAX_VALUE);
+        String md5 = DigestUtils.md5Hex(bis);
+        bis.reset();
+        return md5;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/command/WriteBlobCommandMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitCommandInstructionVisitor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitCommandInstructionVisitor.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitCommandInstructionVisitor.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitCommandInstructionVisitor.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,261 @@
+/*
+ * 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.model;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.AddNodeInstruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.AddPropertyInstruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.CopyNodeInstruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.MoveNodeInstruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.RemoveNodeInstruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.SetPropertyInstruction;
+import org.apache.jackrabbit.mongomk.api.model.InstructionVisitor;
+import org.apache.jackrabbit.mongomk.query.FetchNodeByPathQuery;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+public class CommitCommandInstructionVisitor implements InstructionVisitor {
+
+    private final long headRevisionId;
+    private final MongoConnection mongoConnection;
+    private final Map<String, NodeMongo> pathNodeMap;
+
+    public CommitCommandInstructionVisitor(MongoConnection mongoConnection,
+            long headRevisionId) {
+        this.mongoConnection = mongoConnection;
+        this.headRevisionId = headRevisionId;
+        pathNodeMap = new HashMap<String, NodeMongo>();
+    }
+
+    public Map<String, NodeMongo> getPathNodeMap() {
+        return pathNodeMap;
+    }
+
+    @Override
+    public void visit(AddNodeInstruction instruction) {
+// Old code
+//        String path = instruction.getPath();
+//        getStagedNode(path);
+//        if (!PathUtils.denotesRoot(path)) {
+//            String parentPath = PathUtils.getParentPath(path);
+//            NodeMongo parentNode = getStagedNode(parentPath);
+//            parentNode.addChild(PathUtils.getName(path));
+//        }
+
+        String path = instruction.getPath();
+        getStagedNode(path);
+        String nodeName = PathUtils.getName(path);
+        String parentNodePath = PathUtils.getParentPath(path);
+        NodeMongo parent = null;
+        if (!PathUtils.denotesRoot(parentNodePath)) {
+            parent = getStoredNode(parentNodePath);
+            if (parent == null) {
+                throw new RuntimeException("No such parent: " + PathUtils.getName(parentNodePath));
+            }
+            // FIXME [Mete] Add once tests are fixed.
+            //if (parent.childExists(nodeName)) {
+            //    throw new RuntimeException("There's already a child node with name '" + nodeName + "'");
+            //}
+        } else {
+            parent = getStagedNode(parentNodePath);
+        }
+        parent.addChild(nodeName);
+    }
+
+    @Override
+    public void visit(AddPropertyInstruction instruction) {
+        NodeMongo node = getStagedNode(instruction.getPath());
+        node.addProperty(instruction.getKey(), instruction.getValue());
+    }
+
+    @Override
+    public void visit(CopyNodeInstruction instruction) {
+        String srcPath = instruction.getSourcePath();
+        String destPath = instruction.getDestPath();
+
+        String srcParentPath = PathUtils.getParentPath(srcPath);
+        String srcNodeName = PathUtils.getName(srcPath);
+
+        String destParentPath = PathUtils.getParentPath(destPath);
+        String destNodeName = PathUtils.getName(destPath);
+
+        NodeMongo srcParent = pathNodeMap.get(srcParentPath);
+        if (srcParent == null) {
+            // The subtree to be copied has not been modified
+            boolean entryExists = getStoredNode(srcParentPath).childExists(srcNodeName);
+            if (!entryExists) {
+                throw new RuntimeException("Not found: " + srcPath);
+            }
+            NodeMongo destParent = getStagedNode(destParentPath);
+            if (destParent.childExists(destNodeName)) {
+                throw new RuntimeException("Node already exists at copy destination path: " + destPath);
+            }
+
+            // Copy src node to destPath.
+            NodeMongo srcNode = getStoredNode(srcPath);
+            NodeMongo destNode = NodeMongo.fromDBObject(srcNode);
+            destNode.setPath(destPath);
+            // FIXME - [Mete] This needs to do proper merge instead of just add.
+            List<String> addedChildren = srcNode.getAddedChildren();
+            if (addedChildren != null && !addedChildren.isEmpty()) {
+                for (String child : addedChildren) {
+                    getStagedNode(PathUtils.concat(destPath, child));
+                    destNode.addChild(child);
+                }
+            }
+            pathNodeMap.put(destPath, destNode);
+
+            // Add to destParent.
+            destParent.addChild(destNodeName);
+
+            return;
+        }
+
+        boolean srcEntryExists = srcParent.childExists(srcNodeName);
+        if (!srcEntryExists) {
+            throw new RuntimeException(srcPath);
+        }
+
+        // FIXME - [Mete] The rest is not totally correct.
+        NodeMongo destParent = getStagedNode(destParentPath);
+        NodeMongo srcNode = getStagedNode(srcPath);
+
+        if (srcNode != null) {
+            // Copy the modified subtree
+            NodeMongo destNode = NodeMongo.fromDBObject(srcNode);
+            destNode.setPath(destPath);
+            pathNodeMap.put(destPath,  destNode);
+            destParent.addChild(destNodeName);
+            //destParent.add(destNodeName, srcNode.copy());
+        } else {
+            NodeMongo destNode = NodeMongo.fromDBObject(srcNode);
+            destNode.setPath(destPath);
+            pathNodeMap.put(destPath,  destNode);
+            destParent.addChild(destNodeName);
+            //destParent.add(new ChildNodeEntry(destNodeName, srcEntry.getId()));
+        }
+
+        // [Mete] Old code from Philipp.
+        // retrieve all nodes beyond and add them as new children to the dest location
+//        List<NodeMongo> childNodesToCopy = new FetchNodesByPathAndDepthQuery(mongoConnection, srcPath,
+//                revisionId, -1).execute();
+//        for (NodeMongo nodeMongo : childNodesToCopy) {
+//            String oldPath = nodeMongo.getPath();
+//            String oldPathRel = PathUtils.relativize(srcPath, oldPath);
+//            String newPath = PathUtils.concat(destPath, oldPathRel);
+//
+//            nodeMongo.setPath(newPath);
+//            nodeMongo.removeField("_id");
+//            pathNodeMap.put(newPath, nodeMongo);
+//        }
+
+        // tricky part now: In case we already know about any changes to these existing nodes we need to merge
+        // those now.
+    }
+
+    @Override
+    public void visit(MoveNodeInstruction instruction) {
+        String srcPath = instruction.getSourcePath();
+        String destPath = instruction.getDestPath();
+
+        if (PathUtils.isAncestor(srcPath, destPath)) {
+            throw new RuntimeException("Target path cannot be descendant of source path: "
+                    + destPath);
+        }
+
+        String srcParentPath = PathUtils.getParentPath(srcPath);
+        String srcNodeName = PathUtils.getName(srcPath);
+
+        String destParentPath = PathUtils.getParentPath(destPath);
+        String destNodeName = PathUtils.getName(destPath);
+
+        // Add the old node with the new path.
+        NodeMongo destNode = pathNodeMap.get(destPath);
+        if (destNode == null) {
+            NodeMongo srcNode = getStoredNode(srcPath);
+            destNode = srcNode;
+            destNode.setPath(destPath);
+            pathNodeMap.put(destPath, destNode);
+        }
+
+        // Remove from srcParent - [Mete] What if there is no such child?
+        NodeMongo scrParentNode = getStoredNode(srcParentPath);
+        scrParentNode.removeChild(srcNodeName);
+
+        // Add to destParent
+        NodeMongo destParentNode = getStoredNode(destParentPath);
+        if (destParentNode.childExists(destNodeName)) {
+            throw new RuntimeException("Node already exists at move destination path: " + destPath);
+        }
+        destParentNode.addChild(destNodeName);
+
+        // [Mete] Siblings?
+    }
+
+    @Override
+    public void visit(RemoveNodeInstruction instruction) {
+        String path = instruction.getPath();
+        String parentPath = PathUtils.getParentPath(path);
+        NodeMongo parentNode = getStagedNode(parentPath);
+        // [Mete] What if there is no such child?
+        parentNode.removeChild(PathUtils.getName(path));
+    }
+
+    @Override
+    public void visit(SetPropertyInstruction instruction) {
+        String path = instruction.getPath();
+        String key = instruction.getKey();
+        Object value = instruction.getValue();
+        NodeMongo node = getStagedNode(path);
+        if (value == null) {
+            node.removeProp(key);
+        } else {
+            node.addProperty(key, value);
+        }
+    }
+
+    // TODO - [Mete] I think we need a way to distinguish between Staged
+    // and Stored nodes. For example, what if a node is retrieved as Staged
+    // but later it needs to be retrieved as Stored?
+    private NodeMongo getStagedNode(String path) {
+        NodeMongo node = pathNodeMap.get(path);
+        if (node == null) {
+            node = new NodeMongo();
+            node.setPath(path);
+            pathNodeMap.put(path, node);
+        }
+        return node;
+    }
+
+    private NodeMongo getStoredNode(String path) {
+        NodeMongo node = pathNodeMap.get(path);
+        if (node == null) {
+            FetchNodeByPathQuery query = new FetchNodeByPathQuery(mongoConnection,
+                    path, headRevisionId);
+            query.setFetchAll(true);
+            node = query.execute();
+            if (node != null) {
+                node.removeField("_id");
+                pathNodeMap.put(path, node);
+            }
+        }
+        return node;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitCommandInstructionVisitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,155 @@
+/*
+ * 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.model;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.jackrabbit.mongomk.api.model.Commit;
+import org.apache.jackrabbit.mongomk.api.model.Instruction;
+import org.apache.jackrabbit.mongomk.api.model.Instruction.AddNodeInstruction;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+import com.mongodb.BasicDBObject;
+
+/**
+ * The {@code MongoDB} representation of a commit.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class CommitMongo extends BasicDBObject {
+
+    public static final String KEY_AFFECTED_PATH = "affPaths";
+    public static final String KEY_BASE_REVISION_ID = "baseRevId";
+    public static final String KEY_DIFF = "diff";
+    public static final String KEY_FAILED = "failed";
+    public static final String KEY_MESSAGE = "msg";
+    public static final String KEY_PATH = "path";
+    public static final String KEY_REVISION_ID = "revId";
+    public static final String KEY_TIMESTAMP = "ts";
+    private static final long serialVersionUID = 6656294757102309827L;
+
+    public static CommitMongo fromCommit(Commit commit) {
+        CommitMongo commitMongo = new CommitMongo();
+
+        String message = commit.getMessage();
+        commitMongo.setMessage(message);
+
+        String path = commit.getPath();
+        commitMongo.setPath(path);
+
+        String diff = commit.getDiff();
+        commitMongo.setDiff(diff);
+
+        String revisionId = commit.getRevisionId();
+        if (revisionId != null) {
+            commitMongo.setRevisionId(revisionId);
+        }
+
+        commitMongo.setTimestamp(commit.getTimestamp());
+
+        Set<String> affectedPaths = new HashSet<String>();
+        for (Instruction instruction : commit.getInstructions()) {
+            affectedPaths.add(instruction.getPath());
+
+            if (instruction instanceof AddNodeInstruction) {
+                affectedPaths.add(PathUtils.getParentPath(instruction.getPath()));
+            }
+        }
+        commitMongo.setAffectedPaths(new LinkedList<String>(affectedPaths));
+
+        return commitMongo;
+    }
+
+    public CommitMongo() {
+        setTimestamp(new Date().getTime());
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<String> getAffectedPaths() {
+        return (List<String>) this.get(KEY_AFFECTED_PATH);
+    }
+
+    public long getBaseRevisionId() {
+        return getLong(KEY_BASE_REVISION_ID);
+    }
+
+    public String getDiff() {
+        return getString(KEY_DIFF);
+    }
+
+    public String getMessage() {
+        return getString(KEY_MESSAGE);
+    }
+
+    public String getPath() {
+        return getString(KEY_PATH);
+    }
+
+    public long getRevisionId() {
+        return getLong(KEY_REVISION_ID);
+    }
+
+    public boolean hasFailed() {
+        return this.getBoolean(KEY_FAILED);
+    }
+
+    public void setAffectedPaths(List<String> affectedPaths) {
+        put(KEY_AFFECTED_PATH, affectedPaths);
+    }
+
+    public void setBaseRevId(long baseRevisionId) {
+        put(KEY_BASE_REVISION_ID, baseRevisionId);
+    }
+
+    public void setDiff(String diff) {
+        put(KEY_DIFF, diff);
+    }
+
+    public void setFailed() {
+        put(KEY_FAILED, Boolean.TRUE);
+    }
+
+    public void setMessage(String message) {
+        put(KEY_MESSAGE, message);
+    }
+
+    public void setPath(String path) {
+        put(KEY_PATH, path);
+    }
+
+    public void setRevisionId(long revisionId) {
+        put(KEY_REVISION_ID, revisionId);
+    }
+
+    public void setRevisionId(String revisionId) {
+        this.setRevisionId(MongoUtil.toMongoRepresentation(revisionId));
+    }
+
+    public void setTimestamp(long timestamp) {
+        put(KEY_TIMESTAMP, timestamp);
+    }
+
+    public Long getTimestamp() {
+        return getLong(KEY_TIMESTAMP);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/CommitMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/HeadMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/HeadMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/HeadMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/HeadMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,60 @@
+/*
+ * 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.model;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+
+/**
+ * The {@code MongoDB} representation of the head revision.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class HeadMongo extends BasicDBObject {
+
+    public static final String KEY_HEAD_REVISION_ID = "headRevId";
+    public static final String KEY_NEXT_REVISION_ID = "nextRevId";
+    private static final long serialVersionUID = 3541425042129003691L;
+
+    public static HeadMongo fromDBObject(DBObject dbObject) {
+        HeadMongo headMongo = new HeadMongo();
+        headMongo.putAll(dbObject);
+
+        return headMongo;
+    }
+
+    public static long toDBRepresentation(String revisionId) {
+        return Long.valueOf(revisionId);
+    }
+
+    public long getHeadRevisionId() {
+        return getLong(KEY_HEAD_REVISION_ID);
+    }
+
+    public long getNextRevisionId() {
+        return getLong(KEY_NEXT_REVISION_ID);
+    }
+
+    public void setHeadRevisionId(long revisionId) {
+        put(KEY_HEAD_REVISION_ID, revisionId);
+    }
+
+    public void setNextRevisionId(long revisionId) {
+        put(KEY_NEXT_REVISION_ID, revisionId);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/HeadMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/NodeMongo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/NodeMongo.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/NodeMongo.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/NodeMongo.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,270 @@
+/*
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.mongomk.api.model.Node;
+import org.apache.jackrabbit.mongomk.impl.model.NodeImpl;
+import org.apache.jackrabbit.mongomk.util.MongoUtil;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+
+/**
+ * The {@code MongoDB} representation of a node.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+@SuppressWarnings("javadoc")
+public class NodeMongo extends BasicDBObject {
+
+    public static final String KEY_BASE_REVISION_ID = "baseRevId";
+    public static final String KEY_CHILDREN = "kids";
+    public static final String KEY_PATH = "path";
+    public static final String KEY_PROPERTIES = "props";
+    public static final String KEY_REVISION_ID = "revId";
+    private static final long serialVersionUID = 3153393934945155106L;
+
+    public static NodeMongo fromDBObject(DBObject node) {
+        NodeMongo nodeMongo = new NodeMongo();
+        nodeMongo.putAll(node);
+
+        return nodeMongo;
+    }
+
+    public static NodeMongo fromNode(Node node) {
+        NodeMongo nodeMongo = new NodeMongo();
+
+        String path = node.getPath();
+        nodeMongo.setPath(path);
+
+        String revisionId = node.getRevisionId();
+        if (revisionId != null) {
+            nodeMongo.setRevisionId(revisionId);
+        }
+
+        Map<String, Object> properties = node.getProperties();
+        if (properties != null) {
+            nodeMongo.setProperties(properties);
+        }
+
+        Set<Node> children = node.getChildren();
+        if (children != null) {
+            List<String> childNames = new LinkedList<String>();
+            for (Node child : children) {
+                childNames.add(child.getName());
+            }
+            nodeMongo.setChildren(childNames);
+        }
+
+        return nodeMongo;
+    }
+
+    public static Set<NodeMongo> fromNodes(Collection<Node> nodes) {
+        Set<NodeMongo> nodeMongos = new HashSet<NodeMongo>(nodes.size());
+        for (Node node : nodes) {
+            NodeMongo nodeMongo = NodeMongo.fromNode(node);
+            nodeMongos.add(nodeMongo);
+        }
+
+        return nodeMongos;
+    }
+
+    public static List<Node> toNode(Collection<NodeMongo> nodeMongos) {
+        List<Node> nodes = new ArrayList<Node>(nodeMongos.size());
+        for (NodeMongo nodeMongo : nodeMongos) {
+            Node node = NodeMongo.toNode(nodeMongo);
+            nodes.add(node);
+        }
+
+        return nodes;
+    }
+
+    public static NodeImpl toNode(NodeMongo nodeMongo) {
+        String revisionId = String.valueOf(nodeMongo.getRevisionId());
+        String path = nodeMongo.getPath();
+        List<String> childNames = nodeMongo.getChildren();
+        long childCount = childNames != null ? childNames.size() : 0;
+        Map<String, Object> properties = nodeMongo.getProperties();
+        Set<Node> children = null;
+        if (childNames != null) {
+            children = new HashSet<Node>();
+            for (String childName : childNames) {
+                NodeImpl child = new NodeImpl();
+                child.setPath(PathUtils.concat(path, childName));
+                children.add(child);
+            }
+        }
+
+        NodeImpl nodeImpl = new NodeImpl();
+        nodeImpl.setPath(path);
+        nodeImpl.setChildCount(childCount);
+        nodeImpl.setRevisionId(revisionId);
+        nodeImpl.setProperties(properties);
+        nodeImpl.setChildren(children);
+
+        return nodeImpl;
+    }
+
+    private List<String> addedChildren;
+    private Map<String, Object> addedProps;
+    private List<String> removedChildren;
+    private Map<String, Object> removedProps;
+
+    public void addChild(String childName) {
+        if (addedChildren == null) {
+            addedChildren = new LinkedList<String>();
+        }
+
+        addedChildren.add(childName);
+    }
+
+    public void addProperty(String key, Object value) {
+        if (addedProps == null) {
+            addedProps = new HashMap<String, Object>();
+        }
+
+        addedProps.put(key, value);
+    }
+
+    public List<String> getAddedChildren() {
+        return addedChildren;
+    }
+
+    public Map<String, Object> getAddedProps() {
+        return addedProps;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<String> getChildren() {
+        return (List<String>) this.get(KEY_CHILDREN);
+    }
+
+    public boolean childExists(String childName) {
+        List<String> children = getChildren();
+        if (children != null && !children.isEmpty()) {
+            if (children.contains(childName)) {
+                return true;
+            }
+        }
+        return addedChildExists(childName);
+    }
+
+    private boolean addedChildExists(String childName) {
+        return addedChildren != null && !addedChildren.isEmpty()?
+                addedChildren.contains(childName) : false;
+    }
+
+    public String getName() {
+        return PathUtils.getName(getString(KEY_PATH));
+    }
+
+    public String getPath() {
+        return getString(KEY_PATH);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getProperties() {
+        return (Map<String, Object>) this.get(KEY_PROPERTIES);
+    }
+
+    public List<String> getRemovedChildren() {
+        return removedChildren;
+    }
+
+    public Map<String, Object> getRemovedProps() {
+        return removedProps;
+    }
+
+    public Long getRevisionId() {
+        return getLong(KEY_REVISION_ID);
+    }
+
+    public void removeChild(String childName) {
+        if (removedChildren == null) {
+            removedChildren = new LinkedList<String>();
+        }
+
+        removedChildren.add(childName);
+    }
+
+    public void removeProp(String key) {
+        if (removedProps == null) {
+            removedProps = new HashMap<String, Object>();
+        }
+
+        removedProps.put(key, null);
+    }
+
+    public void setBaseRevisionId(long baseRevisionId) {
+        put(KEY_BASE_REVISION_ID, baseRevisionId);
+    }
+
+    public void setChildren(List<String> children) {
+        if (children != null) {
+            put(KEY_CHILDREN, children);
+        } else {
+            removeField(KEY_CHILDREN);
+        }
+    }
+
+    public void setPath(String path) {
+        put(KEY_PATH, path);
+    }
+
+    public void setProperties(Map<String, Object> properties) {
+        if (properties != null) {
+            put(KEY_PROPERTIES, properties);
+        } else {
+            removeField(KEY_PROPERTIES);
+        }
+    }
+
+    public void setRevisionId(long revisionId) {
+        put(KEY_REVISION_ID, revisionId);
+    }
+
+    public void setRevisionId(String revisionId) {
+        this.setRevisionId(MongoUtil.toMongoRepresentation(revisionId));
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString());
+        sb.append(" internal props: ");
+        sb.append("AddedChildren = ");
+        sb.append(addedChildren);
+        sb.append(", RemovedChildren = ");
+        sb.append(removedChildren);
+        sb.append(", AddedProps = ");
+        sb.append(addedProps);
+        sb.append(", RemovedProps = ");
+        sb.append(removedProps);
+
+        return sb.toString();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/model/NodeMongo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/AbstractQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/AbstractQuery.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/AbstractQuery.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/AbstractQuery.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,52 @@
+/*
+ * 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.query;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+
+/**
+ * An abstract base class for queries performed with {@code MongoDB}.
+ *
+ * @param <T>
+ *            The result type of the query.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public abstract class AbstractQuery<T> {
+
+    /** The {@link MongoConnection}. */
+    protected MongoConnection mongoConnection;
+
+    /**
+     * Constructs a new {@code AbstractQuery}.
+     *
+     * @param mongoConnection
+     *            The {@link MongoConnection}.
+     */
+    protected AbstractQuery(MongoConnection mongoConnection) {
+        this.mongoConnection = mongoConnection;
+    }
+
+    /**
+     * Executes this query.
+     *
+     * @return The result of the query.
+     * @throws Exception
+     *             If an error occurred while executing the query.
+     */
+    public abstract T execute() throws Exception;
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/AbstractQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadQuery.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadQuery.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadQuery.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,48 @@
+/*
+ * 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.query;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.model.HeadMongo;
+
+import com.mongodb.DBCollection;
+
+/**
+ * An query for fetching the current head.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class FetchHeadQuery extends AbstractQuery<HeadMongo> {
+
+    /**
+     * Constructs a new {@code FetchHeadQuery}.
+     *
+     * @param mongoConnection
+     *            The {@link MongoConnection}.
+     */
+    public FetchHeadQuery(MongoConnection mongoConnection) {
+        super(mongoConnection);
+    }
+
+    @Override
+    public HeadMongo execute() throws Exception {
+        DBCollection headCollection = mongoConnection.getHeadCollection();
+        HeadMongo headMongo = (HeadMongo) headCollection.findOne();
+
+        return headMongo;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadRevisionQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadRevisionQuery.java?rev=1386591&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadRevisionQuery.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadRevisionQuery.java Mon Sep 17 12:54:01 2012
@@ -0,0 +1,51 @@
+/*
+ * 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.query;
+
+import org.apache.jackrabbit.mongomk.MongoConnection;
+import org.apache.jackrabbit.mongomk.model.HeadMongo;
+
+
+/**
+ * An query for fetching the head revision.
+ *
+ * @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
+ */
+public class FetchHeadRevisionQuery extends AbstractQuery<Long> {
+
+    /**
+     * Constructs a new {@code FetchHeadRevisionQuery}.
+     *
+     * @param mongoConnection
+     *            The {@link MongoConnection}.
+     */
+    public FetchHeadRevisionQuery(MongoConnection mongoConnection) {
+        super(mongoConnection);
+    }
+
+    @Override
+    public Long execute() throws Exception {
+        HeadMongo headMongo = fetchHead();
+        long headRevision = headMongo.getHeadRevisionId();
+
+        return headRevision;
+    }
+
+    private HeadMongo fetchHead() throws Exception {
+        return new FetchHeadQuery(mongoConnection).execute();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/query/FetchHeadRevisionQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native