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 st...@apache.org on 2012/04/08 22:31:51 UTC

svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Author: stefan
Date: Sun Apr  8 20:31:50 2012
New Revision: 1311085

URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
Log:
OAK-43: Incomplete journal when move and copy operations are involved

Modified:
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java Sun Apr  8 20:31:50 2012
@@ -18,9 +18,7 @@ package org.apache.jackrabbit.mk.core;
 
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -31,6 +29,7 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.mk.model.ChildNodeEntry;
 import org.apache.jackrabbit.mk.model.Commit;
 import org.apache.jackrabbit.mk.model.CommitBuilder;
+import org.apache.jackrabbit.mk.model.CommitBuilder.NodeTree;
 import org.apache.jackrabbit.mk.model.Id;
 import org.apache.jackrabbit.mk.model.NodeState;
 import org.apache.jackrabbit.mk.model.PropertyState;
@@ -40,7 +39,6 @@ import org.apache.jackrabbit.mk.store.No
 import org.apache.jackrabbit.mk.store.RevisionProvider;
 import org.apache.jackrabbit.mk.util.CommitGate;
 import org.apache.jackrabbit.mk.util.PathUtils;
-import org.apache.jackrabbit.mk.util.SimpleLRUCache;
 
 /**
  *
@@ -49,11 +47,6 @@ public class MicroKernelImpl implements 
 
     protected Repository rep;
     private final CommitGate gate = new CommitGate();
-    
-    /**
-     * Key: revision id, Value: diff string
-     */
-    private final Map<Id, String> diffCache = Collections.synchronizedMap(SimpleLRUCache.<Id, String>newInstance(100));
 
     public MicroKernelImpl(String homeDir) throws MicroKernelException {
         init(homeDir);
@@ -97,7 +90,6 @@ public class MicroKernelImpl implements 
             }
             rep = null;
         }
-        diffCache.clear();
     }
 
     public String getHeadRevision() throws MicroKernelException {
@@ -211,13 +203,8 @@ public class MicroKernelImpl implements 
             commitBuff.object().
                     key("id").value(commit.getId().toString()).
                     key("ts").value(commit.getCommitTS()).
-                    key("msg").value(commit.getMsg());
-            String diff = diffCache.get(commit.getId());
-            if (diff == null) {
-                diff = diff(commit.getParentId(), commit.getId(), filter);
-                diffCache.put(commit.getId(), diff);
-            }
-            commitBuff.key("changes").value(diff).endObject();
+                    key("msg").value(commit.getMsg()).
+                    key("changes").value(commit.getChanges()).endObject();
         }
         return commitBuff.endArray().toString();
     }
@@ -478,12 +465,7 @@ public class MicroKernelImpl implements 
                             }
                             String parentPath = PathUtils.getParentPath(nodePath);
                             String nodeName = PathUtils.getName(nodePath);
-                            // build the list of added nodes recursively
-                            LinkedList<AddNodeOperation> list = new LinkedList<AddNodeOperation>();
-                            addNode(list, parentPath, nodeName, t);
-                            for (AddNodeOperation op : list) {
-                                cb.addNode(op.path, op.name, op.props);
-                            }
+                            cb.addNode(parentPath, nodeName, parseNode(t));
                         } else {
                             String value;
                             if (t.matches(JsopTokenizer.NULL)) {
@@ -637,30 +619,20 @@ public class MicroKernelImpl implements 
         }
     }
     
-    static void addNode(LinkedList<AddNodeOperation> list, String path, String name, JsopTokenizer t) throws Exception {
-        AddNodeOperation op = new AddNodeOperation();
-        op.path = path;
-        op.name = name;
-        list.add(op);
+    NodeTree parseNode(JsopTokenizer t) throws Exception {
+        NodeTree node = new NodeTree();
         if (!t.matches('}')) {
             do {
                 String key = t.readString();
                 t.read(':');
                 if (t.matches('{')) {
-                    addNode(list, PathUtils.concat(path, name), key, t);
+                    node.nodes.put(key, parseNode(t));
                 } else {
-                    op.props.put(key, t.readRawValue().trim());
+                    node.props.put(key, t.readRawValue().trim());
                 }
             } while (t.matches(','));
             t.read('}');
         }
+        return node;
     }
-
-    //--------------------------------------------------------< inner classes >
-    static class AddNodeOperation {
-        String path;
-        String name;
-        Map<String, String> props = new HashMap<String, String>();
-    }
-
 }

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java Sun Apr  8 20:31:50 2012
@@ -32,6 +32,9 @@ public abstract class AbstractCommit imp
     // commit message
     protected String msg;
 
+    // changes
+    protected String changes;
+
     // id of parent commit
     protected Id parentId;
 
@@ -42,6 +45,7 @@ public abstract class AbstractCommit imp
         this.parentId = other.getParentId();
         this.rootNodeId = other.getRootNodeId();
         this.msg = other.getMsg();
+        this.changes = other.getChanges();
         this.commitTS = other.getCommitTS();
     }
 
@@ -61,10 +65,15 @@ public abstract class AbstractCommit imp
         return msg;
     }
 
+    public String getChanges() {
+        return changes;
+    }
+
     public void serialize(Binding binding) throws Exception {
         binding.write("rootNodeId", rootNodeId.getBytes());
         binding.write("commitTS", commitTS);
         binding.write("msg", msg == null ? "" : msg);
+        binding.write("changes", changes == null ? "" : changes);
         binding.write("parentId", parentId == null ? "" : parentId.toString());
     }
 }

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java Sun Apr  8 20:31:50 2012
@@ -31,5 +31,7 @@ public interface Commit {
 
     public String getMsg();
 
+    public String getChanges();
+
     void serialize(Binding binding) throws Exception;
 }

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java Sun Apr  8 20:31:50 2012
@@ -50,130 +50,39 @@ public class CommitBuilder {
         this.store = store;
     }
 
