You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2011/12/13 16:54:24 UTC

svn commit: r1213750 - in /jackrabbit/sandbox/microkernel/src: main/java/org/apache/jackrabbit/mk/mem/ test/java/org/apache/jackrabbit/mk/hash/

Author: thomasm
Date: Tue Dec 13 15:54:23 2011
New Revision: 1213750

URL: http://svn.apache.org/viewvc?rev=1213750&view=rev
Log:
Support property ":hash"

Added:
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/hash/HashTest.java
Modified:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeId.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMapInDb.java

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeId.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeId.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeId.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeId.java Tue Dec 13 15:54:23 2011
@@ -24,6 +24,7 @@ public class NodeId {
     public static final NodeId[] EMPTY_ARRAY = new NodeId[0];
 
     private final long x;
+    private byte[] hash;
 
     protected NodeId(long x) {
         this.x = x;
@@ -49,4 +50,12 @@ public class NodeId {
         return false;
     }
 
+    public byte[] getHash() {
+        return hash;
+    }
+
+    public void setHash(byte[] hash) {
+        this.hash = hash;
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java Tue Dec 13 15:54:23 2011
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.mk.mem;
 
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
@@ -26,8 +29,10 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.mk.json.JsopWriter;
 import org.apache.jackrabbit.mk.util.Cache;
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
+import org.apache.jackrabbit.mk.util.IOUtils;
 import org.apache.jackrabbit.mk.util.PathUtils;
 import org.apache.jackrabbit.mk.util.StringCache;
+import org.apache.jackrabbit.mk.util.StringUtils;
 
 /**
  * An in-memory node, including all child nodes.
@@ -50,6 +55,11 @@ public class NodeImpl implements Cache.V
     public static final String DESCENDANT_INLINE_COUNT = ":descendantInlineCount";
 
     /**
+     * The content hash.
+     */
+    public static final String HASH = ":hash";
+
+    /**
      * Used by NodeListLarge when there are many child nodes.
      * The id of an internal node.
      */
@@ -82,6 +92,8 @@ public class NodeImpl implements Cache.V
     private long descendantCount;
     private int descendantInlineCount;
 
+    private byte[] hash;
+
     public NodeImpl(NodeMap map, long revId) {
         this.map = map;
         this.revId = revId;
@@ -294,6 +306,9 @@ public class NodeImpl implements Cache.V
                 json.key(pv[i]).encodedValue(pv[i + 1]);
             }
         }
+        if (map.hash) {
+            json.key(HASH).value(StringUtils.convertBytesToHex(getHash()));
+        }
         if (childNodes == null) {
             if (childNodeCount) {
                 json.key(CHILREN_COUNT).value(0);
@@ -432,6 +447,13 @@ public class NodeImpl implements Cache.V
         return pairs;
     }
 
+    public static NodeImpl parse(String json) {
+        JsopTokenizer t = new JsopTokenizer(json);
+        t.read('{');
+        NodeMap map = new NodeMap();
+        return NodeImpl.parse(map, t, 0);
+    }
+
     public static NodeImpl parse(NodeMap map, JsopReader t, long revId) {
         return parse(map, t, revId, null);
     }
@@ -453,6 +475,9 @@ public class NodeImpl implements Cache.V
                     if (key.length() > 0 && key.charAt(0) == ':') {
                         if (key.equals(CHILREN_COUNT)) {
                             // ignore
+                        } else if (key.equals(HASH)) {
+                            value = JsopTokenizer.decodeQuoted(value);
+                            node.hash = StringUtils.convertHexToBytes(value);
                         } else if (key.equals(DESCENDANT_COUNT)) {
                             node.descendantCount = Long.parseLong(value);
                         } else {
@@ -510,6 +535,40 @@ public class NodeImpl implements Cache.V
         return s;
     }
 
+    public byte[] getHash() {
+        if (hash == null) {
+            try {
+                MessageDigest d = MessageDigest.getInstance("SHA-1");
+                DigestOutputStream out = new DigestOutputStream(new OutputStream() {
+                    public void write(byte[] buff, int off, int length) {
+                        // ignore
+                    }
+                    public void write(byte[] buff) {
+                        // ignore
+                    }
+                    public void write(int b) {
+                        // ignore
+                    }
+                }, d);
+                String[] pv = propertyValuePairs;
+                if (pv != null) {
+                    IOUtils.writeInt(out, pv.length / 2);
+                    for (String p: pv) {
+                        IOUtils.writeString(out, p);
+                    }
+                }
+                if (childNodes != null && childNodes.size() > 0) {
+                    childNodes.updateHash(map, out);
+                }
+                hash = d.digest();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            id.setHash(hash);
+        }
+        return hash;
+    }
+
     public String asString() {
         JsopWriter json = new JsopBuilder();
         json.setLineLength(120);
@@ -526,6 +585,10 @@ public class NodeImpl implements Cache.V
                 json.key(pv[i]).encodedValue(pv[i + 1]);
             }
         }
+        if (map.getHash() && id != null) {
+            byte[] hash = getHash();
+            json.key(HASH).value(StringUtils.convertBytesToHex(hash));
+        }
         if (childNodes != null && childNodes.size() > 0) {
             if (descendantCount > childNodes.size()) {
                 json.key(DESCENDANT_COUNT).value(descendantCount);
@@ -555,6 +618,8 @@ public class NodeImpl implements Cache.V
                     } else if (key.equals(DESCENDANT_COUNT)) {
                         node.descendantCount = Long.parseLong(value);
                         descendantCountSet = true;
+                    } else if (key.equals(HASH)) {
+                        node.id.setHash(StringUtils.convertHexToBytes(JsopTokenizer.decodeQuoted(value)));
                     } else {
                         node.setProperty(key, value);
                     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java Tue Dec 13 15:54:23 2011
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.mk.mem;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.Iterator;
 import org.apache.jackrabbit.mk.json.JsopWriter;
 import org.apache.jackrabbit.mk.mem.NodeImpl.ChildVisitor;
@@ -49,4 +51,6 @@ interface NodeList {
 
     int getMemory();
 
+    void updateHash(NodeMap map, OutputStream out) throws IOException;
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java Tue Dec 13 15:54:23 2011
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.mk.mem;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
@@ -288,4 +290,16 @@ public class NodeListLarge implements No
         return children.size() * 100;
     }
 
+    public void updateHash(NodeMap map, OutputStream out) throws IOException {
+        for (Child c : children) {
+            byte[] hash = c.id.getHash();
+            IOUtils.writeVarInt(out, c.nameFilter.length);
+            out.write(c.nameFilter);
+            if (hash == null) {
+                hash = map.getNode(c.id.getLong()).getHash();
+            }
+            out.write(hash);
+        }
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java Tue Dec 13 15:54:23 2011
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.mk.mem;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 import java.util.Iterator;
 import org.apache.jackrabbit.mk.json.JsopBuilder;
@@ -221,4 +224,23 @@ public class NodeListSmall implements No
         return false;
     }
 
+    public void updateHash(NodeMap map, OutputStream out) throws IOException {
+        if (sort != null) {
+            try {
+                for (int s : sort) {
+                    String n = names[s];
+                    IOUtils.writeString(out, n);
+                    NodeId c = children[s];
+                    byte[] hash = c.getHash();
+                    if (hash == null) {
+                        hash = map.getNode(c.getLong()).getHash();
+                    }
+                    out.write(hash);
+                }
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java Tue Dec 13 15:54:23 2011
@@ -20,6 +20,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
+import org.apache.jackrabbit.mk.mem.NodeImpl.ChildVisitor;
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
 
 public class NodeMap {
@@ -27,6 +28,7 @@ public class NodeMap {
     public static final String MAX_MEMORY_CHILDREN = "maxMemoryChildren";
     public static final String DESCENDANT_COUNT = "descendantCount";
     public static final String DESCENDANT_INLINE_COUNT = "descendantInlineCount";
+    public static final String HASH = "hash";
 
     private static final int DEFAULT_MAX_MEMORY_CHILDREN = Integer.MAX_VALUE;
 
@@ -34,6 +36,9 @@ public class NodeMap {
 
     protected int descendantInlineCount = -1;
 
+    protected boolean hash;
+
+    final HashMap<Long, NodeImpl> temp = new HashMap<Long, NodeImpl>();
     private Map<Long, NodeImpl> nodes = Collections.synchronizedMap(new HashMap<Long, NodeImpl>());
     private AtomicLong nextId = new AtomicLong();
     private NodeId rootId;
@@ -51,6 +56,9 @@ public class NodeMap {
             } else {
                 x = NodeId.get(nextId.incrementAndGet());
                 nodes.put(x.getLong(), node);
+                if (hash) {
+                    temp.put(x.getLong(), node);
+                }
             }
             node.setId(x);
         }
@@ -68,6 +76,8 @@ public class NodeMap {
             descendantCount = Boolean.parseBoolean(value);
         } else if (key.equals(DESCENDANT_INLINE_COUNT)) {
             descendantInlineCount = Integer.parseInt(value);
+        } else if (key.equals(HASH)) {
+            hash = Boolean.parseBoolean(value);
         } else {
             throw ExceptionFactory.get("Unknown setting: " + key);
         }
@@ -90,7 +100,31 @@ public class NodeMap {
     }
 
     public NodeId commit(NodeImpl root) {
-        return rootId = addNode(root, false);
+        rootId = addNode(root, false);
+        if (hash) {
+            final NodeMap map = this;
+            root.visit(new ChildVisitor() {
+                public void accept(NodeId childId) {
+                    if (childId.isInline()) {
+                        NodeImpl t = childId.getNode(map);
+                        if (hash) {
+                            t.getHash();
+                        }
+                        t.visit(this);
+                    } else {
+                        NodeImpl t = temp.get(childId.getLong());
+                        if (t != null) {
+                            t.visit(this);
+                            if (hash) {
+                                t.getHash();
+                            }
+                        }
+                    }
+                }
+            });
+            temp.clear();
+        }
+        return rootId;
     }
 
     public NodeId getRootId() {
@@ -145,4 +179,12 @@ public class NodeMap {
         this.descendantInlineCount = descendantInlineCount;
     }
 
+    public void setHash(boolean hash) {
+        this.hash = hash;
+    }
+
+    public boolean getHash() {
+        return hash;
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMapInDb.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMapInDb.java?rev=1213750&r1=1213749&r2=1213750&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMapInDb.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMapInDb.java Tue Dec 13 15:54:23 2011
@@ -116,10 +116,18 @@ public class NodeMapInDb extends NodeMap
             newRoot.visit(new ChildVisitor() {
                 public void accept(NodeId childId) {
                     if (childId.getLong() < 0) {
-                        temp.get(childId.getLong()).visit(this);
+                        NodeImpl t = temp.get(childId.getLong());
+                        t.visit(this);
                         list.add(childId.getLong());
+                        if (hash) {
+                            t.getHash();
+                        }
                     } else if (childId.isInline()) {
-                        childId.getNode(map).visit(this);
+                        NodeImpl t = childId.getNode(map);
+                        t.visit(this);
+                        if (hash) {
+                            t.getHash();
+                        }
                     }
                 }
             });

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/hash/HashTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/hash/HashTest.java?rev=1213750&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/hash/HashTest.java (added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/hash/HashTest.java Tue Dec 13 15:54:23 2011
@@ -0,0 +1,70 @@
+/*
+ * 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.hash;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.apache.jackrabbit.mk.MultiMkTestBase;
+import org.apache.jackrabbit.mk.mem.NodeImpl;
+import org.apache.jackrabbit.mk.util.IOUtilsTest;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Tests the indexing mechanism.
+ */
+@RunWith(Parameterized.class)
+public class HashTest extends MultiMkTestBase {
+
+    private String head;
+
+    public HashTest(String url) {
+        super(url);
+    }
+
+    @After
+    public void tearDown() throws InterruptedException {
+        if (isMemoryKernel(mk)) {
+            head = mk.commit("/:root/head/config", "^ \"hash\": false", head, "");
+            head = mk.commit("/:root/head/config", "^ \"hash\": null", head, "");
+        }
+        super.tearDown();
+    }
+
+    @Test
+    public void getHash() {
+        head = mk.getHeadRevision();
+        if (isMemoryKernel(mk)) {
+            head = mk.commit("/:root/head/config", "^ \"hash\": true", head, "");
+        } else {
+            int todo;
+            return;
+        }
+
+        head = mk.commit("/", "+ \"test1\": { \"id\": 1 }", mk.getHeadRevision(), "");
+        head = mk.commit("/", "+ \"test2\": { \"id\": 1 }", mk.getHeadRevision(), "");
+        NodeImpl r = NodeImpl.parse(mk.getNodes("/", head));
+        assertTrue(r.getHash() != null);
+        NodeImpl t1 = NodeImpl.parse(mk.getNodes("/", head));
+        NodeImpl t2 = NodeImpl.parse(mk.getNodes("/", head));
+        IOUtilsTest.assertEquals(t1.getHash(), t2.getHash());
+        assertFalse(IOUtilsTest.isEqual(t1.getHash(), r.getHash()));
+    }
+
+}