You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2011/12/27 12:57:38 UTC

svn commit: r1224899 - in /jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk: ./ model/ store/ store/util/

Author: stefan
Date: Tue Dec 27 11:57:38 2011
New Revision: 1224899

URL: http://svn.apache.org/viewvc?rev=1224899&view=rev
Log:
flat hierarchy support (WIP)

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNode.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNodeEntry.java
Modified:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/AbstractNode.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/MutableNode.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/Node.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/NodeUtils.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/StoredNode.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MongoPersistenceManager.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/util/Serializer.java

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java Tue Dec 27 11:57:38 2011
@@ -20,6 +20,7 @@ import org.apache.jackrabbit.mk.api.Micr
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.json.JsopBuilder;
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
+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.Node;
@@ -36,6 +37,7 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -370,7 +372,7 @@ public class MicroKernelImpl implements 
     public String getNodes(String path, String revisionId, int depth, long offset, int count) throws MicroKernelException {
         try {
             JsopBuilder buf = new JsopBuilder().object();
-            toJson(buf, rep.getNode(revisionId, path), depth, offset, count, true);
+            toJson(buf, rep.getNode(revisionId, path), depth, (int) offset, count, true);
             return buf.endObject().toString();
         } catch (Exception e) {
             throw new MicroKernelException(e);
@@ -533,7 +535,7 @@ public class MicroKernelImpl implements 
 
     //-------------------------------------------------------< implementation >
 
-    void toJson(JsopBuilder builder, Node node, int depth, long offset, int count, boolean inclVirtualProps) throws Exception {
+    void toJson(JsopBuilder builder, Node node, int depth, int offset, int count, boolean inclVirtualProps) throws Exception {
         for (Map.Entry<String, String> prop : node.getProperties().entrySet()) {
             builder.key(prop.getKey()).encodedValue(prop.getValue());
         }
@@ -542,20 +544,13 @@ public class MicroKernelImpl implements 
             builder.key(":childNodeCount").value(childCount);
         }
         if (childCount > 0 && depth >= 0) {
-            for (Map.Entry<String, String> child : node.getChildNodeEntries().entrySet()) {
-                if (offset-- > 0) {
-                    continue;
-                }
-                if (count < 0 || count > 0) {
-                    count--;
-                    String childName = child.getKey();
-                    builder.key(childName).object();
-                    if (depth > 0) {
-                        String childId = child.getValue();
-                        toJson(builder, rep.getRevisionStore().getNode(childId), depth - 1, 0, -1, inclVirtualProps);
-                    }
-                    builder.endObject();
+            for (Iterator<ChildNodeEntry> it = node.getChildNodeEntries(offset, count); it.hasNext(); ) {
+                ChildNodeEntry cne = it.next();
+                builder.key(cne.getName()).object();
+                if (depth > 0) {
+                    toJson(builder, rep.getRevisionStore().getNode(cne.getId()), depth - 1, 0, -1, inclVirtualProps);
                 }
+                builder.endObject();
             }
         }
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java Tue Dec 27 11:57:38 2011
@@ -17,6 +17,8 @@
 package org.apache.jackrabbit.mk;
 
 import java.io.File;
+
+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.Node;
@@ -144,11 +146,11 @@ public class Repository {
             String[] names = PathUtils.split(path);
             Node parent = pm.getRootNode(revId);
             for (int i = 0; i < names.length; i++) {
-                String id = parent.getChildNodeEntries().get(names[i]);
-                if (id == null) {
+                ChildNodeEntry cne = parent.getChildNodeEntry(names[i]);
+                if (cne == null) {
                     return false;
                 }
-                parent = pm.getNode(id);
+                parent = pm.getNode(cne.getId());
             }
             return true;
         } catch (Exception e) {
@@ -188,12 +190,12 @@ public class Repository {
         Node parent = pm.getNode(ids[0]);
         // traverse path and remember id of each element
         for (int i = 0; i < names.length; i++) {
-            String id = parent.getChildNodeEntries().get(names[i]);
-            if (id == null) {
+            ChildNodeEntry cne = parent.getChildNodeEntry(names[i]);
+            if (cne == null) {
                 throw new NotFoundException(nodePath);
             }
-            ids[i + 1] = id;
-            parent = pm.getNode(id);
+            ids[i + 1] = cne.getId();
+            parent = pm.getNode(cne.getId());
         }
         return ids;
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/AbstractNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/AbstractNode.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/AbstractNode.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/AbstractNode.java Tue Dec 27 11:57:38 2011
@@ -20,58 +20,65 @@ import org.apache.jackrabbit.mk.store.Re
 import org.apache.jackrabbit.mk.store.NotFoundException;
 import org.apache.jackrabbit.mk.util.PathUtils;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.NoSuchElementException;
 
 /**
  *
  */
 public abstract class AbstractNode implements Node {
 
-    final protected Map<String, String> properties;
+    protected HashMap<String, String> properties;
 
     // map keeping insertion order
-    //final protected LinkedHashMap<String, Node> children;
-    final protected LinkedHashMap<String, String> childEntries;
+    protected LinkedHashMap<String, ChildNodeEntry> childEntries;
 
     protected AbstractNode() {
         this.properties = new HashMap<String, String>();
-        //this.children = new LinkedHashMap<String, Node>();
-        this.childEntries = new LinkedHashMap<String, String>();
+        this.childEntries = new LinkedHashMap<String, ChildNodeEntry>();
     }
 
     protected AbstractNode(Node other) {
-        this.properties = new HashMap<String, String>(other.getProperties());
-        //this.children = new LinkedHashMap<String, Node>(other.getChildNodes(0, -1));
-        this.childEntries = new LinkedHashMap<String, String>(other.getChildNodeEntries());
+        if (other instanceof AbstractNode) {
+            AbstractNode srcNode = (AbstractNode) other;
+            this.properties = (HashMap<String, String>) srcNode.properties.clone();
+            this.childEntries = (LinkedHashMap<String, ChildNodeEntry>) srcNode.childEntries.clone();
+        } else {
+            this.properties = new HashMap<String, String>(other.getProperties());
+            this.childEntries = new LinkedHashMap<String, ChildNodeEntry>(other.getChildNodeCount());
+            for (Iterator<ChildNodeEntry> it = other.getChildNodeEntries(); it.hasNext(); ) {
+                ChildNodeEntry cne = it.next();
+                this.childEntries.put(cne.getName(), cne);
+            }
+        }
     }
 
     public Map<String, String> getProperties() {
         return properties;
     }
 
-    public List<String> getChildNodeNames(int offset, int count) {
-        Set<String> keys = childEntries.keySet();
-        List<String> names = new ArrayList(keys);
+    public ChildNodeEntry getChildNodeEntry(String name) {
+        return childEntries.get(name);
+    }
+
+    public Iterator<String> getChildNodeNames(int offset, int count) {
         if (offset < 0 || count < -1) {
             throw new IllegalArgumentException();
         }
+
         if (offset == 0 && count == -1) {
-            return names;
+            return childEntries.keySet().iterator();
         } else {
-            if (offset >= names.size()) {
-                return Collections.emptyList();
+            if (offset >= childEntries.size()) {
+                return new EmptyIterator<String>();
             }
-            int to = offset + count;
-            if (to > names.size()) {
-                to = names.size();
+            if (count == -1 || (offset + count) > childEntries.size()) {
+                count = childEntries.size() - offset;
             }
-            return names.subList(offset, to);
+            return new RangeIterator<String>(childEntries.keySet().iterator(), offset, count);
         }
     }
 
@@ -79,67 +86,54 @@ public abstract class AbstractNode imple
         return childEntries.size();
     }
 
-    public Map<String, String> getChildNodeEntries() {
-        return childEntries;
+    public Iterator<ChildNodeEntry> getChildNodeEntries() {
+        return getChildNodeEntries(0, -1);
     }
 
-    public Map<String, String> getChildNodeEntries(int offset, int count) {
+    public Iterator<ChildNodeEntry> getChildNodeEntries(int offset, int count) {
         if (offset < 0 || count < -1) {
             throw new IllegalArgumentException();
         }
         if (offset == 0 && count == -1) {
-            return childEntries;
+            return childEntries.values().iterator();
         } else {
             if (offset >= childEntries.size()) {
-                return Collections.emptyMap();
+                return new EmptyIterator<ChildNodeEntry>();
             }
-            int to = offset + count;
-            if (to > childEntries.size()) {
-                to = childEntries.size();
-                count = to - offset;
-            }
-            Map<String, String> result = new LinkedHashMap<String, String>(count);
-            for (Map.Entry<String, String> entry : childEntries.entrySet()) {
-                if (offset-- > 0) {
-                    continue;
-                }
-                result.put(entry.getKey(), entry.getValue());
-                if (--count <= 0) {
-                    break;
-                }
+            if (count == -1 || (offset + count) > childEntries.size()) {
+                count = childEntries.size() - offset;
             }
-            return result;
+            return new RangeIterator<ChildNodeEntry>(childEntries.values().iterator(), offset, count);
         }
     }
 
-    public Map<String, Node> getChildNodes(int offset, int count, RevisionProvider provider)
+    public Iterator<ChildNode> getChildNodes(int offset, int count, final RevisionProvider provider)
             throws Exception {
         if (offset < 0 || count < -1) {
             throw new IllegalArgumentException();
         }
 
         if (offset >= childEntries.size()) {
-            return Collections.emptyMap();
+            return new EmptyIterator<ChildNode>();
         }
 
         // todo support embedded/in-lined sub-trees
 
-        int to = offset + count;
-        if (to > childEntries.size()) {
-            to = childEntries.size();
-            count = to - offset;
-        }
-        Map<String, Node> result = new LinkedHashMap<String, Node>(count);
-        for (Map.Entry<String, String> entry : childEntries.entrySet()) {
-            if (offset-- > 0) {
-                continue;
-            }
-            result.put(entry.getKey(), provider.getNode(entry.getValue()));
-            if (--count <= 0) {
-                break;
-            }
+        if (count == -1 || (offset + count) > childEntries.size()) {
+            count = childEntries.size() - offset;
         }
-        return result;
+
+        return new AbstractRangeIterator<ChildNode>(childEntries.values().iterator(), offset, count) {
+            @Override
+            protected ChildNode doNext() {
+                ChildNodeEntry cne = (ChildNodeEntry) it.next();
+                try {
+                    return new ChildNode(cne.getName(), provider.getNode(cne.getId()));
+                } catch (Exception e) {
+                    throw new NoSuchElementException();
+                }
+            }
+        };
     }
 
     public Node getNode(String relPath, RevisionProvider provider)
@@ -148,11 +142,72 @@ public abstract class AbstractNode imple
 
         Node node = this;
         for (String name : names) {
-            node = node.getChildNodes(0, -1, provider).get(name);
-            if (node == null) {
+            ChildNodeEntry cne = node.getChildNodeEntry(name);
+            if (cne == null) {
                 throw new NotFoundException();
             }
+            node = provider.getNode(cne.getId());
         }
         return node;
     }
+
+    //--------------------------------------------------------< inner classes >
+
+    static class EmptyIterator<T> implements Iterator<T> {
+
+        public boolean hasNext() {
+            return false;
+        }
+
+        public T next() {
+            throw new NoSuchElementException();
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    static abstract class AbstractRangeIterator<T> implements Iterator<T> {
+
+        final Iterator<?> it;
+        int count;
+
+        public AbstractRangeIterator(Iterator<?> it, int offset, int count) {
+            while (offset-- > 0 && it.hasNext()) {
+                it.next();
+            }
+            this.count = count;
+            this.it = it;
+        }
+
+        public boolean hasNext() {
+            return (count > 0 && it.hasNext());
+        }
+
+        public T next() {
+            if (count-- > 0) {
+                return doNext();
+            }
+            throw new NoSuchElementException();
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+        
+        protected abstract T doNext();
+    }
+
+    static class RangeIterator<T> extends AbstractRangeIterator<T> {
+
+        public RangeIterator(Iterator<T> it, int offset, int count) {
+            super(it, offset, count);
+        }
+
+        @Override
+        protected T doNext() {
+            return (T) it.next();
+        }
+    }
 }

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNode.java?rev=1224899&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNode.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNode.java Tue Dec 27 11:57:38 2011
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mk.model;
+
+/**
+ *
+ */
+public class ChildNode {
+
+    private final String name;
+    private final Node node;
+
+    public ChildNode(String name, Node node) {
+        this.name = name;
+        this.node = node;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Node getNode() {
+        return node;
+    }
+}

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNodeEntry.java?rev=1224899&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNodeEntry.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/ChildNodeEntry.java Tue Dec 27 11:57:38 2011
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mk.model;
+
+/**
+ * 
+ */
+public class ChildNodeEntry {
+    
+    private final String name;
+    private final String id;
+    
+    public ChildNodeEntry(String name, String id) {
+        this.name = name;
+        this.id = id;
+    }
+    
+    public String getName() {
+        return name;
+    }
+
+    public String getId() {
+        return id;
+    }
+}

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/CommitBuilder.java Tue Dec 27 11:57:38 2011
@@ -24,8 +24,10 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  *
@@ -56,14 +58,14 @@ public class CommitBuilder {
 
     public void addNode(String parentNodePath, String nodeName, Map<String, String> properties) throws Exception {
         MutableNode modParent = getOrCreateStagedNode(parentNodePath);
-        if (modParent.getChildNodeEntries().containsKey(nodeName)) {
+        if (modParent.getChildNodeEntry(nodeName) != null) {
             throw new Exception("there's already a child node with name '" + nodeName + "'");
         }
         MutableNode newChild = new MutableNode();
         newChild.getProperties().putAll(properties);
 
-        // value will be computed on commit
-        modParent.getChildNodeEntries().put(nodeName, "");
+        // id will be computed on commit
+        modParent.put(new ChildNodeEntry(nodeName, ""));
         String newPath = PathUtils.concat(parentNodePath, nodeName);
         staged.put(newPath, newChild);
         // update change log
@@ -75,12 +77,10 @@ public class CommitBuilder {
         String nodeName = PathUtils.getName(nodePath);
 
         MutableNode parent = getOrCreateStagedNode(parentPath);
-        if (!parent.getChildNodeEntries().containsKey(nodeName)) {
+        if (parent.remove(nodeName) == null) {
             throw new NotFoundException(nodePath);
         }
 
-        parent.getChildNodeEntries().remove(nodeName);
-
         // update staging area
         removeStagedNodes(nodePath);
 
@@ -96,17 +96,16 @@ public class CommitBuilder {
         String destNodeName = PathUtils.getName(destPath);
 
         MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
-        String targetId = srcParent.getChildNodeEntries().get(srcNodeName);
-        if (targetId == null) {
+        ChildNodeEntry srcCNE = srcParent.remove(srcNodeName);
+        if (srcCNE == null) {
             throw new NotFoundException(srcPath);
         }
-        srcParent.getChildNodeEntries().remove(srcNodeName);
 
         MutableNode destParent = getOrCreateStagedNode(destParentPath);
-        if (destParent.getChildNodeEntries().containsKey(destNodeName)) {
+        if (destParent.getChildNodeEntry(destNodeName) != null) {
             throw new Exception("node already exists at move destination path: " + destPath);
         }
-        destParent.getChildNodeEntries().put(destNodeName, targetId);
+        destParent.put(new ChildNodeEntry(destNodeName, srcCNE.getId()));
 
         // update staging area
         moveStagedNodes(srcPath, destPath);
@@ -123,13 +122,13 @@ public class CommitBuilder {
         String destNodeName = PathUtils.getName(destPath);
 
         MutableNode srcParent = getOrCreateStagedNode(srcParentPath);
-        String targetId = srcParent.getChildNodeEntries().get(srcNodeName);
-        if (targetId == null) {
+        ChildNodeEntry srcCNE = srcParent.getChildNodeEntry(srcNodeName);
+        if (srcCNE == null) {
             throw new NotFoundException(srcPath);
         }
 
         MutableNode destParent = getOrCreateStagedNode(destParentPath);
-        destParent.getChildNodeEntries().put(destNodeName, targetId);
+        destParent.put(new ChildNodeEntry(destNodeName, srcCNE.getId()));
 
         // update change log
         changeLog.add(new CopyNode(srcPath, destPath));
@@ -149,12 +148,7 @@ public class CommitBuilder {
         changeLog.add(new SetProperty(nodePath, propName, propValue));
     }
 
-    public void setProperty(String nodePath, String propName, String[] propValues) throws Exception {
-        // todo support MVPs
-    }
-
     public void setProperties(String nodePath, Map<String, String> properties) throws Exception {
-        // todo support MVPs
         MutableNode node = getOrCreateStagedNode(nodePath);
 
         node.getProperties().clear();
@@ -164,6 +158,9 @@ public class CommitBuilder {
         changeLog.add(new SetProperties(nodePath, properties));
     }
 
+    //final static AtomicLong commitCount = new AtomicLong();
+    //final static AtomicLong mergeCount = new AtomicLong();
+
     public String /* new revId */ doCommit() throws Exception {
         if (staged.isEmpty()) {
             // nothing to commit
@@ -197,6 +194,8 @@ public class CommitBuilder {
 
                 rootNodeId = mergeTree(baseRoot, ourRoot, theirRoot);
 
+                //mergeCount.getAndIncrement();
+
                 baseRevId = currentHead;
             }
 
@@ -213,6 +212,13 @@ public class CommitBuilder {
             newRevId = store.putCommit(newCommit);
 
             store.setHeadCommitId(newRevId);
+/*
+            long commits = commitCount.incrementAndGet();
+            if (commits % 100 == 0) {
+                long merges = mergeCount.get();
+                System.out.println("#commits: " + commits + ", #merges: " + merges + " (" + ((int)(100*(1.0*merges/commits))) + "%)");
+            }
+*/
         } finally {
             store.unlockHead();
         }
@@ -240,11 +246,11 @@ public class CommitBuilder {
                 if (node == null) {
                     // not yet staged, resolve id using staged parent
                     // to allow for staged move operations
-                    String id = parent.getChildNodeEntries().get(names[names.length - i - 1]);
-                    if (id == null) {
+                    ChildNodeEntry cne = parent.getChildNodeEntry(names[names.length - i - 1]);
+                    if (cne == null) {
                         throw new NotFoundException(nodePath);
                     }
-                    node = new MutableNode(store.getNode(id));
+                    node = new MutableNode(store.getNode(cne.getId()));
                     staged.put(path, node);
                 }
                 parent = node;
@@ -258,7 +264,8 @@ public class CommitBuilder {
         if (node != null) {
             staged.remove(srcPath);
             staged.put(destPath, node);
-            for (String childName : node.getChildNodeEntries().keySet()) {
+            for (Iterator<String> it = node.getChildNodeNames(0, -1); it.hasNext(); ) {
+                String childName = it.next();
                 moveStagedNodes(PathUtils.concat(srcPath, childName), PathUtils.concat(destPath, childName));
             }
         }
@@ -268,7 +275,8 @@ public class CommitBuilder {
         MutableNode node = staged.get(nodePath);
         if (node != null) {
             staged.remove(nodePath);
-            for (String childName : node.getChildNodeEntries().keySet()) {
+            for (Iterator<String> it = node.getChildNodeNames(0, -1); it.hasNext(); ) {
+                String childName = it.next();
                 removeStagedNodes(PathUtils.concat(nodePath, childName));
             }
         }
@@ -296,7 +304,7 @@ public class CommitBuilder {
             if (PathUtils.denotesRoot(path)) {
                 rootNodeId = id;
             } else {
-                staged.get(PathUtils.getParentPath(path)).getChildNodeEntries().put(PathUtils.getName(path), id);
+                staged.get(PathUtils.getParentPath(path)).put(new ChildNodeEntry(PathUtils.getName(path), id));
             }
         }
         if (rootNodeId == null) {
@@ -327,7 +335,6 @@ public class CommitBuilder {
     }
 
     void mergeNode(StoredNode baseNode, StoredNode ourNode, StoredNode theirNode, String path) throws Exception {
-        // todo review/verify correctness of merge algorithm
         NodeDelta theirChanges = new NodeDelta(baseNode, theirNode);
         NodeDelta ourChanges = new NodeDelta(baseNode, ourNode);
 
@@ -341,10 +348,15 @@ public class CommitBuilder {
             mergedNode.getProperties().remove(name);
         }
 
-        mergedNode.getChildNodeEntries().putAll(ourChanges.getAddedChildNodes());
-        mergedNode.getChildNodeEntries().putAll(ourChanges.getChangedChildNodes());
+        // todo fixme
+        for (Map.Entry<String, String> entry : ourChanges.getAddedChildNodes ().entrySet()) {
+            mergedNode.put(new ChildNodeEntry(entry.getKey(), entry.getValue()));
+        }
+        for (Map.Entry<String, String> entry : ourChanges.getChangedChildNodes ().entrySet()) {
+            mergedNode.put(new ChildNodeEntry(entry.getKey(), entry.getValue()));
+        }
         for (String name : ourChanges.getRemovedChildNodes().keySet()) {
-            mergedNode.getChildNodeEntries().remove(name);
+            mergedNode.remove(name);
         }
 
         List<NodeDelta.Conflict> conflicts = theirChanges.listConflicts(ourChanges);
@@ -364,9 +376,9 @@ public class CommitBuilder {
                 case NODE_CONTENT_CONFLICT: {
                     if (ourChanges.getChangedChildNodes().containsKey(conflictName)) {
                         // modified subtrees
-                        StoredNode baseChild = store.getNode(baseNode.getChildNodeEntries().get(conflictName));
-                        StoredNode ourChild = store.getNode(ourNode.getChildNodeEntries().get(conflictName));
-                        StoredNode theirChild = store.getNode(theirNode.getChildNodeEntries().get(conflictName));
+                        StoredNode baseChild = store.getNode(baseNode.getChildNodeEntry(conflictName).getId());
+                        StoredNode ourChild = store.getNode(ourNode.getChildNodeEntry(conflictName).getId());
+                        StoredNode theirChild = store.getNode(theirNode.getChildNodeEntry(conflictName).getId());
                         // merge the dirty subtrees recursively
                         mergeNode(baseChild, ourChild, theirChild, PathUtils.concat(path, conflictName));
                     } else {
@@ -381,7 +393,7 @@ public class CommitBuilder {
                     break;
 
                 case REMOVED_DIRTY_PROPERTY_CONFLICT:
-                    mergedNode.getChildNodeEntries().remove(conflictName);
+                    mergedNode.remove(conflictName);
                     break;
             }
 

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/MutableNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/MutableNode.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/MutableNode.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/MutableNode.java Tue Dec 27 11:57:38 2011
@@ -27,4 +27,12 @@ public class MutableNode extends Abstrac
     public MutableNode(Node other) {
         super(other);
     }
+
+    public ChildNodeEntry put(ChildNodeEntry newEntry) {
+        return childEntries.put(newEntry.getName(), newEntry);
+    }
+    
+    public ChildNodeEntry remove(String name) {
+        return childEntries.remove(name);
+    }
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/Node.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/Node.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/Node.java Tue Dec 27 11:57:38 2011
@@ -19,7 +19,7 @@ package org.apache.jackrabbit.mk.model;
 import org.apache.jackrabbit.mk.store.RevisionProvider;
 import org.apache.jackrabbit.mk.store.NotFoundException;
 
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -28,17 +28,21 @@ import java.util.Map;
 public interface Node {
 
     Map<String, String> getProperties();
-    List<String> getChildNodeNames(int offset, int count);
+    
+    ChildNodeEntry getChildNodeEntry(String name);
+
+    Iterator<String> getChildNodeNames(int offset, int count);
     int getChildNodeCount();
+    
     /**
      * @deprecated use getChildNodes(int, int, RevisionProvider) instead
      */
-    Map<String, String> getChildNodeEntries();
+    Iterator<ChildNodeEntry> getChildNodeEntries();
     /**
      * @deprecated use getChildNodes(int, int, RevisionProvider) instead
      */
-    Map<String, String> getChildNodeEntries(int offset, int count);
+    Iterator<ChildNodeEntry> getChildNodeEntries(int offset, int count);
 
-    Map<String, Node> getChildNodes(int offset, int count, RevisionProvider provider) throws Exception;
+    Iterator<ChildNode> getChildNodes(int offset, int count, RevisionProvider provider) throws Exception;
     Node getNode(String relPath, RevisionProvider provider) throws NotFoundException, Exception;
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/NodeUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/NodeUtils.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/NodeUtils.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/NodeUtils.java Tue Dec 27 11:57:38 2011
@@ -20,6 +20,8 @@ import org.apache.jackrabbit.mk.store.Re
 import org.apache.jackrabbit.mk.util.PathUtils;
 
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -77,8 +79,18 @@ public class NodeUtils {
         // todo determine copy/move and child-node reorder operations
 
         // compare child node entries
-        Map<String, String> oldEntries = node1.getChildNodeEntries(0, -1);
-        Map<String, String> newEntries = node2.getChildNodeEntries(0, -1);
+        
+        // todo delegate comparing child node entries to (Abstract)Node
+        Map<String, String> oldEntries = new LinkedHashMap<String, String>(node1.getChildNodeCount());
+        for (Iterator<ChildNodeEntry> it = node1.getChildNodeEntries(0, -1); it.hasNext(); ) {
+            ChildNodeEntry cne = it.next();
+            oldEntries.put(cne.getName(), cne.getId());
+        }
+        Map<String, String> newEntries = new LinkedHashMap<String, String>(node2.getChildNodeCount());
+        for (Iterator<ChildNodeEntry> it = node2.getChildNodeEntries(0, -1); it.hasNext(); ) {
+            ChildNodeEntry cne = it.next();
+            newEntries.put(cne.getName(), cne.getId());
+        }
         if (!oldEntries.equals(newEntries)) {
             Set<String> set = new HashSet<String>();
             set.addAll(oldEntries.keySet());

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/StoredNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/StoredNode.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/StoredNode.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/model/StoredNode.java Tue Dec 27 11:57:38 2011
@@ -20,10 +20,9 @@ import org.apache.jackrabbit.mk.store.Re
 import org.apache.jackrabbit.mk.store.NotFoundException;
 
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 
 /**
  *
@@ -32,9 +31,13 @@ public class StoredNode extends Abstract
 
     private final String id;
 
-    public StoredNode(String id, Map<String, String> properties, Map<String, String> childEntries) {
+    public StoredNode(String id, Map<String, String> properties, Iterator<ChildNodeEntry> cneIt) {
         this.id = id;
         this.properties.putAll(properties);
+        while (cneIt.hasNext()) {
+            ChildNodeEntry cne = cneIt.next();
+            childEntries.put(cne.getName(), cne);
+        }
         this.childEntries.putAll(childEntries);
     }
 
@@ -51,26 +54,21 @@ public class StoredNode extends Abstract
         return Collections.unmodifiableMap(properties);
     }
 
-    public Map<String, String> getChildNodeEntries() {
-        return Collections.unmodifiableMap(super.getChildNodeEntries());
-    }
-
-    public Map<String, String> getChildNodeEntries(int offset, int count) {
-        return Collections.unmodifiableMap(super.getChildNodeEntries(offset, count));
+    public Iterator<ChildNodeEntry> getChildNodeEntries() {
+        return getChildNodeEntries(0, -1);
     }
 
-    public List<String> getChildNodeNames(int offset, int count) {
-        return Collections.unmodifiableList(super.getChildNodeNames(offset, count));
+    public Iterator<ChildNodeEntry> getChildNodeEntries(int offset, int count) {
+        return new UnmodifiableIterator<ChildNodeEntry>(super.getChildNodeEntries(offset, count));
     }
 
-    public int getChildNodeCount() {
-        return childEntries.size();
+    public Iterator<String> getChildNodeNames(int offset, int count) {
+        return new UnmodifiableIterator<String>(super.getChildNodeNames(offset, count));
     }
 
-    public Map<String, Node> getChildNodes(int offset, int count, RevisionProvider provider)
+    public Iterator<ChildNode> getChildNodes(int offset, int count, RevisionProvider provider)
             throws Exception {
-        // todo return StoredNode instances instead?
-        return Collections.unmodifiableMap(super.getChildNodes(offset, count, provider));
+        return new UnmodifiableIterator<ChildNode>(super.getChildNodes(offset, count, provider));
     }
 
     public Node getNode(String relPath, RevisionProvider provider) throws NotFoundException, Exception {
@@ -80,4 +78,27 @@ public class StoredNode extends Abstract
         }
         return result;
     }
+
+    //--------------------------------------------------------< inner classes >
+
+    static class UnmodifiableIterator<T> implements Iterator<T> {
+        
+        final Iterator<T> it;
+
+        public UnmodifiableIterator(Iterator<T> it) {
+            this.it = it;
+        }
+
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        public T next() {
+            return it.next();
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MongoPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MongoPersistenceManager.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MongoPersistenceManager.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MongoPersistenceManager.java Tue Dec 27 11:57:38 2011
@@ -19,9 +19,11 @@ package org.apache.jackrabbit.mk.store;
 import java.io.File;
 import java.io.InputStream;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.jackrabbit.mk.model.ChildNodeEntry;
 import org.apache.jackrabbit.mk.model.Commit;
 import org.apache.jackrabbit.mk.model.Node;
 import org.apache.jackrabbit.mk.model.StoredCommit;
@@ -119,15 +121,31 @@ public class MongoPersistenceManager ext
         } else {
             key.put(ID_FIELD, id);
         }
-        DBObject nodeObject = nodes.findOne(key);
+        final DBObject nodeObject = nodes.findOne(key);
         if (nodeObject != null) {
+            // todo support partitioned child node lists
             if (BINARY_FORMAT) {
                 byte[] bytes = (byte[]) nodeObject.get(DATA_FIELD);
                 return Serializer.fromBytes(id, bytes, Node.class);
             } else {
+                final Iterator<String> it = nodeObject.keySet().iterator();
+                final BasicDBObject children = (BasicDBObject) nodeObject.get(CHILDREN_OBJECT);
+                Iterator<ChildNodeEntry> cneIt = new Iterator<ChildNodeEntry>() {
+                    public boolean hasNext() {
+                        return it.hasNext();
+                    }
+
+                    public ChildNodeEntry next() {
+                        String name = it.next();
+                        return new ChildNodeEntry(decodeName(name), (String) children.get(name));
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
                 return new StoredNode(id,
-                        decodeKeys(((BasicDBObject) nodeObject.get(PROPERTIES_OBJECT)).toMap()),
-                        (LinkedHashMap<String, String>) decodeKeys(((BasicDBObject) nodeObject.get(CHILDREN_OBJECT)).toMap()));
+                        decodeKeys(((BasicDBObject) nodeObject.get(PROPERTIES_OBJECT)).toMap()), cneIt);
             }
         } else {
             throw new NotFoundException(id);
@@ -140,13 +158,20 @@ public class MongoPersistenceManager ext
         byte[] key = idFactory.createNodeId(node, bytes);
         String id = StringUtils.convertBytesToHex(key);
 
+        // todo support partitioned child node lists
         BasicDBObject nodeObject;
         if (BINARY_FORMAT) {
             nodeObject = new BasicDBObject(ID_FIELD, key).append(DATA_FIELD, bytes);
         } else {
+            Iterator<ChildNodeEntry> it = node.getChildNodeEntries(0, -1);
+            BasicDBObject children = new BasicDBObject();
+            while (it.hasNext()) {
+                ChildNodeEntry cne = it.next();
+                children.put(cne.getName(), cne.getId());
+            }
             nodeObject = new BasicDBObject(ID_FIELD, id)
                     .append(PROPERTIES_OBJECT, new BasicDBObject(encodeKeys(node.getProperties())))
-                    .append(CHILDREN_OBJECT, new BasicDBObject(encodeKeys(node.getChildNodeEntries())));
+                    .append(CHILDREN_OBJECT, children);
         }
         try {
             nodes.insert(nodeObject);

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/util/Serializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/util/Serializer.java?rev=1224899&r1=1224898&r2=1224899&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/util/Serializer.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/util/Serializer.java Tue Dec 27 11:57:38 2011
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.mk.store.util;
 
+import org.apache.jackrabbit.mk.model.ChildNodeEntry;
 import org.apache.jackrabbit.mk.model.Commit;
 import org.apache.jackrabbit.mk.model.StoredCommit;
 import org.apache.jackrabbit.mk.model.StoredNode;
@@ -26,7 +27,7 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -49,11 +50,13 @@ public class Serializer {
                 IOUtils.writeString(out, entry.getKey());
                 IOUtils.writeString(out, entry.getValue());
             }
-            Map<String, String> childEntries = node.getChildNodeEntries(0, -1);
-            IOUtils.writeVarInt(out, childEntries.size());
-            for (Map.Entry<String, String> entry : childEntries.entrySet()) {
-                IOUtils.writeString(out, entry.getKey());
-                IOUtils.writeString(out, entry.getValue());
+            // todo support partitioned child node lists
+            Iterator<ChildNodeEntry> it = node.getChildNodeEntries(0, -1);
+            IOUtils.writeVarInt(out, node.getChildNodeCount());
+            while (it.hasNext())  {
+                ChildNodeEntry cne = it.next();
+                IOUtils.writeString(out, cne.getName());
+                IOUtils.writeString(out, cne.getId());
             }
             out.close();
         } catch (IOException e) {
@@ -64,7 +67,7 @@ public class Serializer {
     }
 
     public static StoredNode fromBytes(String id, byte[] bytes, Class<Node> type) throws Exception {
-        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+        final ByteArrayInputStream in = new ByteArrayInputStream(bytes);
         if (in.read() != Constants.NODE) {
             throw new Exception("unknown data format");
         }
@@ -75,14 +78,33 @@ public class Serializer {
             String value = IOUtils.readString(in);
             props.put(name, value);
         }
-        count = IOUtils.readVarInt(in);
-        LinkedHashMap<String, String> childEntries = new LinkedHashMap<String, String>(count);
-        while (count-- > 0) {
-            String name = IOUtils.readString(in);
-            String childId = IOUtils.readString(in);
-            childEntries.put(name, childId);
-        }
-        return new StoredNode(id, props, childEntries);
+        // todo support partitioned child node lists
+        final int numChildren = IOUtils.readVarInt(in);
+        Iterator<ChildNodeEntry> cneIt = new Iterator<ChildNodeEntry>() {
+            
+            int cnt = numChildren;
+
+            public boolean hasNext() {
+                return cnt > 0;
+            }
+
+            public ChildNodeEntry next() {
+                cnt--;
+                String name = null, childId = null;
+                try {
+                    name = IOUtils.readString(in);
+                    childId = IOUtils.readString(in);
+                } catch (IOException ignore) {
+                    // won't get here...
+                }
+                return new ChildNodeEntry(name, childId);
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+        return new StoredNode(id, props, cneIt);
     }
 
     //---------------------------------------------------------------< Commit >