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 2006/01/30 18:01:03 UTC
svn commit: r373549 - in
/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state:
NodeState.java util/Serializer.java
Author: stefan
Date: Mon Jan 30 09:00:59 2006
New Revision: 373549
URL: http://svn.apache.org/viewcvs?rev=373549&view=rev
Log:
JCR-307: improved performance when handling nodes with large number of child node entries
- reimplemented NodeState.ChildNodeEntries
- made ChildNodeEntry immutable again
- avoided unnecessary object creation when de-/serializing child node entries
Modified:
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java?rev=373549&r1=373548&r2=373549&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java Mon Jan 30 09:00:59 2006
@@ -53,7 +53,7 @@
protected QName nodeTypeName;
/** the names of this node's mixin types */
- protected Set mixinTypeNames = new HashSet();
+ protected HashSet mixinTypeNames = new HashSet();
/** id of this node's definition */
protected NodeDefId defId;
@@ -62,7 +62,7 @@
protected ChildNodeEntries childNodeEntries = new ChildNodeEntries();
/** set of property names (QName objects) */
- protected Set propertyNames = new HashSet();
+ protected HashSet propertyNames = new HashSet();
/**
* Listeners (weak references)
@@ -111,12 +111,11 @@
NodeState nodeState = (NodeState) state;
nodeTypeName = nodeState.getNodeTypeName();
- mixinTypeNames = new HashSet(nodeState.getMixinTypeNames());
+ mixinTypeNames = (HashSet) nodeState.mixinTypeNames.clone();
defId = nodeState.getDefinitionId();
uuid = nodeState.getUUID();
- propertyNames = new HashSet(nodeState.getPropertyNames());
- childNodeEntries = new ChildNodeEntries();
- childNodeEntries.addAll(nodeState.getChildNodeEntries());
+ propertyNames = (HashSet) nodeState.propertyNames.clone();
+ childNodeEntries = (ChildNodeEntries) nodeState.childNodeEntries.clone();
}
}
@@ -711,31 +710,86 @@
* <code>ChildNodeEntries</code> also provides an unmodifiable
* <code>List</code> view.
*/
- private static class ChildNodeEntries implements List, Serializable {
+ private static class ChildNodeEntries implements List, Cloneable, Serializable {
// insertion-ordered map of entries (key=uuid, value=entry)
LinkedMap entries;
- // map used for lookup by name (key=name, value=1st same-name sibling entry)
- Map nameMap;
+ // map used for lookup by name
+ // (key=name, value=either a single entry or a list of sns entries)
+ HashMap nameMap;
ChildNodeEntries() {
entries = new LinkedMap();
nameMap = new HashMap();
}
+ ChildNodeEntry get(String uuid) {
+ return (ChildNodeEntry) entries.get(uuid);
+ }
+
+ List get(QName nodeName) {
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ return Collections.unmodifiableList((ArrayList) obj);
+ } else {
+ // map entry is a single child node entry
+ return Collections.singletonList(obj);
+ }
+ }
+
+ ChildNodeEntry get(QName nodeName, int index) {
+ if (index < 1) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ ArrayList siblings = (ArrayList) obj;
+ if (index <= siblings.size()) {
+ return (ChildNodeEntry) siblings.get(index - 1);
+ }
+ } else {
+ // map entry is a single child node entry
+ if (index == 1) {
+ return (ChildNodeEntry) obj;
+ }
+ }
+ return null;
+ }
+
ChildNodeEntry add(QName nodeName, String uuid) {
- ChildNodeEntry sibling = (ChildNodeEntry) nameMap.get(nodeName);
- while (sibling != null && sibling.getNextSibling() != null) {
- sibling = sibling.getNextSibling();
+ List siblings = null;
+ int index = 0;
+ Object obj = nameMap.get(nodeName);
+ if (obj != null) {
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ siblings = (ArrayList) obj;
+ } else {
+ // map entry is a single child node entry,
+ // convert to siblings list
+ siblings = new ArrayList();
+ siblings.add(obj);
+ nameMap.put(nodeName, siblings);
+ }
+ index = siblings.size();
}
- int index = (sibling == null) ? 1 : sibling.getIndex() + 1;
+ index++;
ChildNodeEntry entry = new ChildNodeEntry(nodeName, uuid, index);
- if (sibling == null) {
- nameMap.put(nodeName, entry);
+ if (siblings != null) {
+ siblings.add(entry);
} else {
- sibling.setNextSibling(entry);
+ nameMap.put(nodeName, entry);
}
entries.put(uuid, entry);
@@ -751,96 +805,78 @@
}
}
- public void removeAll() {
- entries.clear();
- nameMap.clear();
- }
-
- ChildNodeEntry remove(String uuid) {
- ChildNodeEntry entry = (ChildNodeEntry) entries.get(uuid);
- if (entry != null) {
- return remove(entry.getName(), entry.getIndex());
- }
- return entry;
- }
-
- public ChildNodeEntry remove(ChildNodeEntry entry) {
- return remove(entry.getName(), entry.getIndex());
- }
-
public ChildNodeEntry remove(QName nodeName, int index) {
if (index < 1) {
throw new IllegalArgumentException("index is 1-based");
}
- ChildNodeEntry sibling = (ChildNodeEntry) nameMap.get(nodeName);
- ChildNodeEntry prevSibling = null;
- while (sibling != null) {
- if (sibling.getIndex() == index) {
- break;
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+
+ if (obj instanceof ChildNodeEntry) {
+ // map entry is a single child node entry
+ if (index != 1) {
+ return null;
}
- prevSibling = sibling;
- sibling = sibling.getNextSibling();
+ ChildNodeEntry removedEntry = (ChildNodeEntry) obj;
+ nameMap.remove(nodeName);
+ entries.remove(removedEntry.getUUID());
+ return removedEntry;
}
- if (sibling == null) {
+
+ // map entry is a list of siblings
+ List siblings = (ArrayList) obj;
+ if (index > siblings.size()) {
return null;
}
- // remove from entries list
- entries.remove(sibling.getUUID());
+ // remove from siblings list
+ ChildNodeEntry removedEntry = (ChildNodeEntry) siblings.remove(index - 1);
+ // remove from ordered entries map
+ entries.remove(removedEntry.getUUID());
- // update linked list of siblings & name map entry
- if (prevSibling != null) {
- prevSibling.setNextSibling(sibling.getNextSibling());
- } else {
- // the head is removed from the linked siblings list,
- // update name map
- if (sibling.getNextSibling() == null) {
- nameMap.remove(nodeName);
- } else {
- nameMap.put(nodeName, sibling.getNextSibling());
- }
- }
// update indices of subsequent same-name siblings
- ChildNodeEntry nextSibling = sibling.getNextSibling();
- while (nextSibling != null) {
- nextSibling.decIndex();
- nextSibling = nextSibling.getNextSibling();
+ for (int i = index - 1; i < siblings.size(); i++) {
+ ChildNodeEntry oldEntry = (ChildNodeEntry) siblings.get(i);
+ ChildNodeEntry newEntry = new ChildNodeEntry(nodeName, oldEntry.getUUID(), oldEntry.getIndex() - 1);
+ // overwrite old entry with updated entry in siblings list
+ siblings.set(i, newEntry);
+ // overwrite old entry with updated entry in ordered entries map
+ entries.put(newEntry.getUUID(), newEntry);
+ }
+
+ // clean up name lookup map if necessary
+ if (siblings.size() == 0) {
+ // no more entries with that name left:
+ // remove from name lookup map as well
+ nameMap.remove(nodeName);
+ } else if (siblings.size() == 1) {
+ // just one entry with that name left:
+ // discard siblings list and update name lookup map accordingly
+ nameMap.put(nodeName, siblings.get(0));
}
- return sibling;
+ // we're done
+ return removedEntry;
}
- List get(QName nodeName) {
- ChildNodeEntry sibling = (ChildNodeEntry) nameMap.get(nodeName);
- if (sibling == null) {
- return Collections.EMPTY_LIST;
- }
- List siblings = new ArrayList();
- while (sibling != null) {
- siblings.add(sibling);
- sibling = sibling.getNextSibling();
- }
- return siblings;
+ ChildNodeEntry remove(String uuid) {
+ ChildNodeEntry entry = (ChildNodeEntry) entries.get(uuid);
+ if (entry != null) {
+ return remove(entry.getName(), entry.getIndex());
+ }
+ return entry;
}
- ChildNodeEntry get(String uuid) {
- return (ChildNodeEntry) entries.get(uuid);
+ public ChildNodeEntry remove(ChildNodeEntry entry) {
+ return remove(entry.getName(), entry.getIndex());
}
- ChildNodeEntry get(QName nodeName, int index) {
- if (index < 1) {
- throw new IllegalArgumentException("index is 1-based");
- }
-
- ChildNodeEntry sibling = (ChildNodeEntry) nameMap.get(nodeName);
- while (sibling != null) {
- if (sibling.getIndex() == index) {
- return sibling;
- }
- sibling = sibling.getNextSibling();
- }
- return null;
+ public void removeAll() {
+ nameMap.clear();
+ entries.clear();
}
/**
@@ -1030,6 +1066,28 @@
}
}
+ //------------------------------------------------< Cloneable support >
+ /**
+ * Returns a shallow copy of this <code>ChildNodeEntries</code> instance;
+ * the entries themselves are not cloned.
+ * @return a shallow copy of this instance.
+ */
+ protected Object clone() {
+ ChildNodeEntries clone = new ChildNodeEntries();
+ clone.entries = (LinkedMap) entries.clone();
+ clone.nameMap = new HashMap(nameMap.size());
+ for (Iterator it = nameMap.keySet().iterator(); it.hasNext(); ) {
+ Object key = it.next();
+ Object obj = nameMap.get(key);
+ if (obj instanceof ArrayList) {
+ // clone List
+ obj = ((ArrayList) obj).clone();
+ }
+ clone.nameMap.put(key, obj);
+ }
+ return clone;
+ }
+
//----------------------------------------------------< inner classes >
class OrderedMapIterator implements ListIterator {
@@ -1082,13 +1140,16 @@
/**
* <code>ChildNodeEntry</code> specifies the name, index (in the case of
* same-name siblings) and the UUID of a child node entry.
+ * <p/>
+ * <code>ChildNodeEntry</code> instances are immutable.
*/
public static class ChildNodeEntry {
- private QName name;
- private int index; // 1-based index for same-name siblings
- private String uuid;
- private ChildNodeEntry nextSibling;
+ private int hash = 0;
+
+ private final QName name;
+ private final int index; // 1-based index for same-name siblings
+ private final String uuid;
private ChildNodeEntry(QName name, String uuid, int index) {
if (name == null) {
@@ -1105,8 +1166,6 @@
throw new IllegalArgumentException("index is 1-based");
}
this.index = index;
-
- nextSibling = null;
}
public String getUUID() {
@@ -1121,29 +1180,6 @@
return index;
}
- public ChildNodeEntry getNextSibling() {
- return nextSibling;
- }
-
- void setNextSibling(ChildNodeEntry nextSibling) {
- if (nextSibling != null && !nextSibling.getName().equals(name)) {
- throw new IllegalArgumentException("not a same-name sibling entry");
- }
-
- this.nextSibling = nextSibling;
- }
-
- int incIndex() {
- return ++index;
- }
-
- int decIndex() {
- if (index == 1) {
- throw new IndexOutOfBoundsException();
- }
- return --index;
- }
-
//---------------------------------------< java.lang.Object overrides >
public boolean equals(Object obj) {
if (this == obj) {
@@ -1161,15 +1197,17 @@
return name.toString() + "[" + index + "] -> " + uuid;
}
- /**
- * Returns zero to satisfy the Object equals/hashCode contract.
- * This class is mutable and not meant to be used as a hash key.
- *
- * @return always zero
- * @see Object#hashCode()
- */
public int hashCode() {
- return 0;
+ // ChildNodeEntry is immutable, we can store the computed hash code value
+ int h = hash;
+ if (h == 0) {
+ h = 17;
+ h = 37 * h + name.hashCode();
+ h = 37 * h + uuid.hashCode();
+ h = 37 * h + index;
+ hash = h;
+ }
+ return h;
}
}
}
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java?rev=373549&r1=373548&r2=373549&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java Mon Jan 30 09:00:59 2006
@@ -38,6 +38,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.Arrays;
/**
* <code>Serializer</code> is a utility class that provides static methods
@@ -47,8 +48,9 @@
*/
public final class Serializer {
- private static final UUID NULL_UUID_PLACEHOLDER =
- UUID.fromString("00000000-0000-0000-0000-000000000000");
+ private static final byte[] NULL_UUID_PLACEHOLDER_BYTES = new byte[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
/**
* encoding used for serializing String values
@@ -73,9 +75,9 @@
out.writeUTF(state.getNodeTypeName().toString());
// parentUUID
if (state.getParentUUID() == null) {
- out.write(NULL_UUID_PLACEHOLDER.getRawBytes());
+ out.write(NULL_UUID_PLACEHOLDER_BYTES);
} else {
- out.write(UUID.fromString(state.getParentUUID()).getRawBytes());
+ out.write(UUID.stringToBytes(state.getParentUUID()));
}
// definitionId
out.writeUTF(state.getDefinitionId().toString());
@@ -100,7 +102,7 @@
for (Iterator iter = c.iterator(); iter.hasNext();) {
NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
out.writeUTF(entry.getName().toString()); // name
- out.write(UUID.fromString(entry.getUUID()).getRawBytes()); // uuid
+ out.write(UUID.stringToBytes(entry.getUUID())); // uuid
}
}
@@ -123,9 +125,8 @@
// parentUUID (may be null)
byte[] uuidBytes = new byte[UUID.UUID_BYTE_LENGTH];
in.readFully(uuidBytes);
- UUID uuid = new UUID(uuidBytes);
- if (!uuid.equals(NULL_UUID_PLACEHOLDER)) {
- state.setParentUUID(uuid.toString());
+ if (!Arrays.equals(uuidBytes, NULL_UUID_PLACEHOLDER_BYTES)) {
+ state.setParentUUID(UUID.bytesToString(uuidBytes));
}
// definitionId
s = in.readUTF();
@@ -153,7 +154,7 @@
QName name = QName.valueOf(in.readUTF()); // name
// uuid
in.readFully(uuidBytes);
- state.addChildNodeEntry(name, new UUID(uuidBytes).toString());
+ state.addChildNodeEntry(name, UUID.bytesToString(uuidBytes));
}
}