-    public void addNode(String parentNodePath, String nodeName) throws Exception {
-        addNode(parentNodePath, nodeName, Collections.<String, String>emptyMap());
-    }
-
-    public void addNode(String parentNodePath, String nodeName, Map<String, String> properties) throws Exception {
-        MutableNode modParent = getOrCreateStagedNode(parentNodePath);
-        if (modParent.getChildNodeEntry(nodeName) != null) {
-            throw new Exception("there's already a child node with name '" + nodeName + "'");
-        }
-        String newPath = PathUtils.concat(parentNodePath, nodeName);
-        MutableNode newChild = new MutableNode(store, newPath);
-        newChild.getProperties().putAll(properties);
-
-        // id will be computed on commit
-        modParent.add(new ChildNode(nodeName, null));
-        staged.put(newPath, newChild);
+    public void addNode(String parentNodePath, String nodeName, NodeTree node) throws Exception {
+        Change change = new AddNode(parentNodePath, nodeName, node);
+        change.apply();
         // update change log
-        changeLog.add(new AddNode(parentNodePath, nodeName, properties));
+        changeLog.add(change);
     }
 
     public void removeNode(String nodePath) throws NotFoundException, Exception {
-        String parentPath = PathUtils.getParentPath(nodePath);
-        String nodeName = PathUtils.getName(nodePath);
-
-        MutableNode parent = getOrCreateStagedNode(parentPath);
-        if (parent.remove(nodeName) == null) {
-            throw new NotFoundException(nodePath);
-        }
-
-        // update staging area
-        removeStagedNodes(nodePath);
-
+        Change change = new RemoveNode(nodePath);
+        change.apply();
         // update change log
-        changeLog.add(new RemoveNode(nodePath));
+        changeLog.add(change);
     }
 
     public void moveNode(String srcPath, String destPath) throws NotFoundException, Exception {
-        if (PathUtils.isAncestor(srcPath, destPath)) {
-            throw new Exception("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);
-
-        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
-        if (srcParentPath.equals(destParentPath)) {
-            if (srcParent.getChildNodeEntry(destNodeName) != null) {
-                throw new Exception("node already exists at move destination path: " + destPath);
-            }
-            if (srcParent.rename(srcNodeName, destNodeName) == null) {
-                throw new NotFoundException(srcPath);
-            }
-        } else {
-            ChildNode srcCNE = srcParent.remove(srcNodeName);
-            if (srcCNE == null) {
-                throw new NotFoundException(srcPath);
-            }
-
-            MutableNode destParent = getOrCreateStagedNode(destParentPath);
-            if (destParent.getChildNodeEntry(destNodeName) != null) {
-                throw new Exception("node already exists at move destination path: " + destPath);
-            }
-            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
-        }
-
-        // update staging area
-        moveStagedNodes(srcPath, destPath);
-
+        Change change = new MoveNode(srcPath, destPath);
+        change.apply();
         // update change log
-        changeLog.add(new MoveNode(srcPath, destPath));
+        changeLog.add(change);
     }
 
     public void copyNode(String srcPath, String destPath) throws NotFoundException, Exception {
-        String srcParentPath = PathUtils.getParentPath(srcPath);
-        String srcNodeName = PathUtils.getName(srcPath);
-
-        String destParentPath = PathUtils.getParentPath(destPath);
-        String destNodeName = PathUtils.getName(destPath);
-
-        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
-        ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
-        if (srcCNE == null) {
-            throw new NotFoundException(srcPath);
-        }
-
-        MutableNode destParent = getOrCreateStagedNode(destParentPath);
-        destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
-
-        if (srcCNE.getId() == null) {
-            // a 'new' node is being copied
-
-            // update staging area
-            copyStagedNodes(srcPath, destPath);
-        }
-
+        Change change = new CopyNode(srcPath, destPath);
+        change.apply();
         // update change log
-        changeLog.add(new CopyNode(srcPath, destPath));
+        changeLog.add(change);
     }
 
     public void setProperty(String nodePath, String propName, String propValue) throws Exception {
-        MutableNode node = getOrCreateStagedNode(nodePath);
-
-        Map<String, String> properties = node.getProperties();
-        if (propValue == null) {
-            properties.remove(propName);
-        } else {
-            properties.put(propName, propValue);
-        }
-
-        // update change log
-        changeLog.add(new SetProperty(nodePath, propName, propValue));
-    }
-
-    public void setProperties(String nodePath, Map<String, String> properties) throws Exception {
-        MutableNode node = getOrCreateStagedNode(nodePath);
-
-        node.getProperties().clear();
-        node.getProperties().putAll(properties);
-
+        Change change = new SetProperty(nodePath, propName, propValue);
+        change.apply();
         // update change log
-        changeLog.add(new SetProperties(nodePath, properties));
+        changeLog.add(change);
     }
 
     public Id /* new revId */ doCommit() throws Exception {
@@ -190,9 +99,7 @@ public class CommitBuilder {
             // clear staging area
             staged.clear();
             // replay change log on new base revision
-            // copy log in order to avoid concurrent modifications
-            List<Change> log = new ArrayList<Change>(changeLog);
-            for (Change change : log) {
+            for (Change change : changeLog) {
                 change.apply();
             }
         }
@@ -222,19 +129,29 @@ public class CommitBuilder {
             newCommit.setParentId(baseRevId);
             newCommit.setCommitTS(System.currentTimeMillis());
             newCommit.setMsg(msg);
+            StringBuilder diff = new StringBuilder();
+            for (Change change : changeLog) {
+                if (diff.length() > 0) {
+                    diff.append('\n');
+                }
+                diff.append(change.asDiff());
+            }
+            newCommit.setChanges(diff.toString());
             newCommit.setRootNodeId(rootNodeId);
             newRevId = store.putHeadCommit(newCommit);
         } finally {
             store.unlockHead();
         }
 
-        // reset instance in order to be reusable
+        // reset instance
         staged.clear();
         changeLog.clear();
 
         return newRevId;
     }
 
+    //--------------------------------------------------------< inner classes >
+
     MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
         MutableNode node = staged.get(nodePath);
         if (node == null) {
@@ -418,23 +335,79 @@ public class CommitBuilder {
     }
 
     //--------------------------------------------------------< inner classes >
+
+    public static class NodeTree {
+        public Map<String, String> props = new HashMap<String, String>();
+        public Map<String, NodeTree> nodes = new HashMap<String, NodeTree>();
+
+        void toJson(StringBuffer buf) {
+            toJson(buf, this);
+        }
+
+        private static void toJson(StringBuffer buf, NodeTree node) {
+            buf.append('{');
+            for (String name : node.props.keySet()) {
+                if (buf.charAt(buf.length() - 1) != '{')  {
+                    buf.append(',');
+                }
+                buf.append('"').append(name).append("\":").append(node.props.get(name));
+            }
+            for (String name : node.nodes.keySet()) {
+                if (buf.charAt(buf.length() - 1) != '{')  {
+                    buf.append(',');
+                }
+                buf.append('"').append(name).append("\":");
+                toJson(buf, node.nodes.get(name));
+            }
+            buf.append('}');
+        }
+    }
+
     abstract class Change {
         abstract void apply() throws Exception;
+        abstract String asDiff();
     }
 
     class AddNode extends Change {
         String parentNodePath;
         String nodeName;
-        Map<String, String> properties;
+        NodeTree node;
 
-        AddNode(String parentNodePath, String nodeName, Map<String, String> properties) {
+        AddNode(String parentNodePath, String nodeName, NodeTree node) {
             this.parentNodePath = parentNodePath;
             this.nodeName = nodeName;
-            this.properties = properties;
+            this.node = node;
         }
 
+        @Override
         void apply() throws Exception {
-            addNode(parentNodePath, nodeName, properties);
+            recursiveAddNode(parentNodePath, nodeName, node);
+        }
+
+        @Override
+        String asDiff() {
+            StringBuffer diff = new StringBuffer("+");
+            diff.append('"').append(PathUtils.concat(parentNodePath, nodeName)).append("\":");
+            node.toJson(diff);
+            return diff.toString();
+        }
+
+        private void recursiveAddNode(String parentPath, String name, NodeTree node) throws Exception {
+            MutableNode modParent = getOrCreateStagedNode(parentPath);
+            if (modParent.getChildNodeEntry(name) != null) {
+                throw new Exception("there's already a child node with name '" + name + "'");
+            }
+            String newPath = PathUtils.concat(parentPath, name);
+            MutableNode newChild = new MutableNode(store, newPath);
+            newChild.getProperties().putAll(node.props);
+
+            // id will be computed on commit
+            modParent.add(new ChildNode(name, null));
+            staged.put(newPath, newChild);
+
+            for (String childName : node.nodes.keySet()) {
+                recursiveAddNode(PathUtils.concat(parentPath, name), childName, node.nodes.get(childName));
+            }
         }
     }
 
@@ -445,8 +418,25 @@ public class CommitBuilder {
             this.nodePath = nodePath;
         }
 
+        @Override
         void apply() throws Exception {
-            removeNode(nodePath);
+            String parentPath = PathUtils.getParentPath(nodePath);
+            String nodeName = PathUtils.getName(nodePath);
+
+            MutableNode parent = getOrCreateStagedNode(parentPath);
+            if (parent.remove(nodeName) == null) {
+                throw new NotFoundException(nodePath);
+            }
+
+            // update staging area
+            removeStagedNodes(nodePath);
+        }
+
+        @Override
+        String asDiff() {
+            StringBuffer diff = new StringBuffer("-");
+            diff.append('"').append(nodePath).append('"');
+            return diff.toString();
         }
     }
 
@@ -459,8 +449,48 @@ public class CommitBuilder {
             this.destPath = destPath;
         }
 
+        @Override
         void apply() throws Exception {
-            moveNode(srcPath, destPath);
+            if (PathUtils.isAncestor(srcPath, destPath)) {
+                throw new Exception("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);
+
+            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
+            if (srcParentPath.equals(destParentPath)) {
+                if (srcParent.getChildNodeEntry(destNodeName) != null) {
+                    throw new Exception("node already exists at move destination path: " + destPath);
+                }
+                if (srcParent.rename(srcNodeName, destNodeName) == null) {
+                    throw new NotFoundException(srcPath);
+                }
+            } else {
+                ChildNode srcCNE = srcParent.remove(srcNodeName);
+                if (srcCNE == null) {
+                    throw new NotFoundException(srcPath);
+                }
+
+                MutableNode destParent = getOrCreateStagedNode(destParentPath);
+                if (destParent.getChildNodeEntry(destNodeName) != null) {
+                    throw new Exception("node already exists at move destination path: " + destPath);
+                }
+                destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
+            }
+
+            // update staging area
+            moveStagedNodes(srcPath, destPath);
+        }
+
+        @Override
+        String asDiff() {
+            StringBuffer diff = new StringBuffer(">");
+            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
+            return diff.toString();
         }
     }
 
@@ -473,8 +503,36 @@ public class CommitBuilder {
             this.destPath = destPath;
         }
 
+        @Override
         void apply() throws Exception {
-            copyNode(srcPath, destPath);
+            String srcParentPath = PathUtils.getParentPath(srcPath);
+            String srcNodeName = PathUtils.getName(srcPath);
+
+            String destParentPath = PathUtils.getParentPath(destPath);
+            String destNodeName = PathUtils.getName(destPath);
+
+            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
+            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
+            if (srcCNE == null) {
+                throw new NotFoundException(srcPath);
+            }
+
+            MutableNode destParent = getOrCreateStagedNode(destParentPath);
+            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
+
+            if (srcCNE.getId() == null) {
+                // a 'new' node is being copied
+
+                // update staging area
+                copyStagedNodes(srcPath, destPath);
+            }
+        }
+
+        @Override
+        String asDiff() {
+            StringBuffer diff = new StringBuffer("*");
+            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
+            return diff.toString();
         }
     }
 
@@ -489,22 +547,23 @@ public class CommitBuilder {
             this.propValue = propValue;
         }
 
+        @Override
         void apply() throws Exception {
-            setProperty(nodePath, propName, propValue);
-        }
-    }
+            MutableNode node = getOrCreateStagedNode(nodePath);
 
-    class SetProperties extends Change {
-        String nodePath;
-        Map<String, String> properties;
-
-        SetProperties(String nodePath, Map<String, String> properties) {
-            this.nodePath = nodePath;
-            this.properties = properties;
+            Map<String, String> properties = node.getProperties();
+            if (propValue == null) {
+                properties.remove(propName);
+            } else {
+                properties.put(propName, propValue);
+            }
         }
 
-        void apply() throws Exception {
-            setProperties(nodePath, properties);
+        @Override
+        String asDiff() {
+            StringBuffer diff = new StringBuffer("^");
+            diff.append('"').append(PathUtils.concat(nodePath, propName)).append("\":").append(propValue);
+            return diff.toString();
         }
     }
 }

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java Sun Apr  8 20:31:50 2012
@@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
         setRootNodeId(other.getRootNodeId());
         setCommitTS(other.getCommitTS());
         setMsg(other.getMsg());
+        setChanges(other.getChanges());
         this.id = other.getId();
     }
 
@@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
     public void setMsg(String msg) {
         this.msg = msg;
     }
-    
+
+    public void setChanges(String changes) {
+        this.changes = changes;
+    }
+
     /**
      * Return the commit id.
      * 

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java Sun Apr  8 20:31:50 2012
@@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
         Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
         long commitTS = binding.readLongValue("commitTS");
         String msg = binding.readStringValue("msg");
+        String changes = binding.readStringValue("changes");
         String parentId = binding.readStringValue("parentId");
         return new StoredCommit(id, "".equals(parentId) ? null : Id.fromString(parentId),
-                commitTS, rootNodeId, "".equals(msg) ? null : msg);
+                commitTS, rootNodeId, "".equals(msg) ? null : msg, changes);
     }
 
-    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg) {
+    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg, String changes) {
         this.id = id;
         this.parentId = parentId;
         this.commitTS = commitTS;
         this.rootNodeId = rootNodeId;
         this.msg = msg;
+        this.changes = changes;
     }
 
     public StoredCommit(Id id, Commit commit) {



Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Stefan Guggisberg <st...@gmail.com>.
On Mon, Apr 9, 2012 at 1:18 PM, Michael Dürig <md...@apache.org> wrote:
>
> On 8.4.12 21:31, stefan@apache.org wrote:
>>
>> Author: stefan
>> Date: Sun Apr  8 20:31:50 2012
>> New Revision: 1311085
>>
>> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
>> Log:
>> OAK-43: Incomplete journal when move and copy operations are involved
>
>
> [...]
>
>
>> @@ -222,19 +129,29 @@ public class CommitBuilder {
>>              newCommit.setParentId(baseRevId);
>>              newCommit.setCommitTS(System.currentTimeMillis());
>>              newCommit.setMsg(msg);
>> +            StringBuilder diff = new StringBuilder();
>> +            for (Change change : changeLog) {
>> +                if (diff.length()>  0) {
>> +                    diff.append('\n');
>> +                }
>> +                diff.append(change.asDiff());
>> +            }
>> +            newCommit.setChanges(diff.toString());
>
>
> Why going through all that trouble here? Why not storing the json diff
> passed to the commit method?

i thought about this but decided against it because the paths within
the passed diff might need to be normalized.
furthermore i would have to pass the raw diff to the CommitBuilder
instance which feels wrong.

>
> Michael
>
>
>
>>              newCommit.setRootNodeId(rootNodeId);
>>              newRevId = store.putHeadCommit(newCommit);
>>          } finally {
>>              store.unlockHead();
>>          }
>>
>> -        // reset instance in order to be reusable
>> +        // reset instance
>>          staged.clear();
>>          changeLog.clear();
>>
>>          return newRevId;
>>      }
>>
>> +    //--------------------------------------------------------<  inner
>> classes>
>> +
>>      MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
>>          MutableNode node = staged.get(nodePath);
>>          if (node == null) {
>> @@ -418,23 +335,79 @@ public class CommitBuilder {
>>      }
>>
>>      //--------------------------------------------------------<  inner
>> classes>
>> +
>> +    public static class NodeTree {
>> +        public Map<String, String>  props = new HashMap<String,
>> String>();
>> +        public Map<String, NodeTree>  nodes = new HashMap<String,
>> NodeTree>();
>> +
>> +        void toJson(StringBuffer buf) {
>> +            toJson(buf, this);
>> +        }
>> +
>> +        private static void toJson(StringBuffer buf, NodeTree node) {
>> +            buf.append('{');
>> +            for (String name : node.props.keySet()) {
>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>> +                    buf.append(',');
>> +                }
>> +
>>  buf.append('"').append(name).append("\":").append(node.props.get(name));
>> +            }
>> +            for (String name : node.nodes.keySet()) {
>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>> +                    buf.append(',');
>> +                }
>> +                buf.append('"').append(name).append("\":");
>> +                toJson(buf, node.nodes.get(name));
>> +            }
>> +            buf.append('}');
>> +        }
>> +    }
>> +
>>      abstract class Change {
>>          abstract void apply() throws Exception;
>> +        abstract String asDiff();
>>      }
>>
>>      class AddNode extends Change {
>>          String parentNodePath;
>>          String nodeName;
>> -        Map<String, String>  properties;
>> +        NodeTree node;
>>
>> -        AddNode(String parentNodePath, String nodeName, Map<String,
>> String>  properties) {
>> +        AddNode(String parentNodePath, String nodeName, NodeTree node) {
>>              this.parentNodePath = parentNodePath;
>>              this.nodeName = nodeName;
>> -            this.properties = properties;
>> +            this.node = node;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            addNode(parentNodePath, nodeName, properties);
>> +            recursiveAddNode(parentNodePath, nodeName, node);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("+");
>> +            diff.append('"').append(PathUtils.concat(parentNodePath,
>> nodeName)).append("\":");
>> +            node.toJson(diff);
>> +            return diff.toString();
>> +        }
>> +
>> +        private void recursiveAddNode(String parentPath, String name,
>> NodeTree node) throws Exception {
>> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
>> +            if (modParent.getChildNodeEntry(name) != null) {
>> +                throw new Exception("there's already a child node with
>> name '" + name + "'");
>> +            }
>> +            String newPath = PathUtils.concat(parentPath, name);
>> +            MutableNode newChild = new MutableNode(store, newPath);
>> +            newChild.getProperties().putAll(node.props);
>> +
>> +            // id will be computed on commit
>> +            modParent.add(new ChildNode(name, null));
>> +            staged.put(newPath, newChild);
>> +
>> +            for (String childName : node.nodes.keySet()) {
>> +                recursiveAddNode(PathUtils.concat(parentPath, name),
>> childName, node.nodes.get(childName));
>> +            }
>>          }
>>      }
>>
>> @@ -445,8 +418,25 @@ public class CommitBuilder {
>>              this.nodePath = nodePath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            removeNode(nodePath);
>> +            String parentPath = PathUtils.getParentPath(nodePath);
>> +            String nodeName = PathUtils.getName(nodePath);
>> +
>> +            MutableNode parent = getOrCreateStagedNode(parentPath);
>> +            if (parent.remove(nodeName) == null) {
>> +                throw new NotFoundException(nodePath);
>> +            }
>> +
>> +            // update staging area
>> +            removeStagedNodes(nodePath);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("-");
>> +            diff.append('"').append(nodePath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -459,8 +449,48 @@ public class CommitBuilder {
>>              this.destPath = destPath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            moveNode(srcPath, destPath);
>> +            if (PathUtils.isAncestor(srcPath, destPath)) {
>> +                throw new Exception("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);
>> +
>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> +            if (srcParentPath.equals(destParentPath)) {
>> +                if (srcParent.getChildNodeEntry(destNodeName) != null) {
>> +                    throw new Exception("node already exists at move
>> destination path: " + destPath);
>> +                }
>> +                if (srcParent.rename(srcNodeName, destNodeName) == null)
>> {
>> +                    throw new NotFoundException(srcPath);
>> +                }
>> +            } else {
>> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
>> +                if (srcCNE == null) {
>> +                    throw new NotFoundException(srcPath);
>> +                }
>> +
>> +                MutableNode destParent =
>> getOrCreateStagedNode(destParentPath);
>> +                if (destParent.getChildNodeEntry(destNodeName) != null) {
>> +                    throw new Exception("node already exists at move
>> destination path: " + destPath);
>> +                }
>> +                destParent.add(new ChildNode(destNodeName,
>> srcCNE.getId()));
>> +            }
>> +
>> +            // update staging area
>> +            moveStagedNodes(srcPath, destPath);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer(">");
>> +
>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -473,8 +503,36 @@ public class CommitBuilder {
>>              this.destPath = destPath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            copyNode(srcPath, destPath);
>> +            String srcParentPath = PathUtils.getParentPath(srcPath);
>> +            String srcNodeName = PathUtils.getName(srcPath);
>> +
>> +            String destParentPath = PathUtils.getParentPath(destPath);
>> +            String destNodeName = PathUtils.getName(destPath);
>> +
>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> +            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>> +            if (srcCNE == null) {
>> +                throw new NotFoundException(srcPath);
>> +            }
>> +
>> +            MutableNode destParent =
>> getOrCreateStagedNode(destParentPath);
>> +            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>> +
>> +            if (srcCNE.getId() == null) {
>> +                // a 'new' node is being copied
>> +
>> +                // update staging area
>> +                copyStagedNodes(srcPath, destPath);
>> +            }
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("*");
>> +
>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -489,22 +547,23 @@ public class CommitBuilder {
>>              this.propValue = propValue;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            setProperty(nodePath, propName, propValue);
>> -        }
>> -    }
>> +            MutableNode node = getOrCreateStagedNode(nodePath);
>>
>> -    class SetProperties extends Change {
>> -        String nodePath;
>> -        Map<String, String>  properties;
>> -
>> -        SetProperties(String nodePath, Map<String, String>  properties) {
>> -            this.nodePath = nodePath;
>> -            this.properties = properties;
>> +            Map<String, String>  properties = node.getProperties();
>> +            if (propValue == null) {
>> +                properties.remove(propName);
>> +            } else {
>> +                properties.put(propName, propValue);
>> +            }
>>          }
>>
>> -        void apply() throws Exception {
>> -            setProperties(nodePath, properties);
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("^");
>> +            diff.append('"').append(PathUtils.concat(nodePath,
>> propName)).append("\":").append(propValue);
>> +            return diff.toString();
>>          }
>>      }
>>  }
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>>          setRootNodeId(other.getRootNodeId());
>>          setCommitTS(other.getCommitTS());
>>          setMsg(other.getMsg());
>> +        setChanges(other.getChanges());
>>          this.id = other.getId();
>>      }
>>
>> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>>      public void setMsg(String msg) {
>>          this.msg = msg;
>>      }
>> -
>> +
>> +    public void setChanges(String changes) {
>> +        this.changes = changes;
>> +    }
>> +
>>      /**
>>       * Return the commit id.
>>       *
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>>          Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>>          long commitTS = binding.readLongValue("commitTS");
>>          String msg = binding.readStringValue("msg");
>> +        String changes = binding.readStringValue("changes");
>>          String parentId = binding.readStringValue("parentId");
>>          return new StoredCommit(id, "".equals(parentId) ? null :
>> Id.fromString(parentId),
>> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
>> +                commitTS, rootNodeId, "".equals(msg) ? null : msg,
>> changes);
>>      }
>>
>> -    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>> String msg) {
>> +    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>> String msg, String changes) {
>>          this.id = id;
>>          this.parentId = parentId;
>>          this.commitTS = commitTS;
>>          this.rootNodeId = rootNodeId;
>>          this.msg = msg;
>> +        this.changes = changes;
>>      }
>>
>>      public StoredCommit(Id id, Commit commit) {
>>
>>
>

Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Michael Dürig <md...@apache.org>.
On 8.4.12 21:31, stefan@apache.org wrote:
> Author: stefan
> Date: Sun Apr  8 20:31:50 2012
> New Revision: 1311085
>
> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
> Log:
> OAK-43: Incomplete journal when move and copy operations are involved

[...]

> @@ -222,19 +129,29 @@ public class CommitBuilder {
>               newCommit.setParentId(baseRevId);
>               newCommit.setCommitTS(System.currentTimeMillis());
>               newCommit.setMsg(msg);
> +            StringBuilder diff = new StringBuilder();
> +            for (Change change : changeLog) {
> +                if (diff.length()>  0) {
> +                    diff.append('\n');
> +                }
> +                diff.append(change.asDiff());
> +            }
> +            newCommit.setChanges(diff.toString());

Why going through all that trouble here? Why not storing the json diff 
passed to the commit method?

Michael


>               newCommit.setRootNodeId(rootNodeId);
>               newRevId = store.putHeadCommit(newCommit);
>           } finally {
>               store.unlockHead();
>           }
>
> -        // reset instance in order to be reusable
> +        // reset instance
>           staged.clear();
>           changeLog.clear();
>
>           return newRevId;
>       }
>
> +    //--------------------------------------------------------<  inner classes>
> +
>       MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
>           MutableNode node = staged.get(nodePath);
>           if (node == null) {
> @@ -418,23 +335,79 @@ public class CommitBuilder {
>       }
>
>       //--------------------------------------------------------<  inner classes>
> +
> +    public static class NodeTree {
> +        public Map<String, String>  props = new HashMap<String, String>();
> +        public Map<String, NodeTree>  nodes = new HashMap<String, NodeTree>();
> +
> +        void toJson(StringBuffer buf) {
> +            toJson(buf, this);
> +        }
> +
> +        private static void toJson(StringBuffer buf, NodeTree node) {
> +            buf.append('{');
> +            for (String name : node.props.keySet()) {
> +                if (buf.charAt(buf.length() - 1) != '{')  {
> +                    buf.append(',');
> +                }
> +                buf.append('"').append(name).append("\":").append(node.props.get(name));
> +            }
> +            for (String name : node.nodes.keySet()) {
> +                if (buf.charAt(buf.length() - 1) != '{')  {
> +                    buf.append(',');
> +                }
> +                buf.append('"').append(name).append("\":");
> +                toJson(buf, node.nodes.get(name));
> +            }
> +            buf.append('}');
> +        }
> +    }
> +
>       abstract class Change {
>           abstract void apply() throws Exception;
> +        abstract String asDiff();
>       }
>
>       class AddNode extends Change {
>           String parentNodePath;
>           String nodeName;
> -        Map<String, String>  properties;
> +        NodeTree node;
>
> -        AddNode(String parentNodePath, String nodeName, Map<String, String>  properties) {
> +        AddNode(String parentNodePath, String nodeName, NodeTree node) {
>               this.parentNodePath = parentNodePath;
>               this.nodeName = nodeName;
> -            this.properties = properties;
> +            this.node = node;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            addNode(parentNodePath, nodeName, properties);
> +            recursiveAddNode(parentNodePath, nodeName, node);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("+");
> +            diff.append('"').append(PathUtils.concat(parentNodePath, nodeName)).append("\":");
> +            node.toJson(diff);
> +            return diff.toString();
> +        }
> +
> +        private void recursiveAddNode(String parentPath, String name, NodeTree node) throws Exception {
> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
> +            if (modParent.getChildNodeEntry(name) != null) {
> +                throw new Exception("there's already a child node with name '" + name + "'");
> +            }
> +            String newPath = PathUtils.concat(parentPath, name);
> +            MutableNode newChild = new MutableNode(store, newPath);
> +            newChild.getProperties().putAll(node.props);
> +
> +            // id will be computed on commit
> +            modParent.add(new ChildNode(name, null));
> +            staged.put(newPath, newChild);
> +
> +            for (String childName : node.nodes.keySet()) {
> +                recursiveAddNode(PathUtils.concat(parentPath, name), childName, node.nodes.get(childName));
> +            }
>           }
>       }
>
> @@ -445,8 +418,25 @@ public class CommitBuilder {
>               this.nodePath = nodePath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            removeNode(nodePath);
> +            String parentPath = PathUtils.getParentPath(nodePath);
> +            String nodeName = PathUtils.getName(nodePath);
> +
> +            MutableNode parent = getOrCreateStagedNode(parentPath);
> +            if (parent.remove(nodeName) == null) {
> +                throw new NotFoundException(nodePath);
> +            }
> +
> +            // update staging area
> +            removeStagedNodes(nodePath);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("-");
> +            diff.append('"').append(nodePath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -459,8 +449,48 @@ public class CommitBuilder {
>               this.destPath = destPath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            moveNode(srcPath, destPath);
> +            if (PathUtils.isAncestor(srcPath, destPath)) {
> +                throw new Exception("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);
> +
> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> +            if (srcParentPath.equals(destParentPath)) {
> +                if (srcParent.getChildNodeEntry(destNodeName) != null) {
> +                    throw new Exception("node already exists at move destination path: " + destPath);
> +                }
> +                if (srcParent.rename(srcNodeName, destNodeName) == null) {
> +                    throw new NotFoundException(srcPath);
> +                }
> +            } else {
> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
> +                if (srcCNE == null) {
> +                    throw new NotFoundException(srcPath);
> +                }
> +
> +                MutableNode destParent = getOrCreateStagedNode(destParentPath);
> +                if (destParent.getChildNodeEntry(destNodeName) != null) {
> +                    throw new Exception("node already exists at move destination path: " + destPath);
> +                }
> +                destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> +            }
> +
> +            // update staging area
> +            moveStagedNodes(srcPath, destPath);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer(">");
> +            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -473,8 +503,36 @@ public class CommitBuilder {
>               this.destPath = destPath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            copyNode(srcPath, destPath);
> +            String srcParentPath = PathUtils.getParentPath(srcPath);
> +            String srcNodeName = PathUtils.getName(srcPath);
> +
> +            String destParentPath = PathUtils.getParentPath(destPath);
> +            String destNodeName = PathUtils.getName(destPath);
> +
> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> +            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
> +            if (srcCNE == null) {
> +                throw new NotFoundException(srcPath);
> +            }
> +
> +            MutableNode destParent = getOrCreateStagedNode(destParentPath);
> +            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> +
> +            if (srcCNE.getId() == null) {
> +                // a 'new' node is being copied
> +
> +                // update staging area
> +                copyStagedNodes(srcPath, destPath);
> +            }
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("*");
> +            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -489,22 +547,23 @@ public class CommitBuilder {
>               this.propValue = propValue;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            setProperty(nodePath, propName, propValue);
> -        }
> -    }
> +            MutableNode node = getOrCreateStagedNode(nodePath);
>
> -    class SetProperties extends Change {
> -        String nodePath;
> -        Map<String, String>  properties;
> -
> -        SetProperties(String nodePath, Map<String, String>  properties) {
> -            this.nodePath = nodePath;
> -            this.properties = properties;
> +            Map<String, String>  properties = node.getProperties();
> +            if (propValue == null) {
> +                properties.remove(propName);
> +            } else {
> +                properties.put(propName, propValue);
> +            }
>           }
>
> -        void apply() throws Exception {
> -            setProperties(nodePath, properties);
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("^");
> +            diff.append('"').append(PathUtils.concat(nodePath, propName)).append("\":").append(propValue);
> +            return diff.toString();
>           }
>       }
>   }
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java Sun Apr  8 20:31:50 2012
> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>           setRootNodeId(other.getRootNodeId());
>           setCommitTS(other.getCommitTS());
>           setMsg(other.getMsg());
> +        setChanges(other.getChanges());
>           this.id = other.getId();
>       }
>
> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>       public void setMsg(String msg) {
>           this.msg = msg;
>       }
> -
> +
> +    public void setChanges(String changes) {
> +        this.changes = changes;
> +    }
> +
>       /**
>        * Return the commit id.
>        *
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java Sun Apr  8 20:31:50 2012
> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>           Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>           long commitTS = binding.readLongValue("commitTS");
>           String msg = binding.readStringValue("msg");
> +        String changes = binding.readStringValue("changes");
>           String parentId = binding.readStringValue("parentId");
>           return new StoredCommit(id, "".equals(parentId) ? null : Id.fromString(parentId),
> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
> +                commitTS, rootNodeId, "".equals(msg) ? null : msg, changes);
>       }
>
> -    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg) {
> +    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg, String changes) {
>           this.id = id;
>           this.parentId = parentId;
>           this.commitTS = commitTS;
>           this.rootNodeId = rootNodeId;
>           this.msg = msg;
> +        this.changes = changes;
>       }
>
>       public StoredCommit(Id id, Commit commit) {
>
>


Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Stefan Guggisberg <st...@gmail.com>.
On Tue, Apr 10, 2012 at 12:01 AM, Michael Dürig <md...@apache.org> wrote:
>
>
> On 9.4.12 14:47, Stefan Guggisberg wrote:
>>
>> On Mon, Apr 9, 2012 at 12:52 PM, Michael Dürig<md...@apache.org>  wrote:
>>>
>>>
>>> Hi,
>>>
>>> This causes org.apache.jackrabbit.mk.ConcurrentWriteTest to run
>>> indefinitely. AFAICT this is caused by an endless loop in
>>> IOUtils.readVarInt
>>
>>
>> the new serialization format is unfortunately not backwards
>> compatible. you have to
>> start with a clean repo.
>
>
> This happens with mvn clean install which should be enough to clean the repo
> I guess.

the repo was created in '.' instead of 'target'. fixed now.

>
>
>>
>> BTW: KernelNodeStateEditorFuzzTest takes ages on my machine. could you
>> please make it complete in a couple of seconds or otherwise disable it in
>> the default profile?
>
>
> Use the smoke-test profile. See OAK-52.

i don't think that developers should be required to use a custom profile
during the development cycle, see )AK-53 ;)

cheers
stefan

>
> Michael
>
>
>>
>> thanks
>> stefan
>>
>>>
>>> Michael
>>>
>>>
>>>
>>> On 8.4.12 21:31, stefan@apache.org wrote:
>>>>
>>>>
>>>> Author: stefan
>>>> Date: Sun Apr  8 20:31:50 2012
>>>> New Revision: 1311085
>>>>
>>>> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
>>>> Log:
>>>> OAK-43: Incomplete journal when move and copy operations are involved
>>>>
>>>> Modified:
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>>>
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -18,9 +18,7 @@ package org.apache.jackrabbit.mk.core;
>>>>
>>>>  import java.io.InputStream;
>>>>  import java.util.ArrayList;
>>>> -import java.util.Collections;
>>>>  import java.util.HashMap;
>>>> -import java.util.LinkedList;
>>>>  import java.util.List;
>>>>  import java.util.Map;
>>>>
>>>> @@ -31,6 +29,7 @@ import org.apache.jackrabbit.mk.json.Jso
>>>>  import org.apache.jackrabbit.mk.model.ChildNodeEntry;
>>>>  import org.apache.jackrabbit.mk.model.Commit;
>>>>  import org.apache.jackrabbit.mk.model.CommitBuilder;
>>>> +import org.apache.jackrabbit.mk.model.CommitBuilder.NodeTree;
>>>>  import org.apache.jackrabbit.mk.model.Id;
>>>>  import org.apache.jackrabbit.mk.model.NodeState;
>>>>  import org.apache.jackrabbit.mk.model.PropertyState;
>>>> @@ -40,7 +39,6 @@ import org.apache.jackrabbit.mk.store.No
>>>>  import org.apache.jackrabbit.mk.store.RevisionProvider;
>>>>  import org.apache.jackrabbit.mk.util.CommitGate;
>>>>  import org.apache.jackrabbit.mk.util.PathUtils;
>>>> -import org.apache.jackrabbit.mk.util.SimpleLRUCache;
>>>>
>>>>  /**
>>>>   *
>>>> @@ -49,11 +47,6 @@ public class MicroKernelImpl implements
>>>>
>>>>      protected Repository rep;
>>>>      private final CommitGate gate = new CommitGate();
>>>> -
>>>> -    /**
>>>> -     * Key: revision id, Value: diff string
>>>> -     */
>>>> -    private final Map<Id, String>    diffCache =
>>>> Collections.synchronizedMap(SimpleLRUCache.<Id,
>>>> String>newInstance(100));
>>>>
>>>>      public MicroKernelImpl(String homeDir) throws MicroKernelException
>>>> {
>>>>          init(homeDir);
>>>> @@ -97,7 +90,6 @@ public class MicroKernelImpl implements
>>>>              }
>>>>              rep = null;
>>>>          }
>>>> -        diffCache.clear();
>>>>      }
>>>>
>>>>      public String getHeadRevision() throws MicroKernelException {
>>>> @@ -211,13 +203,8 @@ public class MicroKernelImpl implements
>>>>              commitBuff.object().
>>>>                      key("id").value(commit.getId().toString()).
>>>>                      key("ts").value(commit.getCommitTS()).
>>>> -                    key("msg").value(commit.getMsg());
>>>> -            String diff = diffCache.get(commit.getId());
>>>> -            if (diff == null) {
>>>> -                diff = diff(commit.getParentId(), commit.getId(),
>>>> filter);
>>>> -                diffCache.put(commit.getId(), diff);
>>>> -            }
>>>> -            commitBuff.key("changes").value(diff).endObject();
>>>> +                    key("msg").value(commit.getMsg()).
>>>> +
>>>>  key("changes").value(commit.getChanges()).endObject();
>>>>          }
>>>>          return commitBuff.endArray().toString();
>>>>      }
>>>> @@ -478,12 +465,7 @@ public class MicroKernelImpl implements
>>>>                              }
>>>>                              String parentPath =
>>>> PathUtils.getParentPath(nodePath);
>>>>                              String nodeName =
>>>> PathUtils.getName(nodePath);
>>>> -                            // build the list of added nodes
>>>> recursively
>>>> -                            LinkedList<AddNodeOperation>    list = new
>>>> LinkedList<AddNodeOperation>();
>>>> -                            addNode(list, parentPath, nodeName, t);
>>>> -                            for (AddNodeOperation op : list) {
>>>> -                                cb.addNode(op.path, op.name, op.props);
>>>> -                            }
>>>> +                            cb.addNode(parentPath, nodeName,
>>>> parseNode(t));
>>>>                          } else {
>>>>                              String value;
>>>>                              if (t.matches(JsopTokenizer.NULL)) {
>>>> @@ -637,30 +619,20 @@ public class MicroKernelImpl implements
>>>>          }
>>>>      }
>>>>
>>>> -    static void addNode(LinkedList<AddNodeOperation>    list, String
>>>> path,
>>>> String name, JsopTokenizer t) throws Exception {
>>>> -        AddNodeOperation op = new AddNodeOperation();
>>>> -        op.path = path;
>>>> -        op.name = name;
>>>> -        list.add(op);
>>>> +    NodeTree parseNode(JsopTokenizer t) throws Exception {
>>>> +        NodeTree node = new NodeTree();
>>>>          if (!t.matches('}')) {
>>>>              do {
>>>>                  String key = t.readString();
>>>>                  t.read(':');
>>>>                  if (t.matches('{')) {
>>>> -                    addNode(list, PathUtils.concat(path, name), key,
>>>> t);
>>>> +                    node.nodes.put(key, parseNode(t));
>>>>                  } else {
>>>> -                    op.props.put(key, t.readRawValue().trim());
>>>> +                    node.props.put(key, t.readRawValue().trim());
>>>>                  }
>>>>              } while (t.matches(','));
>>>>              t.read('}');
>>>>          }
>>>> +        return node;
>>>>      }
>>>> -
>>>> -    //--------------------------------------------------------<
>>>>  inner
>>>> classes>
>>>> -    static class AddNodeOperation {
>>>> -        String path;
>>>> -        String name;
>>>> -        Map<String, String>    props = new HashMap<String, String>();
>>>> -    }
>>>> -
>>>>  }
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -32,6 +32,9 @@ public abstract class AbstractCommit imp
>>>>      // commit message
>>>>      protected String msg;
>>>>
>>>> +    // changes
>>>> +    protected String changes;
>>>> +
>>>>      // id of parent commit
>>>>      protected Id parentId;
>>>>
>>>> @@ -42,6 +45,7 @@ public abstract class AbstractCommit imp
>>>>          this.parentId = other.getParentId();
>>>>          this.rootNodeId = other.getRootNodeId();
>>>>          this.msg = other.getMsg();
>>>> +        this.changes = other.getChanges();
>>>>          this.commitTS = other.getCommitTS();
>>>>      }
>>>>
>>>> @@ -61,10 +65,15 @@ public abstract class AbstractCommit imp
>>>>          return msg;
>>>>      }
>>>>
>>>> +    public String getChanges() {
>>>> +        return changes;
>>>> +    }
>>>> +
>>>>      public void serialize(Binding binding) throws Exception {
>>>>          binding.write("rootNodeId", rootNodeId.getBytes());
>>>>          binding.write("commitTS", commitTS);
>>>>          binding.write("msg", msg == null ? "" : msg);
>>>> +        binding.write("changes", changes == null ? "" : changes);
>>>>          binding.write("parentId", parentId == null ? "" :
>>>> parentId.toString());
>>>>      }
>>>>  }
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -31,5 +31,7 @@ public interface Commit {
>>>>
>>>>      public String getMsg();
>>>>
>>>> +    public String getChanges();
>>>> +
>>>>      void serialize(Binding binding) throws Exception;
>>>>  }
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -50,130 +50,39 @@ public class CommitBuilder {
>>>>          this.store = store;
>>>>      }
>>>>
>>>> -    public void addNode(String parentNodePath, String nodeName) throws
>>>> Exception {
>>>> -        addNode(parentNodePath, nodeName, Collections.<String,
>>>> String>emptyMap());
>>>> -    }
>>>> -
>>>> -    public void addNode(String parentNodePath, String nodeName,
>>>> Map<String, String>    properties) throws Exception {
>>>> -        MutableNode modParent = getOrCreateStagedNode(parentNodePath);
>>>> -        if (modParent.getChildNodeEntry(nodeName) != null) {
>>>> -            throw new Exception("there's already a child node with name
>>>> '" + nodeName + "'");
>>>> -        }
>>>> -        String newPath = PathUtils.concat(parentNodePath, nodeName);
>>>> -        MutableNode newChild = new MutableNode(store, newPath);
>>>> -        newChild.getProperties().putAll(properties);
>>>> -
>>>> -        // id will be computed on commit
>>>> -        modParent.add(new ChildNode(nodeName, null));
>>>> -        staged.put(newPath, newChild);
>>>> +    public void addNode(String parentNodePath, String nodeName,
>>>> NodeTree
>>>> node) throws Exception {
>>>> +        Change change = new AddNode(parentNodePath, nodeName, node);
>>>> +        change.apply();
>>>>          // update change log
>>>> -        changeLog.add(new AddNode(parentNodePath, nodeName,
>>>> properties));
>>>> +        changeLog.add(change);
>>>>      }
>>>>
>>>>      public void removeNode(String nodePath) throws NotFoundException,
>>>> Exception {
>>>> -        String parentPath = PathUtils.getParentPath(nodePath);
>>>> -        String nodeName = PathUtils.getName(nodePath);
>>>> -
>>>> -        MutableNode parent = getOrCreateStagedNode(parentPath);
>>>> -        if (parent.remove(nodeName) == null) {
>>>> -            throw new NotFoundException(nodePath);
>>>> -        }
>>>> -
>>>> -        // update staging area
>>>> -        removeStagedNodes(nodePath);
>>>> -
>>>> +        Change change = new RemoveNode(nodePath);
>>>> +        change.apply();
>>>>          // update change log
>>>> -        changeLog.add(new RemoveNode(nodePath));
>>>> +        changeLog.add(change);
>>>>      }
>>>>
>>>>      public void moveNode(String srcPath, String destPath) throws
>>>> NotFoundException, Exception {
>>>> -        if (PathUtils.isAncestor(srcPath, destPath)) {
>>>> -            throw new Exception("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);
>>>> -
>>>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>>> -        if (srcParentPath.equals(destParentPath)) {
>>>> -            if (srcParent.getChildNodeEntry(destNodeName) != null) {
>>>> -                throw new Exception("node already exists at move
>>>> destination path: " + destPath);
>>>> -            }
>>>> -            if (srcParent.rename(srcNodeName, destNodeName) == null) {
>>>> -                throw new NotFoundException(srcPath);
>>>> -            }
>>>> -        } else {
>>>> -            ChildNode srcCNE = srcParent.remove(srcNodeName);
>>>> -            if (srcCNE == null) {
>>>> -                throw new NotFoundException(srcPath);
>>>> -            }
>>>> -
>>>> -            MutableNode destParent =
>>>> getOrCreateStagedNode(destParentPath);
>>>> -            if (destParent.getChildNodeEntry(destNodeName) != null) {
>>>> -                throw new Exception("node already exists at move
>>>> destination path: " + destPath);
>>>> -            }
>>>> -            destParent.add(new ChildNode(destNodeName,
>>>> srcCNE.getId()));
>>>> -        }
>>>> -
>>>> -        // update staging area
>>>> -        moveStagedNodes(srcPath, destPath);
>>>> -
>>>> +        Change change = new MoveNode(srcPath, destPath);
>>>> +        change.apply();
>>>>          // update change log
>>>> -        changeLog.add(new MoveNode(srcPath, destPath));
>>>> +        changeLog.add(change);
>>>>      }
>>>>
>>>>      public void copyNode(String srcPath, String destPath) throws
>>>> NotFoundException, Exception {
>>>> -        String srcParentPath = PathUtils.getParentPath(srcPath);
>>>> -        String srcNodeName = PathUtils.getName(srcPath);
>>>> -
>>>> -        String destParentPath = PathUtils.getParentPath(destPath);
>>>> -        String destNodeName = PathUtils.getName(destPath);
>>>> -
>>>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>>> -        ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>>>> -        if (srcCNE == null) {
>>>> -            throw new NotFoundException(srcPath);
>>>> -        }
>>>> -
>>>> -        MutableNode destParent = getOrCreateStagedNode(destParentPath);
>>>> -        destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>>>> -
>>>> -        if (srcCNE.getId() == null) {
>>>> -            // a 'new' node is being copied
>>>> -
>>>> -            // update staging area
>>>> -            copyStagedNodes(srcPath, destPath);
>>>> -        }
>>>> -
>>>> +        Change change = new CopyNode(srcPath, destPath);
>>>> +        change.apply();
>>>>          // update change log
>>>> -        changeLog.add(new CopyNode(srcPath, destPath));
>>>> +        changeLog.add(change);
>>>>      }
>>>>
>>>>      public void setProperty(String nodePath, String propName, String
>>>> propValue) throws Exception {
>>>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>>>> -
>>>> -        Map<String, String>    properties = node.getProperties();
>>>> -        if (propValue == null) {
>>>> -            properties.remove(propName);
>>>> -        } else {
>>>> -            properties.put(propName, propValue);
>>>> -        }
>>>> -
>>>> -        // update change log
>>>> -        changeLog.add(new SetProperty(nodePath, propName, propValue));
>>>> -    }
>>>> -
>>>> -    public void setProperties(String nodePath, Map<String, String>
>>>>  properties) throws Exception {
>>>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>>>> -
>>>> -        node.getProperties().clear();
>>>> -        node.getProperties().putAll(properties);
>>>> -
>>>> +        Change change = new SetProperty(nodePath, propName, propValue);
>>>> +        change.apply();
>>>>          // update change log
>>>> -        changeLog.add(new SetProperties(nodePath, properties));
>>>> +        changeLog.add(change);
>>>>      }
>>>>
>>>>      public Id /* new revId */ doCommit() throws Exception {
>>>> @@ -190,9 +99,7 @@ public class CommitBuilder {
>>>>              // clear staging area
>>>>              staged.clear();
>>>>              // replay change log on new base revision
>>>> -            // copy log in order to avoid concurrent modifications
>>>> -            List<Change>    log = new ArrayList<Change>(changeLog);
>>>> -            for (Change change : log) {
>>>> +            for (Change change : changeLog) {
>>>>                  change.apply();
>>>>              }
>>>>          }
>>>> @@ -222,19 +129,29 @@ public class CommitBuilder {
>>>>              newCommit.setParentId(baseRevId);
>>>>              newCommit.setCommitTS(System.currentTimeMillis());
>>>>              newCommit.setMsg(msg);
>>>> +            StringBuilder diff = new StringBuilder();
>>>> +            for (Change change : changeLog) {
>>>> +                if (diff.length()>    0) {
>>>> +                    diff.append('\n');
>>>> +                }
>>>> +                diff.append(change.asDiff());
>>>> +            }
>>>> +            newCommit.setChanges(diff.toString());
>>>>              newCommit.setRootNodeId(rootNodeId);
>>>>              newRevId = store.putHeadCommit(newCommit);
>>>>          } finally {
>>>>              store.unlockHead();
>>>>          }
>>>>
>>>> -        // reset instance in order to be reusable
>>>> +        // reset instance
>>>>          staged.clear();
>>>>          changeLog.clear();
>>>>
>>>>          return newRevId;
>>>>      }
>>>>
>>>> +    //--------------------------------------------------------<
>>>>  inner
>>>> classes>
>>>> +
>>>>      MutableNode getOrCreateStagedNode(String nodePath) throws Exception
>>>> {
>>>>          MutableNode node = staged.get(nodePath);
>>>>          if (node == null) {
>>>> @@ -418,23 +335,79 @@ public class CommitBuilder {
>>>>      }
>>>>
>>>>      //--------------------------------------------------------<
>>>>  inner
>>>> classes>
>>>> +
>>>> +    public static class NodeTree {
>>>> +        public Map<String, String>    props = new HashMap<String,
>>>> String>();
>>>> +        public Map<String, NodeTree>    nodes = new HashMap<String,
>>>> NodeTree>();
>>>> +
>>>> +        void toJson(StringBuffer buf) {
>>>> +            toJson(buf, this);
>>>> +        }
>>>> +
>>>> +        private static void toJson(StringBuffer buf, NodeTree node) {
>>>> +            buf.append('{');
>>>> +            for (String name : node.props.keySet()) {
>>>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>>>> +                    buf.append(',');
>>>> +                }
>>>> +
>>>>
>>>>  buf.append('"').append(name).append("\":").append(node.props.get(name));
>>>> +            }
>>>> +            for (String name : node.nodes.keySet()) {
>>>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>>>> +                    buf.append(',');
>>>> +                }
>>>> +                buf.append('"').append(name).append("\":");
>>>> +                toJson(buf, node.nodes.get(name));
>>>> +            }
>>>> +            buf.append('}');
>>>> +        }
>>>> +    }
>>>> +
>>>>      abstract class Change {
>>>>          abstract void apply() throws Exception;
>>>> +        abstract String asDiff();
>>>>      }
>>>>
>>>>      class AddNode extends Change {
>>>>          String parentNodePath;
>>>>          String nodeName;
>>>> -        Map<String, String>    properties;
>>>> +        NodeTree node;
>>>>
>>>> -        AddNode(String parentNodePath, String nodeName, Map<String,
>>>> String>    properties) {
>>>> +        AddNode(String parentNodePath, String nodeName, NodeTree node)
>>>> {
>>>>              this.parentNodePath = parentNodePath;
>>>>              this.nodeName = nodeName;
>>>> -            this.properties = properties;
>>>> +            this.node = node;
>>>>          }
>>>>
>>>> +        @Override
>>>>          void apply() throws Exception {
>>>> -            addNode(parentNodePath, nodeName, properties);
>>>> +            recursiveAddNode(parentNodePath, nodeName, node);
>>>> +        }
>>>> +
>>>> +        @Override
>>>> +        String asDiff() {
>>>> +            StringBuffer diff = new StringBuffer("+");
>>>> +            diff.append('"').append(PathUtils.concat(parentNodePath,
>>>> nodeName)).append("\":");
>>>> +            node.toJson(diff);
>>>> +            return diff.toString();
>>>> +        }
>>>> +
>>>> +        private void recursiveAddNode(String parentPath, String name,
>>>> NodeTree node) throws Exception {
>>>> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
>>>> +            if (modParent.getChildNodeEntry(name) != null) {
>>>> +                throw new Exception("there's already a child node with
>>>> name '" + name + "'");
>>>> +            }
>>>> +            String newPath = PathUtils.concat(parentPath, name);
>>>> +            MutableNode newChild = new MutableNode(store, newPath);
>>>> +            newChild.getProperties().putAll(node.props);
>>>> +
>>>> +            // id will be computed on commit
>>>> +            modParent.add(new ChildNode(name, null));
>>>> +            staged.put(newPath, newChild);
>>>> +
>>>> +            for (String childName : node.nodes.keySet()) {
>>>> +                recursiveAddNode(PathUtils.concat(parentPath, name),
>>>> childName, node.nodes.get(childName));
>>>> +            }
>>>>          }
>>>>      }
>>>>
>>>> @@ -445,8 +418,25 @@ public class CommitBuilder {
>>>>              this.nodePath = nodePath;
>>>>          }
>>>>
>>>> +        @Override
>>>>          void apply() throws Exception {
>>>> -            removeNode(nodePath);
>>>> +            String parentPath = PathUtils.getParentPath(nodePath);
>>>> +            String nodeName = PathUtils.getName(nodePath);
>>>> +
>>>> +            MutableNode parent = getOrCreateStagedNode(parentPath);
>>>> +            if (parent.remove(nodeName) == null) {
>>>> +                throw new NotFoundException(nodePath);
>>>> +            }
>>>> +
>>>> +            // update staging area
>>>> +            removeStagedNodes(nodePath);
>>>> +        }
>>>> +
>>>> +        @Override
>>>> +        String asDiff() {
>>>> +            StringBuffer diff = new StringBuffer("-");
>>>> +            diff.append('"').append(nodePath).append('"');
>>>> +            return diff.toString();
>>>>          }
>>>>      }
>>>>
>>>> @@ -459,8 +449,48 @@ public class CommitBuilder {
>>>>              this.destPath = destPath;
>>>>          }
>>>>
>>>> +        @Override
>>>>          void apply() throws Exception {
>>>> -            moveNode(srcPath, destPath);
>>>> +            if (PathUtils.isAncestor(srcPath, destPath)) {
>>>> +                throw new Exception("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);
>>>> +
>>>> +            MutableNode srcParent =
>>>> getOrCreateStagedNode(srcParentPath);
>>>> +            if (srcParentPath.equals(destParentPath)) {
>>>> +                if (srcParent.getChildNodeEntry(destNodeName) != null)
>>>> {
>>>> +                    throw new Exception("node already exists at move
>>>> destination path: " + destPath);
>>>> +                }
>>>> +                if (srcParent.rename(srcNodeName, destNodeName) ==
>>>> null)
>>>> {
>>>> +                    throw new NotFoundException(srcPath);
>>>> +                }
>>>> +            } else {
>>>> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
>>>> +                if (srcCNE == null) {
>>>> +                    throw new NotFoundException(srcPath);
>>>> +                }
>>>> +
>>>> +                MutableNode destParent =
>>>> getOrCreateStagedNode(destParentPath);
>>>> +                if (destParent.getChildNodeEntry(destNodeName) != null)
>>>> {
>>>> +                    throw new Exception("node already exists at move
>>>> destination path: " + destPath);
>>>> +                }
>>>> +                destParent.add(new ChildNode(destNodeName,
>>>> srcCNE.getId()));
>>>> +            }
>>>> +
>>>> +            // update staging area
>>>> +            moveStagedNodes(srcPath, destPath);
>>>> +        }
>>>> +
>>>> +        @Override
>>>> +        String asDiff() {
>>>> +            StringBuffer diff = new StringBuffer(">");
>>>> +
>>>>
>>>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>>>> +            return diff.toString();
>>>>          }
>>>>      }
>>>>
>>>> @@ -473,8 +503,36 @@ public class CommitBuilder {
>>>>              this.destPath = destPath;
>>>>          }
>>>>
>>>> +        @Override
>>>>          void apply() throws Exception {
>>>> -            copyNode(srcPath, destPath);
>>>> +            String srcParentPath = PathUtils.getParentPath(srcPath);
>>>> +            String srcNodeName = PathUtils.getName(srcPath);
>>>> +
>>>> +            String destParentPath = PathUtils.getParentPath(destPath);
>>>> +            String destNodeName = PathUtils.getName(destPath);
>>>> +
>>>> +            MutableNode srcParent =
>>>> getOrCreateStagedNode(srcParentPath);
>>>> +            ChildNode srcCNE =
>>>> srcParent.getChildNodeEntry(srcNodeName);
>>>> +            if (srcCNE == null) {
>>>> +                throw new NotFoundException(srcPath);
>>>> +            }
>>>> +
>>>> +            MutableNode destParent =
>>>> getOrCreateStagedNode(destParentPath);
>>>> +            destParent.add(new ChildNode(destNodeName,
>>>> srcCNE.getId()));
>>>> +
>>>> +            if (srcCNE.getId() == null) {
>>>> +                // a 'new' node is being copied
>>>> +
>>>> +                // update staging area
>>>> +                copyStagedNodes(srcPath, destPath);
>>>> +            }
>>>> +        }
>>>> +
>>>> +        @Override
>>>> +        String asDiff() {
>>>> +            StringBuffer diff = new StringBuffer("*");
>>>> +
>>>>
>>>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>>>> +            return diff.toString();
>>>>          }
>>>>      }
>>>>
>>>> @@ -489,22 +547,23 @@ public class CommitBuilder {
>>>>              this.propValue = propValue;
>>>>          }
>>>>
>>>> +        @Override
>>>>          void apply() throws Exception {
>>>> -            setProperty(nodePath, propName, propValue);
>>>> -        }
>>>> -    }
>>>> +            MutableNode node = getOrCreateStagedNode(nodePath);
>>>>
>>>> -    class SetProperties extends Change {
>>>> -        String nodePath;
>>>> -        Map<String, String>    properties;
>>>> -
>>>> -        SetProperties(String nodePath, Map<String, String>
>>>>  properties) {
>>>> -            this.nodePath = nodePath;
>>>> -            this.properties = properties;
>>>> +            Map<String, String>    properties = node.getProperties();
>>>> +            if (propValue == null) {
>>>> +                properties.remove(propName);
>>>> +            } else {
>>>> +                properties.put(propName, propValue);
>>>> +            }
>>>>          }
>>>>
>>>> -        void apply() throws Exception {
>>>> -            setProperties(nodePath, properties);
>>>> +        @Override
>>>> +        String asDiff() {
>>>> +            StringBuffer diff = new StringBuffer("^");
>>>> +            diff.append('"').append(PathUtils.concat(nodePath,
>>>> propName)).append("\":").append(propValue);
>>>> +            return diff.toString();
>>>>          }
>>>>      }
>>>>  }
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>>>>          setRootNodeId(other.getRootNodeId());
>>>>          setCommitTS(other.getCommitTS());
>>>>          setMsg(other.getMsg());
>>>> +        setChanges(other.getChanges());
>>>>          this.id = other.getId();
>>>>      }
>>>>
>>>> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>>>>      public void setMsg(String msg) {
>>>>          this.msg = msg;
>>>>      }
>>>> -
>>>> +
>>>> +    public void setChanges(String changes) {
>>>> +        this.changes = changes;
>>>> +    }
>>>> +
>>>>      /**
>>>>       * Return the commit id.
>>>>       *
>>>>
>>>> Modified:
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>>> URL:
>>>>
>>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>>
>>>>
>>>> ==============================================================================
>>>> ---
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>>> (original)
>>>> +++
>>>>
>>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>>> Sun Apr  8 20:31:50 2012
>>>> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>>>>          Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>>>>          long commitTS = binding.readLongValue("commitTS");
>>>>          String msg = binding.readStringValue("msg");
>>>> +        String changes = binding.readStringValue("changes");
>>>>          String parentId = binding.readStringValue("parentId");
>>>>          return new StoredCommit(id, "".equals(parentId) ? null :
>>>> Id.fromString(parentId),
>>>> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
>>>> +                commitTS, rootNodeId, "".equals(msg) ? null : msg,
>>>> changes);
>>>>      }
>>>>
>>>> -    public StoredCommit(Id id, Id parentId, long commitTS, Id
>>>> rootNodeId,
>>>> String msg) {
>>>> +    public StoredCommit(Id id, Id parentId, long commitTS, Id
>>>> rootNodeId,
>>>> String msg, String changes) {
>>>>          this.id = id;
>>>>          this.parentId = parentId;
>>>>          this.commitTS = commitTS;
>>>>          this.rootNodeId = rootNodeId;
>>>>          this.msg = msg;
>>>> +        this.changes = changes;
>>>>      }
>>>>
>>>>      public StoredCommit(Id id, Commit commit) {
>>>>
>>>>
>>>
>

Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Michael Dürig <md...@apache.org>.

On 9.4.12 14:47, Stefan Guggisberg wrote:
> On Mon, Apr 9, 2012 at 12:52 PM, Michael Dürig<md...@apache.org>  wrote:
>>
>> Hi,
>>
>> This causes org.apache.jackrabbit.mk.ConcurrentWriteTest to run
>> indefinitely. AFAICT this is caused by an endless loop in IOUtils.readVarInt
>
> the new serialization format is unfortunately not backwards
> compatible. you have to
> start with a clean repo.

This happens with mvn clean install which should be enough to clean the 
repo I guess.

>
> BTW: KernelNodeStateEditorFuzzTest takes ages on my machine. could you
> please make it complete in a couple of seconds or otherwise disable it in
> the default profile?

Use the smoke-test profile. See OAK-52.

Michael

>
> thanks
> stefan
>
>>
>> Michael
>>
>>
>>
>> On 8.4.12 21:31, stefan@apache.org wrote:
>>>
>>> Author: stefan
>>> Date: Sun Apr  8 20:31:50 2012
>>> New Revision: 1311085
>>>
>>> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
>>> Log:
>>> OAK-43: Incomplete journal when move and copy operations are involved
>>>
>>> Modified:
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>>
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -18,9 +18,7 @@ package org.apache.jackrabbit.mk.core;
>>>
>>>   import java.io.InputStream;
>>>   import java.util.ArrayList;
>>> -import java.util.Collections;
>>>   import java.util.HashMap;
>>> -import java.util.LinkedList;
>>>   import java.util.List;
>>>   import java.util.Map;
>>>
>>> @@ -31,6 +29,7 @@ import org.apache.jackrabbit.mk.json.Jso
>>>   import org.apache.jackrabbit.mk.model.ChildNodeEntry;
>>>   import org.apache.jackrabbit.mk.model.Commit;
>>>   import org.apache.jackrabbit.mk.model.CommitBuilder;
>>> +import org.apache.jackrabbit.mk.model.CommitBuilder.NodeTree;
>>>   import org.apache.jackrabbit.mk.model.Id;
>>>   import org.apache.jackrabbit.mk.model.NodeState;
>>>   import org.apache.jackrabbit.mk.model.PropertyState;
>>> @@ -40,7 +39,6 @@ import org.apache.jackrabbit.mk.store.No
>>>   import org.apache.jackrabbit.mk.store.RevisionProvider;
>>>   import org.apache.jackrabbit.mk.util.CommitGate;
>>>   import org.apache.jackrabbit.mk.util.PathUtils;
>>> -import org.apache.jackrabbit.mk.util.SimpleLRUCache;
>>>
>>>   /**
>>>    *
>>> @@ -49,11 +47,6 @@ public class MicroKernelImpl implements
>>>
>>>       protected Repository rep;
>>>       private final CommitGate gate = new CommitGate();
>>> -
>>> -    /**
>>> -     * Key: revision id, Value: diff string
>>> -     */
>>> -    private final Map<Id, String>    diffCache =
>>> Collections.synchronizedMap(SimpleLRUCache.<Id, String>newInstance(100));
>>>
>>>       public MicroKernelImpl(String homeDir) throws MicroKernelException {
>>>           init(homeDir);
>>> @@ -97,7 +90,6 @@ public class MicroKernelImpl implements
>>>               }
>>>               rep = null;
>>>           }
>>> -        diffCache.clear();
>>>       }
>>>
>>>       public String getHeadRevision() throws MicroKernelException {
>>> @@ -211,13 +203,8 @@ public class MicroKernelImpl implements
>>>               commitBuff.object().
>>>                       key("id").value(commit.getId().toString()).
>>>                       key("ts").value(commit.getCommitTS()).
>>> -                    key("msg").value(commit.getMsg());
>>> -            String diff = diffCache.get(commit.getId());
>>> -            if (diff == null) {
>>> -                diff = diff(commit.getParentId(), commit.getId(),
>>> filter);
>>> -                diffCache.put(commit.getId(), diff);
>>> -            }
>>> -            commitBuff.key("changes").value(diff).endObject();
>>> +                    key("msg").value(commit.getMsg()).
>>> +
>>>   key("changes").value(commit.getChanges()).endObject();
>>>           }
>>>           return commitBuff.endArray().toString();
>>>       }
>>> @@ -478,12 +465,7 @@ public class MicroKernelImpl implements
>>>                               }
>>>                               String parentPath =
>>> PathUtils.getParentPath(nodePath);
>>>                               String nodeName =
>>> PathUtils.getName(nodePath);
>>> -                            // build the list of added nodes recursively
>>> -                            LinkedList<AddNodeOperation>    list = new
>>> LinkedList<AddNodeOperation>();
>>> -                            addNode(list, parentPath, nodeName, t);
>>> -                            for (AddNodeOperation op : list) {
>>> -                                cb.addNode(op.path, op.name, op.props);
>>> -                            }
>>> +                            cb.addNode(parentPath, nodeName,
>>> parseNode(t));
>>>                           } else {
>>>                               String value;
>>>                               if (t.matches(JsopTokenizer.NULL)) {
>>> @@ -637,30 +619,20 @@ public class MicroKernelImpl implements
>>>           }
>>>       }
>>>
>>> -    static void addNode(LinkedList<AddNodeOperation>    list, String path,
>>> String name, JsopTokenizer t) throws Exception {
>>> -        AddNodeOperation op = new AddNodeOperation();
>>> -        op.path = path;
>>> -        op.name = name;
>>> -        list.add(op);
>>> +    NodeTree parseNode(JsopTokenizer t) throws Exception {
>>> +        NodeTree node = new NodeTree();
>>>           if (!t.matches('}')) {
>>>               do {
>>>                   String key = t.readString();
>>>                   t.read(':');
>>>                   if (t.matches('{')) {
>>> -                    addNode(list, PathUtils.concat(path, name), key, t);
>>> +                    node.nodes.put(key, parseNode(t));
>>>                   } else {
>>> -                    op.props.put(key, t.readRawValue().trim());
>>> +                    node.props.put(key, t.readRawValue().trim());
>>>                   }
>>>               } while (t.matches(','));
>>>               t.read('}');
>>>           }
>>> +        return node;
>>>       }
>>> -
>>> -    //--------------------------------------------------------<    inner
>>> classes>
>>> -    static class AddNodeOperation {
>>> -        String path;
>>> -        String name;
>>> -        Map<String, String>    props = new HashMap<String, String>();
>>> -    }
>>> -
>>>   }
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -32,6 +32,9 @@ public abstract class AbstractCommit imp
>>>       // commit message
>>>       protected String msg;
>>>
>>> +    // changes
>>> +    protected String changes;
>>> +
>>>       // id of parent commit
>>>       protected Id parentId;
>>>
>>> @@ -42,6 +45,7 @@ public abstract class AbstractCommit imp
>>>           this.parentId = other.getParentId();
>>>           this.rootNodeId = other.getRootNodeId();
>>>           this.msg = other.getMsg();
>>> +        this.changes = other.getChanges();
>>>           this.commitTS = other.getCommitTS();
>>>       }
>>>
>>> @@ -61,10 +65,15 @@ public abstract class AbstractCommit imp
>>>           return msg;
>>>       }
>>>
>>> +    public String getChanges() {
>>> +        return changes;
>>> +    }
>>> +
>>>       public void serialize(Binding binding) throws Exception {
>>>           binding.write("rootNodeId", rootNodeId.getBytes());
>>>           binding.write("commitTS", commitTS);
>>>           binding.write("msg", msg == null ? "" : msg);
>>> +        binding.write("changes", changes == null ? "" : changes);
>>>           binding.write("parentId", parentId == null ? "" :
>>> parentId.toString());
>>>       }
>>>   }
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -31,5 +31,7 @@ public interface Commit {
>>>
>>>       public String getMsg();
>>>
>>> +    public String getChanges();
>>> +
>>>       void serialize(Binding binding) throws Exception;
>>>   }
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -50,130 +50,39 @@ public class CommitBuilder {
>>>           this.store = store;
>>>       }
>>>
>>> -    public void addNode(String parentNodePath, String nodeName) throws
>>> Exception {
>>> -        addNode(parentNodePath, nodeName, Collections.<String,
>>> String>emptyMap());
>>> -    }
>>> -
>>> -    public void addNode(String parentNodePath, String nodeName,
>>> Map<String, String>    properties) throws Exception {
>>> -        MutableNode modParent = getOrCreateStagedNode(parentNodePath);
>>> -        if (modParent.getChildNodeEntry(nodeName) != null) {
>>> -            throw new Exception("there's already a child node with name
>>> '" + nodeName + "'");
>>> -        }
>>> -        String newPath = PathUtils.concat(parentNodePath, nodeName);
>>> -        MutableNode newChild = new MutableNode(store, newPath);
>>> -        newChild.getProperties().putAll(properties);
>>> -
>>> -        // id will be computed on commit
>>> -        modParent.add(new ChildNode(nodeName, null));
>>> -        staged.put(newPath, newChild);
>>> +    public void addNode(String parentNodePath, String nodeName, NodeTree
>>> node) throws Exception {
>>> +        Change change = new AddNode(parentNodePath, nodeName, node);
>>> +        change.apply();
>>>           // update change log
>>> -        changeLog.add(new AddNode(parentNodePath, nodeName, properties));
>>> +        changeLog.add(change);
>>>       }
>>>
>>>       public void removeNode(String nodePath) throws NotFoundException,
>>> Exception {
>>> -        String parentPath = PathUtils.getParentPath(nodePath);
>>> -        String nodeName = PathUtils.getName(nodePath);
>>> -
>>> -        MutableNode parent = getOrCreateStagedNode(parentPath);
>>> -        if (parent.remove(nodeName) == null) {
>>> -            throw new NotFoundException(nodePath);
>>> -        }
>>> -
>>> -        // update staging area
>>> -        removeStagedNodes(nodePath);
>>> -
>>> +        Change change = new RemoveNode(nodePath);
>>> +        change.apply();
>>>           // update change log
>>> -        changeLog.add(new RemoveNode(nodePath));
>>> +        changeLog.add(change);
>>>       }
>>>
>>>       public void moveNode(String srcPath, String destPath) throws
>>> NotFoundException, Exception {
>>> -        if (PathUtils.isAncestor(srcPath, destPath)) {
>>> -            throw new Exception("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);
>>> -
>>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>> -        if (srcParentPath.equals(destParentPath)) {
>>> -            if (srcParent.getChildNodeEntry(destNodeName) != null) {
>>> -                throw new Exception("node already exists at move
>>> destination path: " + destPath);
>>> -            }
>>> -            if (srcParent.rename(srcNodeName, destNodeName) == null) {
>>> -                throw new NotFoundException(srcPath);
>>> -            }
>>> -        } else {
>>> -            ChildNode srcCNE = srcParent.remove(srcNodeName);
>>> -            if (srcCNE == null) {
>>> -                throw new NotFoundException(srcPath);
>>> -            }
>>> -
>>> -            MutableNode destParent =
>>> getOrCreateStagedNode(destParentPath);
>>> -            if (destParent.getChildNodeEntry(destNodeName) != null) {
>>> -                throw new Exception("node already exists at move
>>> destination path: " + destPath);
>>> -            }
>>> -            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>>> -        }
>>> -
>>> -        // update staging area
>>> -        moveStagedNodes(srcPath, destPath);
>>> -
>>> +        Change change = new MoveNode(srcPath, destPath);
>>> +        change.apply();
>>>           // update change log
>>> -        changeLog.add(new MoveNode(srcPath, destPath));
>>> +        changeLog.add(change);
>>>       }
>>>
>>>       public void copyNode(String srcPath, String destPath) throws
>>> NotFoundException, Exception {
>>> -        String srcParentPath = PathUtils.getParentPath(srcPath);
>>> -        String srcNodeName = PathUtils.getName(srcPath);
>>> -
>>> -        String destParentPath = PathUtils.getParentPath(destPath);
>>> -        String destNodeName = PathUtils.getName(destPath);
>>> -
>>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>> -        ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>>> -        if (srcCNE == null) {
>>> -            throw new NotFoundException(srcPath);
>>> -        }
>>> -
>>> -        MutableNode destParent = getOrCreateStagedNode(destParentPath);
>>> -        destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>>> -
>>> -        if (srcCNE.getId() == null) {
>>> -            // a 'new' node is being copied
>>> -
>>> -            // update staging area
>>> -            copyStagedNodes(srcPath, destPath);
>>> -        }
>>> -
>>> +        Change change = new CopyNode(srcPath, destPath);
>>> +        change.apply();
>>>           // update change log
>>> -        changeLog.add(new CopyNode(srcPath, destPath));
>>> +        changeLog.add(change);
>>>       }
>>>
>>>       public void setProperty(String nodePath, String propName, String
>>> propValue) throws Exception {
>>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>>> -
>>> -        Map<String, String>    properties = node.getProperties();
>>> -        if (propValue == null) {
>>> -            properties.remove(propName);
>>> -        } else {
>>> -            properties.put(propName, propValue);
>>> -        }
>>> -
>>> -        // update change log
>>> -        changeLog.add(new SetProperty(nodePath, propName, propValue));
>>> -    }
>>> -
>>> -    public void setProperties(String nodePath, Map<String, String>
>>>   properties) throws Exception {
>>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>>> -
>>> -        node.getProperties().clear();
>>> -        node.getProperties().putAll(properties);
>>> -
>>> +        Change change = new SetProperty(nodePath, propName, propValue);
>>> +        change.apply();
>>>           // update change log
>>> -        changeLog.add(new SetProperties(nodePath, properties));
>>> +        changeLog.add(change);
>>>       }
>>>
>>>       public Id /* new revId */ doCommit() throws Exception {
>>> @@ -190,9 +99,7 @@ public class CommitBuilder {
>>>               // clear staging area
>>>               staged.clear();
>>>               // replay change log on new base revision
>>> -            // copy log in order to avoid concurrent modifications
>>> -            List<Change>    log = new ArrayList<Change>(changeLog);
>>> -            for (Change change : log) {
>>> +            for (Change change : changeLog) {
>>>                   change.apply();
>>>               }
>>>           }
>>> @@ -222,19 +129,29 @@ public class CommitBuilder {
>>>               newCommit.setParentId(baseRevId);
>>>               newCommit.setCommitTS(System.currentTimeMillis());
>>>               newCommit.setMsg(msg);
>>> +            StringBuilder diff = new StringBuilder();
>>> +            for (Change change : changeLog) {
>>> +                if (diff.length()>    0) {
>>> +                    diff.append('\n');
>>> +                }
>>> +                diff.append(change.asDiff());
>>> +            }
>>> +            newCommit.setChanges(diff.toString());
>>>               newCommit.setRootNodeId(rootNodeId);
>>>               newRevId = store.putHeadCommit(newCommit);
>>>           } finally {
>>>               store.unlockHead();
>>>           }
>>>
>>> -        // reset instance in order to be reusable
>>> +        // reset instance
>>>           staged.clear();
>>>           changeLog.clear();
>>>
>>>           return newRevId;
>>>       }
>>>
>>> +    //--------------------------------------------------------<    inner
>>> classes>
>>> +
>>>       MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
>>>           MutableNode node = staged.get(nodePath);
>>>           if (node == null) {
>>> @@ -418,23 +335,79 @@ public class CommitBuilder {
>>>       }
>>>
>>>       //--------------------------------------------------------<    inner
>>> classes>
>>> +
>>> +    public static class NodeTree {
>>> +        public Map<String, String>    props = new HashMap<String,
>>> String>();
>>> +        public Map<String, NodeTree>    nodes = new HashMap<String,
>>> NodeTree>();
>>> +
>>> +        void toJson(StringBuffer buf) {
>>> +            toJson(buf, this);
>>> +        }
>>> +
>>> +        private static void toJson(StringBuffer buf, NodeTree node) {
>>> +            buf.append('{');
>>> +            for (String name : node.props.keySet()) {
>>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>>> +                    buf.append(',');
>>> +                }
>>> +
>>>   buf.append('"').append(name).append("\":").append(node.props.get(name));
>>> +            }
>>> +            for (String name : node.nodes.keySet()) {
>>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>>> +                    buf.append(',');
>>> +                }
>>> +                buf.append('"').append(name).append("\":");
>>> +                toJson(buf, node.nodes.get(name));
>>> +            }
>>> +            buf.append('}');
>>> +        }
>>> +    }
>>> +
>>>       abstract class Change {
>>>           abstract void apply() throws Exception;
>>> +        abstract String asDiff();
>>>       }
>>>
>>>       class AddNode extends Change {
>>>           String parentNodePath;
>>>           String nodeName;
>>> -        Map<String, String>    properties;
>>> +        NodeTree node;
>>>
>>> -        AddNode(String parentNodePath, String nodeName, Map<String,
>>> String>    properties) {
>>> +        AddNode(String parentNodePath, String nodeName, NodeTree node) {
>>>               this.parentNodePath = parentNodePath;
>>>               this.nodeName = nodeName;
>>> -            this.properties = properties;
>>> +            this.node = node;
>>>           }
>>>
>>> +        @Override
>>>           void apply() throws Exception {
>>> -            addNode(parentNodePath, nodeName, properties);
>>> +            recursiveAddNode(parentNodePath, nodeName, node);
>>> +        }
>>> +
>>> +        @Override
>>> +        String asDiff() {
>>> +            StringBuffer diff = new StringBuffer("+");
>>> +            diff.append('"').append(PathUtils.concat(parentNodePath,
>>> nodeName)).append("\":");
>>> +            node.toJson(diff);
>>> +            return diff.toString();
>>> +        }
>>> +
>>> +        private void recursiveAddNode(String parentPath, String name,
>>> NodeTree node) throws Exception {
>>> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
>>> +            if (modParent.getChildNodeEntry(name) != null) {
>>> +                throw new Exception("there's already a child node with
>>> name '" + name + "'");
>>> +            }
>>> +            String newPath = PathUtils.concat(parentPath, name);
>>> +            MutableNode newChild = new MutableNode(store, newPath);
>>> +            newChild.getProperties().putAll(node.props);
>>> +
>>> +            // id will be computed on commit
>>> +            modParent.add(new ChildNode(name, null));
>>> +            staged.put(newPath, newChild);
>>> +
>>> +            for (String childName : node.nodes.keySet()) {
>>> +                recursiveAddNode(PathUtils.concat(parentPath, name),
>>> childName, node.nodes.get(childName));
>>> +            }
>>>           }
>>>       }
>>>
>>> @@ -445,8 +418,25 @@ public class CommitBuilder {
>>>               this.nodePath = nodePath;
>>>           }
>>>
>>> +        @Override
>>>           void apply() throws Exception {
>>> -            removeNode(nodePath);
>>> +            String parentPath = PathUtils.getParentPath(nodePath);
>>> +            String nodeName = PathUtils.getName(nodePath);
>>> +
>>> +            MutableNode parent = getOrCreateStagedNode(parentPath);
>>> +            if (parent.remove(nodeName) == null) {
>>> +                throw new NotFoundException(nodePath);
>>> +            }
>>> +
>>> +            // update staging area
>>> +            removeStagedNodes(nodePath);
>>> +        }
>>> +
>>> +        @Override
>>> +        String asDiff() {
>>> +            StringBuffer diff = new StringBuffer("-");
>>> +            diff.append('"').append(nodePath).append('"');
>>> +            return diff.toString();
>>>           }
>>>       }
>>>
>>> @@ -459,8 +449,48 @@ public class CommitBuilder {
>>>               this.destPath = destPath;
>>>           }
>>>
>>> +        @Override
>>>           void apply() throws Exception {
>>> -            moveNode(srcPath, destPath);
>>> +            if (PathUtils.isAncestor(srcPath, destPath)) {
>>> +                throw new Exception("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);
>>> +
>>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>> +            if (srcParentPath.equals(destParentPath)) {
>>> +                if (srcParent.getChildNodeEntry(destNodeName) != null) {
>>> +                    throw new Exception("node already exists at move
>>> destination path: " + destPath);
>>> +                }
>>> +                if (srcParent.rename(srcNodeName, destNodeName) == null)
>>> {
>>> +                    throw new NotFoundException(srcPath);
>>> +                }
>>> +            } else {
>>> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
>>> +                if (srcCNE == null) {
>>> +                    throw new NotFoundException(srcPath);
>>> +                }
>>> +
>>> +                MutableNode destParent =
>>> getOrCreateStagedNode(destParentPath);
>>> +                if (destParent.getChildNodeEntry(destNodeName) != null) {
>>> +                    throw new Exception("node already exists at move
>>> destination path: " + destPath);
>>> +                }
>>> +                destParent.add(new ChildNode(destNodeName,
>>> srcCNE.getId()));
>>> +            }
>>> +
>>> +            // update staging area
>>> +            moveStagedNodes(srcPath, destPath);
>>> +        }
>>> +
>>> +        @Override
>>> +        String asDiff() {
>>> +            StringBuffer diff = new StringBuffer(">");
>>> +
>>>   diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>>> +            return diff.toString();
>>>           }
>>>       }
>>>
>>> @@ -473,8 +503,36 @@ public class CommitBuilder {
>>>               this.destPath = destPath;
>>>           }
>>>
>>> +        @Override
>>>           void apply() throws Exception {
>>> -            copyNode(srcPath, destPath);
>>> +            String srcParentPath = PathUtils.getParentPath(srcPath);
>>> +            String srcNodeName = PathUtils.getName(srcPath);
>>> +
>>> +            String destParentPath = PathUtils.getParentPath(destPath);
>>> +            String destNodeName = PathUtils.getName(destPath);
>>> +
>>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>>> +            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>>> +            if (srcCNE == null) {
>>> +                throw new NotFoundException(srcPath);
>>> +            }
>>> +
>>> +            MutableNode destParent =
>>> getOrCreateStagedNode(destParentPath);
>>> +            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>>> +
>>> +            if (srcCNE.getId() == null) {
>>> +                // a 'new' node is being copied
>>> +
>>> +                // update staging area
>>> +                copyStagedNodes(srcPath, destPath);
>>> +            }
>>> +        }
>>> +
>>> +        @Override
>>> +        String asDiff() {
>>> +            StringBuffer diff = new StringBuffer("*");
>>> +
>>>   diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>>> +            return diff.toString();
>>>           }
>>>       }
>>>
>>> @@ -489,22 +547,23 @@ public class CommitBuilder {
>>>               this.propValue = propValue;
>>>           }
>>>
>>> +        @Override
>>>           void apply() throws Exception {
>>> -            setProperty(nodePath, propName, propValue);
>>> -        }
>>> -    }
>>> +            MutableNode node = getOrCreateStagedNode(nodePath);
>>>
>>> -    class SetProperties extends Change {
>>> -        String nodePath;
>>> -        Map<String, String>    properties;
>>> -
>>> -        SetProperties(String nodePath, Map<String, String>    properties) {
>>> -            this.nodePath = nodePath;
>>> -            this.properties = properties;
>>> +            Map<String, String>    properties = node.getProperties();
>>> +            if (propValue == null) {
>>> +                properties.remove(propName);
>>> +            } else {
>>> +                properties.put(propName, propValue);
>>> +            }
>>>           }
>>>
>>> -        void apply() throws Exception {
>>> -            setProperties(nodePath, properties);
>>> +        @Override
>>> +        String asDiff() {
>>> +            StringBuffer diff = new StringBuffer("^");
>>> +            diff.append('"').append(PathUtils.concat(nodePath,
>>> propName)).append("\":").append(propValue);
>>> +            return diff.toString();
>>>           }
>>>       }
>>>   }
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>>>           setRootNodeId(other.getRootNodeId());
>>>           setCommitTS(other.getCommitTS());
>>>           setMsg(other.getMsg());
>>> +        setChanges(other.getChanges());
>>>           this.id = other.getId();
>>>       }
>>>
>>> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>>>       public void setMsg(String msg) {
>>>           this.msg = msg;
>>>       }
>>> -
>>> +
>>> +    public void setChanges(String changes) {
>>> +        this.changes = changes;
>>> +    }
>>> +
>>>       /**
>>>        * Return the commit id.
>>>        *
>>>
>>> Modified:
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>> URL:
>>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>> (original)
>>> +++
>>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>> Sun Apr  8 20:31:50 2012
>>> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>>>           Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>>>           long commitTS = binding.readLongValue("commitTS");
>>>           String msg = binding.readStringValue("msg");
>>> +        String changes = binding.readStringValue("changes");
>>>           String parentId = binding.readStringValue("parentId");
>>>           return new StoredCommit(id, "".equals(parentId) ? null :
>>> Id.fromString(parentId),
>>> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
>>> +                commitTS, rootNodeId, "".equals(msg) ? null : msg,
>>> changes);
>>>       }
>>>
>>> -    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>>> String msg) {
>>> +    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>>> String msg, String changes) {
>>>           this.id = id;
>>>           this.parentId = parentId;
>>>           this.commitTS = commitTS;
>>>           this.rootNodeId = rootNodeId;
>>>           this.msg = msg;
>>> +        this.changes = changes;
>>>       }
>>>
>>>       public StoredCommit(Id id, Commit commit) {
>>>
>>>
>>


Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Stefan Guggisberg <st...@gmail.com>.
On Mon, Apr 9, 2012 at 12:52 PM, Michael Dürig <md...@apache.org> wrote:
>
> Hi,
>
> This causes org.apache.jackrabbit.mk.ConcurrentWriteTest to run
> indefinitely. AFAICT this is caused by an endless loop in IOUtils.readVarInt

the new serialization format is unfortunately not backwards
compatible. you have to
start with a clean repo.

BTW: KernelNodeStateEditorFuzzTest takes ages on my machine. could you
please make it complete in a couple of seconds or otherwise disable it in
the default profile?

thanks
stefan

>
> Michael
>
>
>
> On 8.4.12 21:31, stefan@apache.org wrote:
>>
>> Author: stefan
>> Date: Sun Apr  8 20:31:50 2012
>> New Revision: 1311085
>>
>> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
>> Log:
>> OAK-43: Incomplete journal when move and copy operations are involved
>>
>> Modified:
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>>
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>> Sun Apr  8 20:31:50 2012
>> @@ -18,9 +18,7 @@ package org.apache.jackrabbit.mk.core;
>>
>>  import java.io.InputStream;
>>  import java.util.ArrayList;
>> -import java.util.Collections;
>>  import java.util.HashMap;
>> -import java.util.LinkedList;
>>  import java.util.List;
>>  import java.util.Map;
>>
>> @@ -31,6 +29,7 @@ import org.apache.jackrabbit.mk.json.Jso
>>  import org.apache.jackrabbit.mk.model.ChildNodeEntry;
>>  import org.apache.jackrabbit.mk.model.Commit;
>>  import org.apache.jackrabbit.mk.model.CommitBuilder;
>> +import org.apache.jackrabbit.mk.model.CommitBuilder.NodeTree;
>>  import org.apache.jackrabbit.mk.model.Id;
>>  import org.apache.jackrabbit.mk.model.NodeState;
>>  import org.apache.jackrabbit.mk.model.PropertyState;
>> @@ -40,7 +39,6 @@ import org.apache.jackrabbit.mk.store.No
>>  import org.apache.jackrabbit.mk.store.RevisionProvider;
>>  import org.apache.jackrabbit.mk.util.CommitGate;
>>  import org.apache.jackrabbit.mk.util.PathUtils;
>> -import org.apache.jackrabbit.mk.util.SimpleLRUCache;
>>
>>  /**
>>   *
>> @@ -49,11 +47,6 @@ public class MicroKernelImpl implements
>>
>>      protected Repository rep;
>>      private final CommitGate gate = new CommitGate();
>> -
>> -    /**
>> -     * Key: revision id, Value: diff string
>> -     */
>> -    private final Map<Id, String>  diffCache =
>> Collections.synchronizedMap(SimpleLRUCache.<Id, String>newInstance(100));
>>
>>      public MicroKernelImpl(String homeDir) throws MicroKernelException {
>>          init(homeDir);
>> @@ -97,7 +90,6 @@ public class MicroKernelImpl implements
>>              }
>>              rep = null;
>>          }
>> -        diffCache.clear();
>>      }
>>
>>      public String getHeadRevision() throws MicroKernelException {
>> @@ -211,13 +203,8 @@ public class MicroKernelImpl implements
>>              commitBuff.object().
>>                      key("id").value(commit.getId().toString()).
>>                      key("ts").value(commit.getCommitTS()).
>> -                    key("msg").value(commit.getMsg());
>> -            String diff = diffCache.get(commit.getId());
>> -            if (diff == null) {
>> -                diff = diff(commit.getParentId(), commit.getId(),
>> filter);
>> -                diffCache.put(commit.getId(), diff);
>> -            }
>> -            commitBuff.key("changes").value(diff).endObject();
>> +                    key("msg").value(commit.getMsg()).
>> +
>>  key("changes").value(commit.getChanges()).endObject();
>>          }
>>          return commitBuff.endArray().toString();
>>      }
>> @@ -478,12 +465,7 @@ public class MicroKernelImpl implements
>>                              }
>>                              String parentPath =
>> PathUtils.getParentPath(nodePath);
>>                              String nodeName =
>> PathUtils.getName(nodePath);
>> -                            // build the list of added nodes recursively
>> -                            LinkedList<AddNodeOperation>  list = new
>> LinkedList<AddNodeOperation>();
>> -                            addNode(list, parentPath, nodeName, t);
>> -                            for (AddNodeOperation op : list) {
>> -                                cb.addNode(op.path, op.name, op.props);
>> -                            }
>> +                            cb.addNode(parentPath, nodeName,
>> parseNode(t));
>>                          } else {
>>                              String value;
>>                              if (t.matches(JsopTokenizer.NULL)) {
>> @@ -637,30 +619,20 @@ public class MicroKernelImpl implements
>>          }
>>      }
>>
>> -    static void addNode(LinkedList<AddNodeOperation>  list, String path,
>> String name, JsopTokenizer t) throws Exception {
>> -        AddNodeOperation op = new AddNodeOperation();
>> -        op.path = path;
>> -        op.name = name;
>> -        list.add(op);
>> +    NodeTree parseNode(JsopTokenizer t) throws Exception {
>> +        NodeTree node = new NodeTree();
>>          if (!t.matches('}')) {
>>              do {
>>                  String key = t.readString();
>>                  t.read(':');
>>                  if (t.matches('{')) {
>> -                    addNode(list, PathUtils.concat(path, name), key, t);
>> +                    node.nodes.put(key, parseNode(t));
>>                  } else {
>> -                    op.props.put(key, t.readRawValue().trim());
>> +                    node.props.put(key, t.readRawValue().trim());
>>                  }
>>              } while (t.matches(','));
>>              t.read('}');
>>          }
>> +        return node;
>>      }
>> -
>> -    //--------------------------------------------------------<  inner
>> classes>
>> -    static class AddNodeOperation {
>> -        String path;
>> -        String name;
>> -        Map<String, String>  props = new HashMap<String, String>();
>> -    }
>> -
>>  }
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -32,6 +32,9 @@ public abstract class AbstractCommit imp
>>      // commit message
>>      protected String msg;
>>
>> +    // changes
>> +    protected String changes;
>> +
>>      // id of parent commit
>>      protected Id parentId;
>>
>> @@ -42,6 +45,7 @@ public abstract class AbstractCommit imp
>>          this.parentId = other.getParentId();
>>          this.rootNodeId = other.getRootNodeId();
>>          this.msg = other.getMsg();
>> +        this.changes = other.getChanges();
>>          this.commitTS = other.getCommitTS();
>>      }
>>
>> @@ -61,10 +65,15 @@ public abstract class AbstractCommit imp
>>          return msg;
>>      }
>>
>> +    public String getChanges() {
>> +        return changes;
>> +    }
>> +
>>      public void serialize(Binding binding) throws Exception {
>>          binding.write("rootNodeId", rootNodeId.getBytes());
>>          binding.write("commitTS", commitTS);
>>          binding.write("msg", msg == null ? "" : msg);
>> +        binding.write("changes", changes == null ? "" : changes);
>>          binding.write("parentId", parentId == null ? "" :
>> parentId.toString());
>>      }
>>  }
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -31,5 +31,7 @@ public interface Commit {
>>
>>      public String getMsg();
>>
>> +    public String getChanges();
>> +
>>      void serialize(Binding binding) throws Exception;
>>  }
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>> Sun Apr  8 20:31:50 2012
>> @@ -50,130 +50,39 @@ public class CommitBuilder {
>>          this.store = store;
>>      }
>>
>> -    public void addNode(String parentNodePath, String nodeName) throws
>> Exception {
>> -        addNode(parentNodePath, nodeName, Collections.<String,
>> String>emptyMap());
>> -    }
>> -
>> -    public void addNode(String parentNodePath, String nodeName,
>> Map<String, String>  properties) throws Exception {
>> -        MutableNode modParent = getOrCreateStagedNode(parentNodePath);
>> -        if (modParent.getChildNodeEntry(nodeName) != null) {
>> -            throw new Exception("there's already a child node with name
>> '" + nodeName + "'");
>> -        }
>> -        String newPath = PathUtils.concat(parentNodePath, nodeName);
>> -        MutableNode newChild = new MutableNode(store, newPath);
>> -        newChild.getProperties().putAll(properties);
>> -
>> -        // id will be computed on commit
>> -        modParent.add(new ChildNode(nodeName, null));
>> -        staged.put(newPath, newChild);
>> +    public void addNode(String parentNodePath, String nodeName, NodeTree
>> node) throws Exception {
>> +        Change change = new AddNode(parentNodePath, nodeName, node);
>> +        change.apply();
>>          // update change log
>> -        changeLog.add(new AddNode(parentNodePath, nodeName, properties));
>> +        changeLog.add(change);
>>      }
>>
>>      public void removeNode(String nodePath) throws NotFoundException,
>> Exception {
>> -        String parentPath = PathUtils.getParentPath(nodePath);
>> -        String nodeName = PathUtils.getName(nodePath);
>> -
>> -        MutableNode parent = getOrCreateStagedNode(parentPath);
>> -        if (parent.remove(nodeName) == null) {
>> -            throw new NotFoundException(nodePath);
>> -        }
>> -
>> -        // update staging area
>> -        removeStagedNodes(nodePath);
>> -
>> +        Change change = new RemoveNode(nodePath);
>> +        change.apply();
>>          // update change log
>> -        changeLog.add(new RemoveNode(nodePath));
>> +        changeLog.add(change);
>>      }
>>
>>      public void moveNode(String srcPath, String destPath) throws
>> NotFoundException, Exception {
>> -        if (PathUtils.isAncestor(srcPath, destPath)) {
>> -            throw new Exception("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);
>> -
>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> -        if (srcParentPath.equals(destParentPath)) {
>> -            if (srcParent.getChildNodeEntry(destNodeName) != null) {
>> -                throw new Exception("node already exists at move
>> destination path: " + destPath);
>> -            }
>> -            if (srcParent.rename(srcNodeName, destNodeName) == null) {
>> -                throw new NotFoundException(srcPath);
>> -            }
>> -        } else {
>> -            ChildNode srcCNE = srcParent.remove(srcNodeName);
>> -            if (srcCNE == null) {
>> -                throw new NotFoundException(srcPath);
>> -            }
>> -
>> -            MutableNode destParent =
>> getOrCreateStagedNode(destParentPath);
>> -            if (destParent.getChildNodeEntry(destNodeName) != null) {
>> -                throw new Exception("node already exists at move
>> destination path: " + destPath);
>> -            }
>> -            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>> -        }
>> -
>> -        // update staging area
>> -        moveStagedNodes(srcPath, destPath);
>> -
>> +        Change change = new MoveNode(srcPath, destPath);
>> +        change.apply();
>>          // update change log
>> -        changeLog.add(new MoveNode(srcPath, destPath));
>> +        changeLog.add(change);
>>      }
>>
>>      public void copyNode(String srcPath, String destPath) throws
>> NotFoundException, Exception {
>> -        String srcParentPath = PathUtils.getParentPath(srcPath);
>> -        String srcNodeName = PathUtils.getName(srcPath);
>> -
>> -        String destParentPath = PathUtils.getParentPath(destPath);
>> -        String destNodeName = PathUtils.getName(destPath);
>> -
>> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> -        ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>> -        if (srcCNE == null) {
>> -            throw new NotFoundException(srcPath);
>> -        }
>> -
>> -        MutableNode destParent = getOrCreateStagedNode(destParentPath);
>> -        destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>> -
>> -        if (srcCNE.getId() == null) {
>> -            // a 'new' node is being copied
>> -
>> -            // update staging area
>> -            copyStagedNodes(srcPath, destPath);
>> -        }
>> -
>> +        Change change = new CopyNode(srcPath, destPath);
>> +        change.apply();
>>          // update change log
>> -        changeLog.add(new CopyNode(srcPath, destPath));
>> +        changeLog.add(change);
>>      }
>>
>>      public void setProperty(String nodePath, String propName, String
>> propValue) throws Exception {
>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>> -
>> -        Map<String, String>  properties = node.getProperties();
>> -        if (propValue == null) {
>> -            properties.remove(propName);
>> -        } else {
>> -            properties.put(propName, propValue);
>> -        }
>> -
>> -        // update change log
>> -        changeLog.add(new SetProperty(nodePath, propName, propValue));
>> -    }
>> -
>> -    public void setProperties(String nodePath, Map<String, String>
>>  properties) throws Exception {
>> -        MutableNode node = getOrCreateStagedNode(nodePath);
>> -
>> -        node.getProperties().clear();
>> -        node.getProperties().putAll(properties);
>> -
>> +        Change change = new SetProperty(nodePath, propName, propValue);
>> +        change.apply();
>>          // update change log
>> -        changeLog.add(new SetProperties(nodePath, properties));
>> +        changeLog.add(change);
>>      }
>>
>>      public Id /* new revId */ doCommit() throws Exception {
>> @@ -190,9 +99,7 @@ public class CommitBuilder {
>>              // clear staging area
>>              staged.clear();
>>              // replay change log on new base revision
>> -            // copy log in order to avoid concurrent modifications
>> -            List<Change>  log = new ArrayList<Change>(changeLog);
>> -            for (Change change : log) {
>> +            for (Change change : changeLog) {
>>                  change.apply();
>>              }
>>          }
>> @@ -222,19 +129,29 @@ public class CommitBuilder {
>>              newCommit.setParentId(baseRevId);
>>              newCommit.setCommitTS(System.currentTimeMillis());
>>              newCommit.setMsg(msg);
>> +            StringBuilder diff = new StringBuilder();
>> +            for (Change change : changeLog) {
>> +                if (diff.length()>  0) {
>> +                    diff.append('\n');
>> +                }
>> +                diff.append(change.asDiff());
>> +            }
>> +            newCommit.setChanges(diff.toString());
>>              newCommit.setRootNodeId(rootNodeId);
>>              newRevId = store.putHeadCommit(newCommit);
>>          } finally {
>>              store.unlockHead();
>>          }
>>
>> -        // reset instance in order to be reusable
>> +        // reset instance
>>          staged.clear();
>>          changeLog.clear();
>>
>>          return newRevId;
>>      }
>>
>> +    //--------------------------------------------------------<  inner
>> classes>
>> +
>>      MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
>>          MutableNode node = staged.get(nodePath);
>>          if (node == null) {
>> @@ -418,23 +335,79 @@ public class CommitBuilder {
>>      }
>>
>>      //--------------------------------------------------------<  inner
>> classes>
>> +
>> +    public static class NodeTree {
>> +        public Map<String, String>  props = new HashMap<String,
>> String>();
>> +        public Map<String, NodeTree>  nodes = new HashMap<String,
>> NodeTree>();
>> +
>> +        void toJson(StringBuffer buf) {
>> +            toJson(buf, this);
>> +        }
>> +
>> +        private static void toJson(StringBuffer buf, NodeTree node) {
>> +            buf.append('{');
>> +            for (String name : node.props.keySet()) {
>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>> +                    buf.append(',');
>> +                }
>> +
>>  buf.append('"').append(name).append("\":").append(node.props.get(name));
>> +            }
>> +            for (String name : node.nodes.keySet()) {
>> +                if (buf.charAt(buf.length() - 1) != '{')  {
>> +                    buf.append(',');
>> +                }
>> +                buf.append('"').append(name).append("\":");
>> +                toJson(buf, node.nodes.get(name));
>> +            }
>> +            buf.append('}');
>> +        }
>> +    }
>> +
>>      abstract class Change {
>>          abstract void apply() throws Exception;
>> +        abstract String asDiff();
>>      }
>>
>>      class AddNode extends Change {
>>          String parentNodePath;
>>          String nodeName;
>> -        Map<String, String>  properties;
>> +        NodeTree node;
>>
>> -        AddNode(String parentNodePath, String nodeName, Map<String,
>> String>  properties) {
>> +        AddNode(String parentNodePath, String nodeName, NodeTree node) {
>>              this.parentNodePath = parentNodePath;
>>              this.nodeName = nodeName;
>> -            this.properties = properties;
>> +            this.node = node;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            addNode(parentNodePath, nodeName, properties);
>> +            recursiveAddNode(parentNodePath, nodeName, node);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("+");
>> +            diff.append('"').append(PathUtils.concat(parentNodePath,
>> nodeName)).append("\":");
>> +            node.toJson(diff);
>> +            return diff.toString();
>> +        }
>> +
>> +        private void recursiveAddNode(String parentPath, String name,
>> NodeTree node) throws Exception {
>> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
>> +            if (modParent.getChildNodeEntry(name) != null) {
>> +                throw new Exception("there's already a child node with
>> name '" + name + "'");
>> +            }
>> +            String newPath = PathUtils.concat(parentPath, name);
>> +            MutableNode newChild = new MutableNode(store, newPath);
>> +            newChild.getProperties().putAll(node.props);
>> +
>> +            // id will be computed on commit
>> +            modParent.add(new ChildNode(name, null));
>> +            staged.put(newPath, newChild);
>> +
>> +            for (String childName : node.nodes.keySet()) {
>> +                recursiveAddNode(PathUtils.concat(parentPath, name),
>> childName, node.nodes.get(childName));
>> +            }
>>          }
>>      }
>>
>> @@ -445,8 +418,25 @@ public class CommitBuilder {
>>              this.nodePath = nodePath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            removeNode(nodePath);
>> +            String parentPath = PathUtils.getParentPath(nodePath);
>> +            String nodeName = PathUtils.getName(nodePath);
>> +
>> +            MutableNode parent = getOrCreateStagedNode(parentPath);
>> +            if (parent.remove(nodeName) == null) {
>> +                throw new NotFoundException(nodePath);
>> +            }
>> +
>> +            // update staging area
>> +            removeStagedNodes(nodePath);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("-");
>> +            diff.append('"').append(nodePath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -459,8 +449,48 @@ public class CommitBuilder {
>>              this.destPath = destPath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            moveNode(srcPath, destPath);
>> +            if (PathUtils.isAncestor(srcPath, destPath)) {
>> +                throw new Exception("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);
>> +
>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> +            if (srcParentPath.equals(destParentPath)) {
>> +                if (srcParent.getChildNodeEntry(destNodeName) != null) {
>> +                    throw new Exception("node already exists at move
>> destination path: " + destPath);
>> +                }
>> +                if (srcParent.rename(srcNodeName, destNodeName) == null)
>> {
>> +                    throw new NotFoundException(srcPath);
>> +                }
>> +            } else {
>> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
>> +                if (srcCNE == null) {
>> +                    throw new NotFoundException(srcPath);
>> +                }
>> +
>> +                MutableNode destParent =
>> getOrCreateStagedNode(destParentPath);
>> +                if (destParent.getChildNodeEntry(destNodeName) != null) {
>> +                    throw new Exception("node already exists at move
>> destination path: " + destPath);
>> +                }
>> +                destParent.add(new ChildNode(destNodeName,
>> srcCNE.getId()));
>> +            }
>> +
>> +            // update staging area
>> +            moveStagedNodes(srcPath, destPath);
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer(">");
>> +
>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -473,8 +503,36 @@ public class CommitBuilder {
>>              this.destPath = destPath;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            copyNode(srcPath, destPath);
>> +            String srcParentPath = PathUtils.getParentPath(srcPath);
>> +            String srcNodeName = PathUtils.getName(srcPath);
>> +
>> +            String destParentPath = PathUtils.getParentPath(destPath);
>> +            String destNodeName = PathUtils.getName(destPath);
>> +
>> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
>> +            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
>> +            if (srcCNE == null) {
>> +                throw new NotFoundException(srcPath);
>> +            }
>> +
>> +            MutableNode destParent =
>> getOrCreateStagedNode(destParentPath);
>> +            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
>> +
>> +            if (srcCNE.getId() == null) {
>> +                // a 'new' node is being copied
>> +
>> +                // update staging area
>> +                copyStagedNodes(srcPath, destPath);
>> +            }
>> +        }
>> +
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("*");
>> +
>>  diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
>> +            return diff.toString();
>>          }
>>      }
>>
>> @@ -489,22 +547,23 @@ public class CommitBuilder {
>>              this.propValue = propValue;
>>          }
>>
>> +        @Override
>>          void apply() throws Exception {
>> -            setProperty(nodePath, propName, propValue);
>> -        }
>> -    }
>> +            MutableNode node = getOrCreateStagedNode(nodePath);
>>
>> -    class SetProperties extends Change {
>> -        String nodePath;
>> -        Map<String, String>  properties;
>> -
>> -        SetProperties(String nodePath, Map<String, String>  properties) {
>> -            this.nodePath = nodePath;
>> -            this.properties = properties;
>> +            Map<String, String>  properties = node.getProperties();
>> +            if (propValue == null) {
>> +                properties.remove(propName);
>> +            } else {
>> +                properties.put(propName, propValue);
>> +            }
>>          }
>>
>> -        void apply() throws Exception {
>> -            setProperties(nodePath, properties);
>> +        @Override
>> +        String asDiff() {
>> +            StringBuffer diff = new StringBuffer("^");
>> +            diff.append('"').append(PathUtils.concat(nodePath,
>> propName)).append("\":").append(propValue);
>> +            return diff.toString();
>>          }
>>      }
>>  }
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>>          setRootNodeId(other.getRootNodeId());
>>          setCommitTS(other.getCommitTS());
>>          setMsg(other.getMsg());
>> +        setChanges(other.getChanges());
>>          this.id = other.getId();
>>      }
>>
>> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>>      public void setMsg(String msg) {
>>          this.msg = msg;
>>      }
>> -
>> +
>> +    public void setChanges(String changes) {
>> +        this.changes = changes;
>> +    }
>> +
>>      /**
>>       * Return the commit id.
>>       *
>>
>> Modified:
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> URL:
>> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
>>
>> ==============================================================================
>> ---
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> (original)
>> +++
>> jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>> Sun Apr  8 20:31:50 2012
>> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>>          Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>>          long commitTS = binding.readLongValue("commitTS");
>>          String msg = binding.readStringValue("msg");
>> +        String changes = binding.readStringValue("changes");
>>          String parentId = binding.readStringValue("parentId");
>>          return new StoredCommit(id, "".equals(parentId) ? null :
>> Id.fromString(parentId),
>> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
>> +                commitTS, rootNodeId, "".equals(msg) ? null : msg,
>> changes);
>>      }
>>
>> -    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>> String msg) {
>> +    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId,
>> String msg, String changes) {
>>          this.id = id;
>>          this.parentId = parentId;
>>          this.commitTS = commitTS;
>>          this.rootNodeId = rootNodeId;
>>          this.msg = msg;
>> +        this.changes = changes;
>>      }
>>
>>      public StoredCommit(Id id, Commit commit) {
>>
>>
>

Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Thomas Mueller <mu...@adobe.com>.
Hi,

>This causes org.apache.jackrabbit.mk.ConcurrentWriteTest to run
>indefinitely. AFAICT this is caused by an endless loop in
>IOUtils.readVarInt

Endless loop should never occur I think, fixed in OAK-54.

Regards,
Thomas


Re: svn commit: r1311085 - in /jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk: core/MicroKernelImpl.java model/AbstractCommit.java model/Commit.java model/CommitBuilder.java model/MutableCommit.java model/StoredCommit.java

Posted by Michael Dürig <md...@apache.org>.
Hi,

This causes org.apache.jackrabbit.mk.ConcurrentWriteTest to run 
indefinitely. AFAICT this is caused by an endless loop in IOUtils.readVarInt

Michael


On 8.4.12 21:31, stefan@apache.org wrote:
> Author: stefan
> Date: Sun Apr  8 20:31:50 2012
> New Revision: 1311085
>
> URL: http://svn.apache.org/viewvc?rev=1311085&view=rev
> Log:
> OAK-43: Incomplete journal when move and copy operations are involved
>
> Modified:
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
>      jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/core/MicroKernelImpl.java Sun Apr  8 20:31:50 2012
> @@ -18,9 +18,7 @@ package org.apache.jackrabbit.mk.core;
>
>   import java.io.InputStream;
>   import java.util.ArrayList;
> -import java.util.Collections;
>   import java.util.HashMap;
> -import java.util.LinkedList;
>   import java.util.List;
>   import java.util.Map;
>
> @@ -31,6 +29,7 @@ import org.apache.jackrabbit.mk.json.Jso
>   import org.apache.jackrabbit.mk.model.ChildNodeEntry;
>   import org.apache.jackrabbit.mk.model.Commit;
>   import org.apache.jackrabbit.mk.model.CommitBuilder;
> +import org.apache.jackrabbit.mk.model.CommitBuilder.NodeTree;
>   import org.apache.jackrabbit.mk.model.Id;
>   import org.apache.jackrabbit.mk.model.NodeState;
>   import org.apache.jackrabbit.mk.model.PropertyState;
> @@ -40,7 +39,6 @@ import org.apache.jackrabbit.mk.store.No
>   import org.apache.jackrabbit.mk.store.RevisionProvider;
>   import org.apache.jackrabbit.mk.util.CommitGate;
>   import org.apache.jackrabbit.mk.util.PathUtils;
> -import org.apache.jackrabbit.mk.util.SimpleLRUCache;
>
>   /**
>    *
> @@ -49,11 +47,6 @@ public class MicroKernelImpl implements
>
>       protected Repository rep;
>       private final CommitGate gate = new CommitGate();
> -
> -    /**
> -     * Key: revision id, Value: diff string
> -     */
> -    private final Map<Id, String>  diffCache = Collections.synchronizedMap(SimpleLRUCache.<Id, String>newInstance(100));
>
>       public MicroKernelImpl(String homeDir) throws MicroKernelException {
>           init(homeDir);
> @@ -97,7 +90,6 @@ public class MicroKernelImpl implements
>               }
>               rep = null;
>           }
> -        diffCache.clear();
>       }
>
>       public String getHeadRevision() throws MicroKernelException {
> @@ -211,13 +203,8 @@ public class MicroKernelImpl implements
>               commitBuff.object().
>                       key("id").value(commit.getId().toString()).
>                       key("ts").value(commit.getCommitTS()).
> -                    key("msg").value(commit.getMsg());
> -            String diff = diffCache.get(commit.getId());
> -            if (diff == null) {
> -                diff = diff(commit.getParentId(), commit.getId(), filter);
> -                diffCache.put(commit.getId(), diff);
> -            }
> -            commitBuff.key("changes").value(diff).endObject();
> +                    key("msg").value(commit.getMsg()).
> +                    key("changes").value(commit.getChanges()).endObject();
>           }
>           return commitBuff.endArray().toString();
>       }
> @@ -478,12 +465,7 @@ public class MicroKernelImpl implements
>                               }
>                               String parentPath = PathUtils.getParentPath(nodePath);
>                               String nodeName = PathUtils.getName(nodePath);
> -                            // build the list of added nodes recursively
> -                            LinkedList<AddNodeOperation>  list = new LinkedList<AddNodeOperation>();
> -                            addNode(list, parentPath, nodeName, t);
> -                            for (AddNodeOperation op : list) {
> -                                cb.addNode(op.path, op.name, op.props);
> -                            }
> +                            cb.addNode(parentPath, nodeName, parseNode(t));
>                           } else {
>                               String value;
>                               if (t.matches(JsopTokenizer.NULL)) {
> @@ -637,30 +619,20 @@ public class MicroKernelImpl implements
>           }
>       }
>
> -    static void addNode(LinkedList<AddNodeOperation>  list, String path, String name, JsopTokenizer t) throws Exception {
> -        AddNodeOperation op = new AddNodeOperation();
> -        op.path = path;
> -        op.name = name;
> -        list.add(op);
> +    NodeTree parseNode(JsopTokenizer t) throws Exception {
> +        NodeTree node = new NodeTree();
>           if (!t.matches('}')) {
>               do {
>                   String key = t.readString();
>                   t.read(':');
>                   if (t.matches('{')) {
> -                    addNode(list, PathUtils.concat(path, name), key, t);
> +                    node.nodes.put(key, parseNode(t));
>                   } else {
> -                    op.props.put(key, t.readRawValue().trim());
> +                    node.props.put(key, t.readRawValue().trim());
>                   }
>               } while (t.matches(','));
>               t.read('}');
>           }
> +        return node;
>       }
> -
> -    //--------------------------------------------------------<  inner classes>
> -    static class AddNodeOperation {
> -        String path;
> -        String name;
> -        Map<String, String>  props = new HashMap<String, String>();
> -    }
> -
>   }
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/AbstractCommit.java Sun Apr  8 20:31:50 2012
> @@ -32,6 +32,9 @@ public abstract class AbstractCommit imp
>       // commit message
>       protected String msg;
>
> +    // changes
> +    protected String changes;
> +
>       // id of parent commit
>       protected Id parentId;
>
> @@ -42,6 +45,7 @@ public abstract class AbstractCommit imp
>           this.parentId = other.getParentId();
>           this.rootNodeId = other.getRootNodeId();
>           this.msg = other.getMsg();
> +        this.changes = other.getChanges();
>           this.commitTS = other.getCommitTS();
>       }
>
> @@ -61,10 +65,15 @@ public abstract class AbstractCommit imp
>           return msg;
>       }
>
> +    public String getChanges() {
> +        return changes;
> +    }
> +
>       public void serialize(Binding binding) throws Exception {
>           binding.write("rootNodeId", rootNodeId.getBytes());
>           binding.write("commitTS", commitTS);
>           binding.write("msg", msg == null ? "" : msg);
> +        binding.write("changes", changes == null ? "" : changes);
>           binding.write("parentId", parentId == null ? "" : parentId.toString());
>       }
>   }
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/Commit.java Sun Apr  8 20:31:50 2012
> @@ -31,5 +31,7 @@ public interface Commit {
>
>       public String getMsg();
>
> +    public String getChanges();
> +
>       void serialize(Binding binding) throws Exception;
>   }
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java Sun Apr  8 20:31:50 2012
> @@ -50,130 +50,39 @@ public class CommitBuilder {
>           this.store = store;
>       }
>
> -    public void addNode(String parentNodePath, String nodeName) throws Exception {
> -        addNode(parentNodePath, nodeName, Collections.<String, String>emptyMap());
> -    }
> -
> -    public void addNode(String parentNodePath, String nodeName, Map<String, String>  properties) throws Exception {
> -        MutableNode modParent = getOrCreateStagedNode(parentNodePath);
> -        if (modParent.getChildNodeEntry(nodeName) != null) {
> -            throw new Exception("there's already a child node with name '" + nodeName + "'");
> -        }
> -        String newPath = PathUtils.concat(parentNodePath, nodeName);
> -        MutableNode newChild = new MutableNode(store, newPath);
> -        newChild.getProperties().putAll(properties);
> -
> -        // id will be computed on commit
> -        modParent.add(new ChildNode(nodeName, null));
> -        staged.put(newPath, newChild);
> +    public void addNode(String parentNodePath, String nodeName, NodeTree node) throws Exception {
> +        Change change = new AddNode(parentNodePath, nodeName, node);
> +        change.apply();
>           // update change log
> -        changeLog.add(new AddNode(parentNodePath, nodeName, properties));
> +        changeLog.add(change);
>       }
>
>       public void removeNode(String nodePath) throws NotFoundException, Exception {
> -        String parentPath = PathUtils.getParentPath(nodePath);
> -        String nodeName = PathUtils.getName(nodePath);
> -
> -        MutableNode parent = getOrCreateStagedNode(parentPath);
> -        if (parent.remove(nodeName) == null) {
> -            throw new NotFoundException(nodePath);
> -        }
> -
> -        // update staging area
> -        removeStagedNodes(nodePath);
> -
> +        Change change = new RemoveNode(nodePath);
> +        change.apply();
>           // update change log
> -        changeLog.add(new RemoveNode(nodePath));
> +        changeLog.add(change);
>       }
>
>       public void moveNode(String srcPath, String destPath) throws NotFoundException, Exception {
> -        if (PathUtils.isAncestor(srcPath, destPath)) {
> -            throw new Exception("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);
> -
> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> -        if (srcParentPath.equals(destParentPath)) {
> -            if (srcParent.getChildNodeEntry(destNodeName) != null) {
> -                throw new Exception("node already exists at move destination path: " + destPath);
> -            }
> -            if (srcParent.rename(srcNodeName, destNodeName) == null) {
> -                throw new NotFoundException(srcPath);
> -            }
> -        } else {
> -            ChildNode srcCNE = srcParent.remove(srcNodeName);
> -            if (srcCNE == null) {
> -                throw new NotFoundException(srcPath);
> -            }
> -
> -            MutableNode destParent = getOrCreateStagedNode(destParentPath);
> -            if (destParent.getChildNodeEntry(destNodeName) != null) {
> -                throw new Exception("node already exists at move destination path: " + destPath);
> -            }
> -            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> -        }
> -
> -        // update staging area
> -        moveStagedNodes(srcPath, destPath);
> -
> +        Change change = new MoveNode(srcPath, destPath);
> +        change.apply();
>           // update change log
> -        changeLog.add(new MoveNode(srcPath, destPath));
> +        changeLog.add(change);
>       }
>
>       public void copyNode(String srcPath, String destPath) throws NotFoundException, Exception {
> -        String srcParentPath = PathUtils.getParentPath(srcPath);
> -        String srcNodeName = PathUtils.getName(srcPath);
> -
> -        String destParentPath = PathUtils.getParentPath(destPath);
> -        String destNodeName = PathUtils.getName(destPath);
> -
> -        MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> -        ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
> -        if (srcCNE == null) {
> -            throw new NotFoundException(srcPath);
> -        }
> -
> -        MutableNode destParent = getOrCreateStagedNode(destParentPath);
> -        destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> -
> -        if (srcCNE.getId() == null) {
> -            // a 'new' node is being copied
> -
> -            // update staging area
> -            copyStagedNodes(srcPath, destPath);
> -        }
> -
> +        Change change = new CopyNode(srcPath, destPath);
> +        change.apply();
>           // update change log
> -        changeLog.add(new CopyNode(srcPath, destPath));
> +        changeLog.add(change);
>       }
>
>       public void setProperty(String nodePath, String propName, String propValue) throws Exception {
> -        MutableNode node = getOrCreateStagedNode(nodePath);
> -
> -        Map<String, String>  properties = node.getProperties();
> -        if (propValue == null) {
> -            properties.remove(propName);
> -        } else {
> -            properties.put(propName, propValue);
> -        }
> -
> -        // update change log
> -        changeLog.add(new SetProperty(nodePath, propName, propValue));
> -    }
> -
> -    public void setProperties(String nodePath, Map<String, String>  properties) throws Exception {
> -        MutableNode node = getOrCreateStagedNode(nodePath);
> -
> -        node.getProperties().clear();
> -        node.getProperties().putAll(properties);
> -
> +        Change change = new SetProperty(nodePath, propName, propValue);
> +        change.apply();
>           // update change log
> -        changeLog.add(new SetProperties(nodePath, properties));
> +        changeLog.add(change);
>       }
>
>       public Id /* new revId */ doCommit() throws Exception {
> @@ -190,9 +99,7 @@ public class CommitBuilder {
>               // clear staging area
>               staged.clear();
>               // replay change log on new base revision
> -            // copy log in order to avoid concurrent modifications
> -            List<Change>  log = new ArrayList<Change>(changeLog);
> -            for (Change change : log) {
> +            for (Change change : changeLog) {
>                   change.apply();
>               }
>           }
> @@ -222,19 +129,29 @@ public class CommitBuilder {
>               newCommit.setParentId(baseRevId);
>               newCommit.setCommitTS(System.currentTimeMillis());
>               newCommit.setMsg(msg);
> +            StringBuilder diff = new StringBuilder();
> +            for (Change change : changeLog) {
> +                if (diff.length()>  0) {
> +                    diff.append('\n');
> +                }
> +                diff.append(change.asDiff());
> +            }
> +            newCommit.setChanges(diff.toString());
>               newCommit.setRootNodeId(rootNodeId);
>               newRevId = store.putHeadCommit(newCommit);
>           } finally {
>               store.unlockHead();
>           }
>
> -        // reset instance in order to be reusable
> +        // reset instance
>           staged.clear();
>           changeLog.clear();
>
>           return newRevId;
>       }
>
> +    //--------------------------------------------------------<  inner classes>
> +
>       MutableNode getOrCreateStagedNode(String nodePath) throws Exception {
>           MutableNode node = staged.get(nodePath);
>           if (node == null) {
> @@ -418,23 +335,79 @@ public class CommitBuilder {
>       }
>
>       //--------------------------------------------------------<  inner classes>
> +
> +    public static class NodeTree {
> +        public Map<String, String>  props = new HashMap<String, String>();
> +        public Map<String, NodeTree>  nodes = new HashMap<String, NodeTree>();
> +
> +        void toJson(StringBuffer buf) {
> +            toJson(buf, this);
> +        }
> +
> +        private static void toJson(StringBuffer buf, NodeTree node) {
> +            buf.append('{');
> +            for (String name : node.props.keySet()) {
> +                if (buf.charAt(buf.length() - 1) != '{')  {
> +                    buf.append(',');
> +                }
> +                buf.append('"').append(name).append("\":").append(node.props.get(name));
> +            }
> +            for (String name : node.nodes.keySet()) {
> +                if (buf.charAt(buf.length() - 1) != '{')  {
> +                    buf.append(',');
> +                }
> +                buf.append('"').append(name).append("\":");
> +                toJson(buf, node.nodes.get(name));
> +            }
> +            buf.append('}');
> +        }
> +    }
> +
>       abstract class Change {
>           abstract void apply() throws Exception;
> +        abstract String asDiff();
>       }
>
>       class AddNode extends Change {
>           String parentNodePath;
>           String nodeName;
> -        Map<String, String>  properties;
> +        NodeTree node;
>
> -        AddNode(String parentNodePath, String nodeName, Map<String, String>  properties) {
> +        AddNode(String parentNodePath, String nodeName, NodeTree node) {
>               this.parentNodePath = parentNodePath;
>               this.nodeName = nodeName;
> -            this.properties = properties;
> +            this.node = node;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            addNode(parentNodePath, nodeName, properties);
> +            recursiveAddNode(parentNodePath, nodeName, node);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("+");
> +            diff.append('"').append(PathUtils.concat(parentNodePath, nodeName)).append("\":");
> +            node.toJson(diff);
> +            return diff.toString();
> +        }
> +
> +        private void recursiveAddNode(String parentPath, String name, NodeTree node) throws Exception {
> +            MutableNode modParent = getOrCreateStagedNode(parentPath);
> +            if (modParent.getChildNodeEntry(name) != null) {
> +                throw new Exception("there's already a child node with name '" + name + "'");
> +            }
> +            String newPath = PathUtils.concat(parentPath, name);
> +            MutableNode newChild = new MutableNode(store, newPath);
> +            newChild.getProperties().putAll(node.props);
> +
> +            // id will be computed on commit
> +            modParent.add(new ChildNode(name, null));
> +            staged.put(newPath, newChild);
> +
> +            for (String childName : node.nodes.keySet()) {
> +                recursiveAddNode(PathUtils.concat(parentPath, name), childName, node.nodes.get(childName));
> +            }
>           }
>       }
>
> @@ -445,8 +418,25 @@ public class CommitBuilder {
>               this.nodePath = nodePath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            removeNode(nodePath);
> +            String parentPath = PathUtils.getParentPath(nodePath);
> +            String nodeName = PathUtils.getName(nodePath);
> +
> +            MutableNode parent = getOrCreateStagedNode(parentPath);
> +            if (parent.remove(nodeName) == null) {
> +                throw new NotFoundException(nodePath);
> +            }
> +
> +            // update staging area
> +            removeStagedNodes(nodePath);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("-");
> +            diff.append('"').append(nodePath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -459,8 +449,48 @@ public class CommitBuilder {
>               this.destPath = destPath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            moveNode(srcPath, destPath);
> +            if (PathUtils.isAncestor(srcPath, destPath)) {
> +                throw new Exception("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);
> +
> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> +            if (srcParentPath.equals(destParentPath)) {
> +                if (srcParent.getChildNodeEntry(destNodeName) != null) {
> +                    throw new Exception("node already exists at move destination path: " + destPath);
> +                }
> +                if (srcParent.rename(srcNodeName, destNodeName) == null) {
> +                    throw new NotFoundException(srcPath);
> +                }
> +            } else {
> +                ChildNode srcCNE = srcParent.remove(srcNodeName);
> +                if (srcCNE == null) {
> +                    throw new NotFoundException(srcPath);
> +                }
> +
> +                MutableNode destParent = getOrCreateStagedNode(destParentPath);
> +                if (destParent.getChildNodeEntry(destNodeName) != null) {
> +                    throw new Exception("node already exists at move destination path: " + destPath);
> +                }
> +                destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> +            }
> +
> +            // update staging area
> +            moveStagedNodes(srcPath, destPath);
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer(">");
> +            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -473,8 +503,36 @@ public class CommitBuilder {
>               this.destPath = destPath;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            copyNode(srcPath, destPath);
> +            String srcParentPath = PathUtils.getParentPath(srcPath);
> +            String srcNodeName = PathUtils.getName(srcPath);
> +
> +            String destParentPath = PathUtils.getParentPath(destPath);
> +            String destNodeName = PathUtils.getName(destPath);
> +
> +            MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
> +            ChildNode srcCNE = srcParent.getChildNodeEntry(srcNodeName);
> +            if (srcCNE == null) {
> +                throw new NotFoundException(srcPath);
> +            }
> +
> +            MutableNode destParent = getOrCreateStagedNode(destParentPath);
> +            destParent.add(new ChildNode(destNodeName, srcCNE.getId()));
> +
> +            if (srcCNE.getId() == null) {
> +                // a 'new' node is being copied
> +
> +                // update staging area
> +                copyStagedNodes(srcPath, destPath);
> +            }
> +        }
> +
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("*");
> +            diff.append('"').append(srcPath).append("\":\"").append(destPath).append('"');
> +            return diff.toString();
>           }
>       }
>
> @@ -489,22 +547,23 @@ public class CommitBuilder {
>               this.propValue = propValue;
>           }
>
> +        @Override
>           void apply() throws Exception {
> -            setProperty(nodePath, propName, propValue);
> -        }
> -    }
> +            MutableNode node = getOrCreateStagedNode(nodePath);
>
> -    class SetProperties extends Change {
> -        String nodePath;
> -        Map<String, String>  properties;
> -
> -        SetProperties(String nodePath, Map<String, String>  properties) {
> -            this.nodePath = nodePath;
> -            this.properties = properties;
> +            Map<String, String>  properties = node.getProperties();
> +            if (propValue == null) {
> +                properties.remove(propName);
> +            } else {
> +                properties.put(propName, propValue);
> +            }
>           }
>
> -        void apply() throws Exception {
> -            setProperties(nodePath, properties);
> +        @Override
> +        String asDiff() {
> +            StringBuffer diff = new StringBuffer("^");
> +            diff.append('"').append(PathUtils.concat(nodePath, propName)).append("\":").append(propValue);
> +            return diff.toString();
>           }
>       }
>   }
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/MutableCommit.java Sun Apr  8 20:31:50 2012
> @@ -39,6 +39,7 @@ public class MutableCommit extends Abstr
>           setRootNodeId(other.getRootNodeId());
>           setCommitTS(other.getCommitTS());
>           setMsg(other.getMsg());
> +        setChanges(other.getChanges());
>           this.id = other.getId();
>       }
>
> @@ -57,7 +58,11 @@ public class MutableCommit extends Abstr
>       public void setMsg(String msg) {
>           this.msg = msg;
>       }
> -
> +
> +    public void setChanges(String changes) {
> +        this.changes = changes;
> +    }
> +
>       /**
>        * Return the commit id.
>        *
>
> Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java?rev=1311085&r1=1311084&r2=1311085&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java (original)
> +++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/model/StoredCommit.java Sun Apr  8 20:31:50 2012
> @@ -29,17 +29,19 @@ public class StoredCommit extends Abstra
>           Id rootNodeId = new Id(binding.readBytesValue("rootNodeId"));
>           long commitTS = binding.readLongValue("commitTS");
>           String msg = binding.readStringValue("msg");
> +        String changes = binding.readStringValue("changes");
>           String parentId = binding.readStringValue("parentId");
>           return new StoredCommit(id, "".equals(parentId) ? null : Id.fromString(parentId),
> -                commitTS, rootNodeId, "".equals(msg) ? null : msg);
> +                commitTS, rootNodeId, "".equals(msg) ? null : msg, changes);
>       }
>
> -    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg) {
> +    public StoredCommit(Id id, Id parentId, long commitTS, Id rootNodeId, String msg, String changes) {
>           this.id = id;
>           this.parentId = parentId;
>           this.commitTS = commitTS;
>           this.rootNodeId = rootNodeId;
>           this.msg = msg;
> +        this.changes = changes;
>       }
>
>       public StoredCommit(Id id, Commit commit) {
>
>