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()));
+ }
+
+}