You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2005/06/13 10:45:50 UTC
svn commit: r190372 [1/2] - in /incubator/jackrabbit/trunk/src:
java/org/apache/jackrabbit/core/ java/org/apache/jackrabbit/core/lock/
java/org/apache/jackrabbit/core/state/
test/org/apache/jackrabbit/test/api/lock/
Author: dpfister
Date: Mon Jun 13 01:45:49 2005
New Revision: 190372
URL: http://svn.apache.org/viewcvs?rev=190372&view=rev
Log:
True caching in CachingHierarchyManager
Added:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeStateListener.java (with props)
Modified:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/BatchedItemOperations.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/CachingHierarchyManager.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PathMap.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/BatchedItemOperations.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/BatchedItemOperations.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/BatchedItemOperations.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/BatchedItemOperations.java Mon Jun 13 01:45:49 2005
@@ -420,23 +420,25 @@
// 3. do move operation (modify and store affected states)
+
boolean renameOnly = srcParent.getUUID().equals(destParent.getUUID());
- // remove from old parent
- if (!renameOnly) {
- target.removeParentUUID(srcParent.getUUID());
- }
int srcNameIndex = srcName.getIndex();
if (srcNameIndex == 0) {
srcNameIndex = 1;
}
- srcParent.removeChildNodeEntry(srcName.getName(), srcNameIndex);
- // add to new parent
- if (!renameOnly) {
+ // remove from old parent
+ if (renameOnly) {
+ destParent.renameChildNodeEntry(srcName.getName(), srcNameIndex,
+ destName.getName());
+ } else {
+ target.removeParentUUID(srcParent.getUUID());
target.addParentUUID(destParent.getUUID());
+
+ destParent.addChildNodeEntry(destName.getName(), target.getUUID());
+ srcParent.removeChildNodeEntry(srcName.getName(), srcNameIndex);
}
- destParent.addChildNodeEntry(destName.getName(), target.getUUID());
// change definition (id) of target node
NodeDef newTargetDef =
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/CachingHierarchyManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/CachingHierarchyManager.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/CachingHierarchyManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/CachingHierarchyManager.java Mon Jun 13 01:45:49 2005
@@ -16,119 +16,250 @@
*/
package org.apache.jackrabbit.core;
-import org.apache.commons.collections.ReferenceMap;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.ItemStateManager;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.NodeStateListener;
+import org.apache.log4j.Logger;
+import org.apache.commons.collections.map.ReferenceMap;
import javax.jcr.ItemNotFoundException;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
-import java.util.Map;
+import java.util.List;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.util.Set;
/**
- * <code>CachingHierarchyManager</code> is a simple wrapper for a
- * <code>HierarchyManager</code> that caches the <code>ItemId</code> to <code>Path</code>
- * mappings returned by the underlying <code>HierarchyManager</code> for better
- * performance.
- * <p/>
- * Please keep in mind that this cache of <code>Path</code>s is not automatically
- * updated when the underlying hierarchy is changing. Therefore it should only be
- * used with caution and in special situations (usually only locally within a
- * narrow scope) where the underlying hierarchy is not expected to change.
+ * Implementation of a <code>HierarchyManager</code> that caches paths of
+ * items.
*/
-public class CachingHierarchyManager implements HierarchyManager {
+public class CachingHierarchyManager extends HierarchyManagerImpl
+ implements NodeStateListener {
- private final HierarchyManager delegatee;
+ /**
+ * Default upper limit of cached states
+ */
+ public static final int DEFAULT_UPPER_LIMIT = 100;
- // map of item id to list of paths
- private Map pathCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.HARD);
- private Map zombiePathCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.HARD);
+ /**
+ * Logger instance
+ */
+ private static Logger log = Logger.getLogger(CachingHierarchyManager.class);
- // map of path to item id
- private Map idCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.HARD);
+ /**
+ * Mapping of paths to children in the path map
+ */
+ private final PathMap pathCache = new PathMap();
/**
- * @param hierMgr
+ * Mapping of item ids to <code>LRUEntry</code> in the path map
*/
- public CachingHierarchyManager(HierarchyManager hierMgr) {
- delegatee = hierMgr;
- }
+ private final ReferenceMap idCache = new ReferenceMap(
+ ReferenceMap.HARD, ReferenceMap.HARD);
/**
- * Returns the wrapped <code>HierarchyManager</code> instance
- *
- * @return the wrapped <code>HierarchyManager</code> instance
+ * Set of items that were moved
+ */
+ private final Set movedIds = new HashSet();
+
+ /**
+ * Cache monitor object
+ */
+ private final Object cacheMonitor = new Object();
+
+ /**
+ * Upper limit
*/
- public HierarchyManager unwrap() {
- return delegatee;
+ private final int upperLimit;
+
+ /**
+ * Head of LRU
+ */
+ private LRUEntry head;
+
+ /**
+ * Tail of LRU
+ */
+ private LRUEntry tail;
+
+ /**
+ * Create a new instance of this class. This hierarchy manager will not
+ * check for item states that have been moved into attic space
+ * @param rootNodeUUID root node UUID
+ * @param provider item state manager
+ * @param nsResolver namespace resolver
+ */
+ public CachingHierarchyManager(String rootNodeUUID,
+ ItemStateManager provider,
+ NamespaceResolver nsResolver) {
+ this(rootNodeUUID, provider, nsResolver, null);
}
/**
- * Clears the cache.
+ * Create a new instance of this class.
+ * @param rootNodeUUID root node UUID
+ * @param provider item state manager
+ * @param nsResolver namespace resolver
+ * @param attic item state manager for states in the attic space
*/
- public synchronized void clearCache() {
- pathCache.clear();
- zombiePathCache.clear();
- idCache.clear();
+ public CachingHierarchyManager(String rootNodeUUID,
+ ItemStateManager provider,
+ NamespaceResolver nsResolver,
+ ItemStateManager attic) {
+
+ super(rootNodeUUID, provider, nsResolver, attic);
+
+ this.upperLimit = DEFAULT_UPPER_LIMIT;
}
//-----------------------------------------------------< HierarchyManager >
+
/**
* {@inheritDoc}
+ *
+ * Check the item indicated inside our path cache first.
*/
public NodeId[] listParents(ItemId id)
throws ItemNotFoundException, RepositoryException {
- return delegatee.listParents(id);
+
+ if (id.denotesNode()) {
+ PathMap.Element element = get(id);
+ if (element != null) {
+ PathMap.Element parent = element.getParent();
+ if (parent != null) {
+ LRUEntry entry = (LRUEntry) element.get();
+ if (entry != null) {
+ return new NodeId[] { (NodeId) entry.getId() };
+ }
+ }
+ }
+ }
+ return super.listParents(id);
}
/**
* {@inheritDoc}
+ *
+ * Check the path indicated inside our cache first.
*/
- public ItemId[] listChildren(NodeId id)
- throws ItemNotFoundException, RepositoryException {
- return delegatee.listChildren(id);
+ public ItemId resolvePath(Path path)
+ throws PathNotFoundException, RepositoryException {
+
+ PathMap.Element element = map(path);
+ if (element == null) {
+ return super.resolvePath(path);
+ }
+ LRUEntry entry = (LRUEntry) element.get();
+ if (element.hasPath(path)) {
+ entry.touch();
+ return entry.getId();
+ }
+ return super.resolvePath(path, entry.getId(), element.getDepth() + 1);
}
/**
* {@inheritDoc}
+ *
+ * Cache the intermediate item inside our cache.
*/
- public ItemId[] listZombieChildren(NodeId id)
- throws ItemNotFoundException, RepositoryException {
- return delegatee.listZombieChildren(id);
+ protected ItemId resolvePath(Path path, ItemState state, int next)
+ throws PathNotFoundException, ItemStateException {
+
+ if (state.isNode() && !isCached(state.getId())) {
+ try {
+ Path.PathBuilder builder = new Path.PathBuilder();
+ Path.PathElement[] elements = path.getElements();
+ for (int i = 0; i < next; i++) {
+ builder.addLast(elements[i]);
+ }
+ Path parentPath = builder.getPath();
+ cache(state, parentPath);
+ } catch (MalformedPathException mpe) {
+ log.warn("Failed to build path of " + state.getId(), mpe);
+ }
+ }
+ return super.resolvePath(path, state, next);
}
/**
* {@inheritDoc}
+ *
+ * Overridden method simply checks whether we have an item matching the id
+ * and returns its path, otherwise calls base implementation.
*/
- public synchronized ItemId resolvePath(Path path)
- throws PathNotFoundException, RepositoryException {
- // check cache first
- ItemId id = (ItemId) idCache.get(path);
- if (id != null) {
- return id;
+ public synchronized Path getPath(ItemId id)
+ throws ItemNotFoundException, RepositoryException {
+
+ if (id.denotesNode()) {
+ PathMap.Element element = get(id);
+ if (element != null) {
+ try {
+ return element.getPath();
+ } catch (MalformedPathException mpe) {
+ String msg = "Failed to build path of " + id;
+ log.debug(msg);
+ throw new RepositoryException(msg, mpe);
+ }
+ }
}
- id = delegatee.resolvePath(path);
- idCache.put(path, id);
- return id;
+ return super.getPath(id);
}
/**
* {@inheritDoc}
- */
- public synchronized Path getPath(ItemId id)
- throws ItemNotFoundException, RepositoryException {
- return getAllPaths(id, false)[0];
+ *
+ * Overridden method tries to find a mapping for the intermediate item
+ * <code>state</code> and add its path elements to the builder currently
+ * being used. If no mapping is found, the item is cached instead after
+ * the base implementation has been invoked.
+ */
+ protected void getPath(Path.PathBuilder builder, ItemState state)
+ throws ItemStateException, RepositoryException {
+
+ if (state.isNode()) {
+ PathMap.Element element = get(state.getId());
+ if (element != null) {
+ try {
+ Path.PathElement[] elements = element.getPath().getElements();
+ for (int i = elements.length - 1; i >= 0; i--) {
+ builder.addFirst(elements[i]);
+ }
+ return;
+ } catch (MalformedPathException mpe) {
+ String msg = "Failed to build path of " + state.getId();
+ log.debug(msg);
+ throw new RepositoryException(msg, mpe);
+ }
+ }
+ }
+
+ super.getPath(builder, state);
+
+ if (state.isNode()) {
+ try {
+ cache(state, builder.getPath());
+ } catch (MalformedPathException mpe) {
+ log.warn("Failed to build path of " + state.getId());
+ }
+ }
}
/**
* {@inheritDoc}
*/
- public QName getName(ItemId itemId)
+ public QName getName(ItemId id)
throws ItemNotFoundException, RepositoryException {
- if (itemId.denotesNode()) {
- return getPath(itemId).getNameElement().getName();
- } else {
- PropertyId propId = (PropertyId) itemId;
- return propId.getName();
+
+ if (id.denotesNode()) {
+ PathMap.Element element = get(id);
+ if (element != null) {
+ return element.getName();
+ }
}
+ return super.getName(id);
}
/**
@@ -136,11 +267,14 @@
*/
public int getDepth(ItemId id)
throws ItemNotFoundException, RepositoryException {
- if (pathCache.containsKey(id)) {
- return getPath(id).getAncestorCount();
- } else {
- return delegatee.getDepth(id);
+
+ if (id.denotesNode()) {
+ PathMap.Element element = get(id);
+ if (element != null) {
+ return element.getDepth();
+ }
}
+ return super.getDepth(id);
}
/**
@@ -148,52 +282,458 @@
*/
public boolean isAncestor(NodeId nodeId, ItemId itemId)
throws ItemNotFoundException, RepositoryException {
- if (pathCache.containsKey(nodeId) && pathCache.containsKey(itemId)) {
- // use cached Path objects rather than calling delegatee
- try {
- return getPath(nodeId).isAncestorOf(getPath(itemId));
- } catch (MalformedPathException mpe) {
- // should never get here...
- String msg = "failed to determine degree of relationship of "
- + nodeId + " and " + itemId;
- throw new ItemNotFoundException(msg, mpe);
+
+ if (itemId.denotesNode()) {
+ PathMap.Element element = get(nodeId);
+ if (element != null) {
+ PathMap.Element child = get(itemId);
+ if (child != null) {
+ return element.isAncestorOf(child);
+ }
}
- } else {
- return delegatee.isAncestor(nodeId, itemId);
}
+ return super.isAncestor(nodeId, itemId);
}
/**
* {@inheritDoc}
*/
- public synchronized Path[] getAllPaths(ItemId id)
+ public Path[] getAllPaths(ItemId id, boolean includeZombies)
throws ItemNotFoundException, RepositoryException {
- return getAllPaths(id, false);
+
+ if (!includeZombies) {
+ return new Path[] { getPath(id) };
+ }
+ return super.getAllPaths(id, includeZombies);
}
+ //----------------------------------------------------< ItemStateListener >
+
/**
* {@inheritDoc}
*/
- public synchronized Path[] getAllPaths(ItemId id, boolean includeZombies)
- throws ItemNotFoundException, RepositoryException {
- // check cache first
- Path[] paths;
- if (includeZombies) {
- paths = (Path[]) zombiePathCache.get(id);
- if (paths != null) {
- return paths;
+ public void stateCreated(ItemState created) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stateModified(ItemState modified) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stateDestroyed(ItemState destroyed) {
+ destroyed.removeListener(this);
+ evict(destroyed.getId());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stateDiscarded(ItemState discarded) {
+ discarded.removeListener(this);
+ evict(discarded.getId());
+ }
+
+ /**
+ * Called when an <code>ItemState</code> has been overlaid by some
+ * other state that now takes its identity. This notification is sent
+ * on the state being overlaid.
+ *
+ * @param overlayer the <code>ItemState</code> that overlays this state
+ */
+ public void stateOverlaid(ItemState overlayer) {
+ if (overlayer.isNode()) {
+ overlayer.getOverlayedState().removeListener(this);
+ overlayer.addListener(this);
+ }
+ }
+
+ /**
+ * Called when an <code>ItemState</code> no longer overlayes some other
+ * item state. This notification is sent on the state overlaying another
+ * state.
+ *
+ * @param overlayer the <code>ItemState</code> that overlaid another
+ * item state. To get the overlaid state, invoke
+ * {@link ItemState#getOverlayedState()}
+ */
+ public void stateUncovered(ItemState overlayer) {
+ if (overlayer.isNode()) {
+ overlayer.removeListener(this);
+ overlayer.getOverlayedState().addListener(this);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void nodeAdded(NodeState state, QName name, int index, String uuid) {
+ try {
+ Path path = Path.create(getPath(state.getId()), name, index, true);
+ insert(path, new NodeId(uuid));
+ } catch (PathNotFoundException e) {
+ log.warn("Added node does not have parent, ignoring event.");
+ } catch (MalformedPathException e) {
+ log.warn("Unable to create path of " + uuid, e);
+ } catch(ItemNotFoundException e) {
+ log.warn("Unable to get path of " + state.getId(), e);
+ } catch(RepositoryException e) {
+ log.warn("Unable to get path of " + state.getId(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Generate subsequent add and remove notifications for every replacement.
+ */
+ public void nodesReplaced(NodeState state) {
+ List entries = state.getReorderedChildNodeEntries();
+ if (entries.size() == 0) {
+ return;
+ }
+
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ NodeState.ChildNodeEntry now = (NodeState.ChildNodeEntry) iter.next();
+ NodeState.ChildNodeEntry old = null;
+
+ List list = ((NodeState) state.getOverlayedState()).
+ getChildNodeEntries(now.getUUID());
+ if (list.size() > 0) {
+ old = (NodeState.ChildNodeEntry) list.get(0);
}
- paths = delegatee.getAllPaths(id, includeZombies);
- zombiePathCache.put(id, paths);
- } else {
- paths = (Path[]) pathCache.get(id);
- if (paths != null) {
- return paths;
+ if (old == null) {
+ log.warn("Reordered child node not found in old list.");
+ continue;
}
- paths = delegatee.getAllPaths(id, includeZombies);
- pathCache.put(id, paths);
+
+ nodeAdded(state, now.getName(), now.getIndex(), now.getUUID());
+ nodeRemoved(state, old.getName(), old.getIndex(), old.getUUID());
}
- return paths;
}
-}
+ /**
+ * {@inheritDoc}
+ */
+ public void nodeRemoved(NodeState state, QName name, int index, String uuid) {
+ try {
+ Path path = Path.create(getPath(state.getId()), name, index, true);
+ remove(path, new NodeId(uuid));
+ } catch (PathNotFoundException e) {
+ log.warn("Added node does not have parent, ignoring event.");
+ } catch (MalformedPathException e) {
+ log.warn("Unable to create path of " + uuid, e);
+ } catch(ItemNotFoundException e) {
+ log.warn("Unable to get path of " + state.getId(), e);
+ } catch(RepositoryException e) {
+ log.warn("Unable to get path of " + state.getId(), e);
+ }
+ }
+
+ //-------------------------------------------------------< private methods >
+
+ /**
+ * Return a cached element in the path map, given its id
+ * @param id node id
+ * @return cached element, <code>null</code> if not found
+ */
+ private PathMap.Element get(ItemId id) {
+ synchronized (cacheMonitor) {
+ LRUEntry entry = (LRUEntry) idCache.get(id);
+ if (entry != null) {
+ entry.touch();
+ return entry.getElement();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Return the nearest cached element in the path map, given a path.
+ * The returned element is guaranteed to have an associated object that
+ * is not <code>null</code>.
+ * @param path path
+ * @return cached element, <code>null</code> if not found
+ */
+ private PathMap.Element map(Path path) {
+ synchronized (cacheMonitor) {
+ PathMap.Element element = pathCache.map(path, false);
+ while (element != null) {
+ LRUEntry entry = (LRUEntry) element.get();
+ if (entry != null) {
+ entry.touch();
+ return element;
+ }
+ element = element.getParent();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Cache an item in the hierarchy given its id and path. Adds a listener
+ * for this item state to get notified about changes.
+ * @param state item state, may be <code>null</code>
+ * @param path path to item
+ */
+ private void cache(ItemState state, Path path) {
+ ItemId id = state.getId();
+
+ synchronized (cacheMonitor) {
+ if (idCache.get(id) != null) {
+ return;
+ }
+ if (idCache.size() >= upperLimit) {
+ removeLRU();
+ }
+
+ PathMap.Element element = pathCache.put(path);
+ LRUEntry entry = new LRUEntry(id, element);
+ element.set(entry);
+ idCache.put(id, entry);
+
+ state.addListener(this);
+ }
+ }
+
+ /**
+ * Remove least recently used item. Scans the LRU list from head to tail
+ * and removes the first item that has no children.
+ */
+ private void removeLRU() {
+ synchronized (cacheMonitor) {
+ LRUEntry entry = head;
+ while (entry != null) {
+ PathMap.Element element = entry.getElement();
+ if (element.getChildrenCount() == 0) {
+ evict(entry, true);
+ return;
+ }
+ entry = entry.getNext();
+ }
+ }
+ }
+
+ /**
+ * Return a flag indicating whether a certain element is cached.
+ * @param id item id
+ * @return <code>true</code> if the item is already cached;
+ * <code>false</code> otherwise
+ */
+ private boolean isCached(ItemId id) {
+ synchronized (cacheMonitor) {
+ return idCache.get(id) != null;
+ }
+ }
+
+ /**
+ * Evict item from cache. Evicts the associated <code>LRUEntry</code>
+ * @param id item id
+ */
+ private void evict(ItemId id) {
+ synchronized (cacheMonitor) {
+ LRUEntry entry = (LRUEntry) idCache.get(id);
+ if (entry != null) {
+ evict(entry, true);
+ }
+ }
+ }
+
+ /**
+ * Evict item from cache
+ * @param entry LRU entry
+ * @param removeFromPathCache whether to remove from path cache
+ */
+ private void evict(LRUEntry entry, boolean removeFromPathCache) {
+ synchronized (cacheMonitor) {
+ if (removeFromPathCache) {
+ PathMap.Element element = entry.getElement();
+ evict(element);
+ element.remove();
+ } else {
+ idCache.remove(entry.getId());
+ entry.remove();
+ }
+ }
+ }
+
+ /**
+ * Evict path map element from cache. This will traverse all children
+ * of this element and evict the objects associated with them
+ * @param element path map element
+ */
+ private void evict(PathMap.Element element) {
+ synchronized (cacheMonitor) {
+ element.traverse(new PathMap.ElementVisitor() {
+ public void elementVisited(PathMap.Element element) {
+ evict((LRUEntry) element.get(), false);
+ }
+ }, false);
+ }
+ }
+
+ /**
+ * Insert a node into the cache. This will automatically shift
+ * all indexes of sibling nodes having index greater or equal.
+ * @param path child path
+ * @param id node id
+ */
+ private void insert(Path path, ItemId id) throws PathNotFoundException {
+ synchronized (cacheMonitor) {
+ PathMap.Element element = null;
+
+ LRUEntry entry = (LRUEntry) idCache.get(id);
+ if (entry != null) {
+ element = entry.getElement();
+ element.remove();
+ }
+
+ PathMap.Element parent = pathCache.map(path.getAncestor(1), true);
+ if (parent != null) {
+ parent.insert(path.getNameElement());
+ }
+ if (element != null) {
+ pathCache.put(path, element);
+
+ /* Remember this as a move */
+ movedIds.add(id);
+ }
+ }
+ }
+
+ /**
+ * Remove an item from the cache in order to shift the indexes
+ * of items following this item.
+ * @param path child path
+ * @param id node id
+ */
+ private void remove(Path path, ItemId id) throws PathNotFoundException {
+ synchronized (cacheMonitor) {
+ /* If we remembered this as a move, ignore this event */
+ if (movedIds.remove(id)) {
+ return;
+ }
+ PathMap.Element parent = pathCache.map(path.getAncestor(1), true);
+ if (parent != null) {
+ PathMap.Element element = parent.remove(path.getNameElement());
+ if (element != null) {
+ evict(element);
+ }
+ }
+ }
+ }
+
+ /**
+ * Entry in the LRU list
+ */
+ private class LRUEntry {
+
+ /**
+ * Previous entry
+ */
+ private LRUEntry previous;
+
+ /**
+ * Next entry
+ */
+ private LRUEntry next;
+
+ /**
+ * Item id
+ */
+ private final ItemId id;
+
+ /**
+ * Element in path map
+ */
+ private final PathMap.Element element;
+
+ /**
+ * Create a new instance of this class
+ * @param id item id
+ */
+ public LRUEntry(ItemId id, PathMap.Element element) {
+ this.id = id;
+ this.element = element;
+
+ append();
+ }
+
+ /**
+ * Append entry to end of LRU list
+ */
+ public void append() {
+ if (tail == null) {
+ head = tail = this;
+ } else {
+ previous = tail;
+ tail.next = this;
+ tail = this;
+ }
+ }
+
+ /**
+ * Remove entry from LRU list
+ */
+ public void remove() {
+ if (previous != null) {
+ previous.next = next;
+ }
+ if (next != null) {
+ next.previous = previous;
+ }
+ if (head == this) {
+ head = next;
+ }
+ if (tail == this) {
+ tail = previous;
+ }
+ previous = next = null;
+ }
+
+ /**
+ * Touch entry. Removes it from its current position in the LRU list
+ * and moves it to the end.
+ */
+ public void touch() {
+ remove();
+ append();
+ }
+
+ /**
+ * Return previous LRU entry
+ * @return previous LRU entry
+ */
+ public LRUEntry getPrevious() {
+ return previous;
+ }
+
+ /**
+ * Return next LRU entry
+ * @return next LRU entry
+ */
+ public LRUEntry getNext() {
+ return next;
+ }
+
+ /**
+ * Return item ID
+ * @return item ID
+ */
+ public ItemId getId() {
+ return id;
+ }
+
+ /**
+ * Return element in path map
+ * @return element in path map
+ */
+ public PathMap.Element getElement() {
+ return element;
+ }
+ }
+}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java Mon Jun 13 01:45:49 2005
@@ -211,59 +211,83 @@
throw new RepositoryException(msg);
}
- NodeState parentState;
+ return resolvePath(path, rootNodeId, 1);
+ }
+
+ /**
+ * Resolve a path into an item ID. Recursively invoked method that may be
+ * overridden by some subclass to either return cached responses or add
+ * response to cache.
+ * @param path full path of item to resolve
+ * @param id item id
+ * @param next next path element index to resolve
+ */
+ protected ItemId resolvePath(Path path, ItemId id, int next)
+ throws RepositoryException {
+
try {
- parentState = (NodeState) getItemState(rootNodeId);
+ return resolvePath(path, getItemState(id), next);
+ } catch (NoSuchItemStateException e) {
+ String msg = "failed to retrieve state of intermediary node";
+ log.debug(msg);
+ throw new RepositoryException(msg, e);
} catch (ItemStateException e) {
- String msg = "failed to retrieve state of root node";
+ String msg = "failed to retrieve state of intermediary node";
log.debug(msg);
throw new RepositoryException(msg, e);
}
+ }
- Path.PathElement[] elems = path.getElements();
- for (int i = 1; i < elems.length; i++) {
- Path.PathElement elem = elems[i];
- QName name = elem.getName();
- int index = elem.getIndex();
- if (index == 0) {
- index = 1;
- }
- if (parentState.hasChildNodeEntry(name, index)) {
- // child node
- NodeState.ChildNodeEntry nodeEntry =
- parentState.getChildNodeEntry(name, index);
- if (i == elems.length - 1) {
- // last element in the path
- return new NodeId(nodeEntry.getUUID());
- }
- try {
- parentState =
- (NodeState) getItemState(new NodeId(nodeEntry.getUUID()));
- } catch (ItemStateException e) {
- String msg = "failed to retrieve state of intermediary node";
- log.debug(msg);
- throw new RepositoryException(msg, e);
- }
- continue;
- } else if (parentState.hasPropertyEntry(name)) {
- // property
- if (index > 1) {
- // properties can't have same name siblings
- throw new PathNotFoundException(safeGetJCRPath(path));
- }
- if (i == elems.length - 1) {
- return new PropertyId(parentState.getUUID(), name);
- } else {
- // property is not the last element in the path
- throw new PathNotFoundException(safeGetJCRPath(path));
- }
- } else {
- // no such item
+ /**
+ * Resolve a path into an item ID. Recursively invoked method that may be
+ * overridden by some subclass to either return cached responses or add
+ * response to cache.
+ * @param path full path of item to resolve
+ * @param state intermediate state
+ * @param next next path element index to resolve
+ */
+ protected ItemId resolvePath(Path path, ItemState state, int next)
+ throws PathNotFoundException, ItemStateException {
+
+ Path.PathElement[] elements = path.getElements();
+ if (elements.length == next) {
+ return state.getId();
+ }
+ Path.PathElement elem = elements[next];
+
+ QName name = elem.getName();
+ int index = elem.getIndex();
+ if (index == 0) {
+ index = 1;
+ }
+
+ NodeState parentState = (NodeState) state;
+ ItemId childId;
+
+ if (parentState.hasChildNodeEntry(name, index)) {
+ // child node
+ NodeState.ChildNodeEntry nodeEntry =
+ parentState.getChildNodeEntry(name, index);
+ childId = new NodeId(nodeEntry.getUUID());
+
+ } else if (parentState.hasPropertyEntry(name)) {
+ // property
+ if (index > 1) {
+ // properties can't have same name siblings
+ throw new PathNotFoundException(safeGetJCRPath(path));
+
+ } else if (next < elements.length - 1) {
+ // property is not the last element in the path
throw new PathNotFoundException(safeGetJCRPath(path));
}
- }
- throw new PathNotFoundException(safeGetJCRPath(path));
+ childId = new PropertyId(parentState.getUUID(), name);
+
+ } else {
+ // no such item
+ throw new PathNotFoundException(safeGetJCRPath(path));
+ }
+ return resolvePath(path, getItemState(childId), next + 1);
}
/**
@@ -271,58 +295,11 @@
*/
public synchronized Path getPath(ItemId id)
throws ItemNotFoundException, RepositoryException {
- try {
- Path.PathBuilder builder = new Path.PathBuilder();
-
- ItemState state = getItemState(id);
- String parentUUID = state.getParentUUID();
- if (parentUUID == null) {
- // specified id denotes the root node
- builder.addRoot();
- return builder.getPath();
- }
- NodeState parent = (NodeState) getItemState(new NodeId(parentUUID));
- do {
- if (state.isNode()) {
- NodeState nodeState = (NodeState) state;
- String uuid = nodeState.getUUID();
- List entries = parent.getChildNodeEntries(uuid);
- if (entries.isEmpty()) {
- String msg = "failed to build path of " + id + ": "
- + parent.getUUID() + " has no child entry for "
- + uuid;
- log.debug(msg);
- throw new RepositoryException(msg);
- }
- // if the parent has more than one child node entries pointing
- // to the same child node, always use the first one
- NodeState.ChildNodeEntry entry =
- (NodeState.ChildNodeEntry) entries.get(0);
- // add to path
- if (entry.getIndex() == 1) {
- builder.addFirst(entry.getName());
- } else {
- builder.addFirst(entry.getName(), entry.getIndex());
- }
- } else {
- PropertyState propState = (PropertyState) state;
- QName name = propState.getName();
- // add to path
- builder.addFirst(name);
- }
- parentUUID = parent.getParentUUID();
- if (parentUUID != null) {
- state = parent;
- parent = (NodeState) getItemState(new NodeId(parentUUID));
- } else {
- parent = null;
- state = null;
- }
- } while (parent != null);
+ Path.PathBuilder builder = new Path.PathBuilder();
- // add root to path
- builder.addRoot();
+ try {
+ getPath(builder, getItemState(id));
return builder.getPath();
} catch (NoSuchItemStateException nsise) {
String msg = "failed to build path of " + id;
@@ -340,6 +317,57 @@
}
/**
+ * Adds the path element of an item ID to the path currently being built.
+ * Recursively invoked method that may be overridden by some subclass to
+ * either return cached responses or add response to cache. On exit,
+ * <code>builder</code> contains the path of <code>state</code>.
+ *
+ * @param builder builder currently being used
+ * @param state item to find path of
+ */
+ protected void getPath(Path.PathBuilder builder, ItemState state)
+ throws ItemStateException, RepositoryException {
+
+ String parentUUID = state.getParentUUID();
+ if (parentUUID == null) {
+ builder.addRoot();
+ return;
+ }
+
+ NodeState parent = (NodeState) getItemState(new NodeId(parentUUID));
+ getPath(builder, parent);
+
+ if (state.isNode()) {
+ NodeState nodeState = (NodeState) state;
+ String uuid = nodeState.getUUID();
+ List entries = parent.getChildNodeEntries(uuid);
+ if (entries.isEmpty()) {
+ String msg = "failed to build path of " + state.getId() + ": "
+ + parent.getUUID() + " has no child entry for "
+ + uuid;
+ log.debug(msg);
+ throw new ItemNotFoundException(msg);
+ }
+ // if the parent has more than one child node entries pointing
+ // to the same child node, always use the first one
+ NodeState.ChildNodeEntry entry =
+ (NodeState.ChildNodeEntry) entries.get(0);
+ // add to path
+ if (entry.getIndex() == 1) {
+ builder.addLast(entry.getName());
+ } else {
+ builder.addLast(entry.getName(), entry.getIndex());
+ }
+
+ } else {
+ PropertyState propState = (PropertyState) state;
+ QName name = propState.getName();
+ // add to path
+ builder.addLast(name);
+ }
+ }
+
+ /**
* {@inheritDoc}
*/
public QName getName(ItemId itemId)
@@ -388,15 +416,12 @@
public int getDepth(ItemId id)
throws ItemNotFoundException, RepositoryException {
try {
- int depth = 0;
ItemState state = getItemState(id, false);
String parentUUID = state.getParentUUID();
- while (parentUUID != null) {
- state = getItemState(new NodeId(parentUUID), false);
- parentUUID = state.getParentUUID();
- depth++;
+ if (parentUUID != null) {
+ return getDepth(new NodeId(parentUUID)) + 1;
}
- return depth;
+ return 0;
} catch (NoSuchItemStateException nsise) {
String msg = "failed to determine depth of " + id;
log.debug(msg);
@@ -416,12 +441,11 @@
try {
ItemState state = getItemState(itemId, false);
String parentUUID = state.getParentUUID();
- while (parentUUID != null) {
+ if (parentUUID != null) {
if (parentUUID.equals(nodeId.getUUID())) {
return true;
}
- state = getItemState(new NodeId(parentUUID), false);
- parentUUID = state.getParentUUID();
+ return isAncestor(nodeId, new NodeId(parentUUID));
}
return false;
} catch (NoSuchItemStateException nsise) {
@@ -522,7 +546,7 @@
return attic.getItemState(id);
}
// fallback: get transient/persistent state
- return provider.getItemState(id);
+ return getItemState(id);
}
/**
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java Mon Jun 13 01:45:49 2005
@@ -1047,6 +1047,8 @@
* if necessary
*/
state.removeListener(this);
+ persistentState.addListener(this);
+ stateMgr.disconnectTransientItemState(state);
state = persistentState;
state.addListener(this);
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java Mon Jun 13 01:45:49 2005
@@ -551,8 +551,7 @@
throws RepositoryException {
// modify the state of 'this', i.e. the parent node
NodeState thisState = (NodeState) getOrCreateTransientItemState();
- thisState.removeChildNodeEntry(oldName, index);
- thisState.addChildNodeEntry(newName, uuid);
+ thisState.renameChildNodeEntry(oldName, index, newName);
}
protected void removeChildProperty(String propName) throws RepositoryException {
@@ -902,8 +901,7 @@
NodeState persistentState = (NodeState) transientState.getOverlayedState();
if (persistentState == null) {
// this node is 'new'
- persistentState = stateMgr.createNew(transientState.getUUID(),
- transientState.getNodeTypeName(), transientState.getParentUUID());
+ persistentState = stateMgr.createNew(transientState);
}
// copy state from transient state:
// parent uuid's
@@ -924,6 +922,8 @@
transientState.removeListener(this);
// add listener to persistent state
persistentState.addListener(this);
+ // tell state manager to disconnect item state
+ stateMgr.disconnectTransientItemState(transientState);
// swap transient state with persistent state
state = persistentState;
// reset status
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PathMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PathMap.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PathMap.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PathMap.java Mon Jun 13 01:45:49 2005
@@ -30,7 +30,7 @@
/**
* Root element
*/
- private final Child root = new Child(Path.ROOT.getNameElement());
+ private final Element root = new Element(Path.ROOT.getNameElement());
/**
* Map a path to a child. If <code>exact</code> is <code>false</code>,
@@ -40,12 +40,12 @@
* @return child, maybe <code>null</code> if <code>exact</code> is
* <code>true</code>
*/
- public Child map(Path path, boolean exact) {
+ public Element map(Path path, boolean exact) {
Path.PathElement[] elements = path.getElements();
- Child current = root;
+ Element current = root;
for (int i = 1; i < elements.length; i++) {
- Child next = current.getChild(elements[i], false);
+ Element next = current.getChild(elements[i]);
if (next == null) {
if (exact) {
return null;
@@ -58,48 +58,53 @@
}
/**
- * Create a child given by its path. The path map will create any necessary
- * intermediate children.
+ * Create an element given by its path. The path map will create any necessary
+ * intermediate elements.
* @param path path to child
* @param obj object to store at destination
*/
- public Child put(Path path, Object obj) {
- Child child = put(path);
- child.obj = obj;
- return child;
+ public Element put(Path path, Object obj) {
+ Element element = put(path);
+ element.obj = obj;
+ return element;
}
/**
- * Create an empty child given by its path.
+ * Put an element given by its path. The path map will create any necessary
+ * intermediate elements.
* @param path path to child
+ * @param element element to store at destination
*/
- public Child put(Path path) {
+ public void put(Path path, Element element) {
Path.PathElement[] elements = path.getElements();
- Child current = root;
+ Element current = root;
- for (int i = 1; i < elements.length; i++) {
- current = current.getChild(elements[i], true);
+ for (int i = 1; i < elements.length - 1; i++) {
+ Element next = current.getChild(elements[i]);
+ if (next == null) {
+ next = current.createChild(elements[i]);
+ }
+ current = next;
}
- return current;
+ current.put(path.getNameElement(), element);
}
/**
- * Ressurrect a child previously removed, given by its path and the
- * child structure. If an item at path already exists, nothing happens.
- * @param path new path to child
- * @param zombie previously removed child object to store at destination
+ * Create an empty child given by its path.
+ * @param path path to child
*/
- public void resurrect(Path path, Child zombie) {
+ public Element put(Path path) {
Path.PathElement[] elements = path.getElements();
- Path.PathElement name = path.getNameElement();
- Child parent = root;
+ Element current = root;
- if (map(path, true) == null) {
- for (int i = 1; i < elements.length - 1; i++) {
- parent = parent.getChild(elements[i], true);
+ for (int i = 1; i < elements.length; i++) {
+ Element next = current.getChild(elements[i]);
+ if (next == null) {
+ next = current.createChild(elements[i]);
}
- parent.setChild(name, zombie);
+ current = next;
}
+ return current;
}
/**
@@ -109,7 +114,7 @@
* or not; otherwise call back on non-empty children
* only
*/
- public void traverse(ChildVisitor visitor, boolean includeEmpty) {
+ public void traverse(ElementVisitor visitor, boolean includeEmpty) {
root.traverse(visitor, includeEmpty);
}
@@ -117,67 +122,72 @@
* Internal class holding the object associated with a certain
* path element.
*/
- public static class Child {
+ public static class Element {
/**
- * Parent child
+ * Parent element
*/
- private Child parent;
+ private Element parent;
/**
- * Map of immediate children of this child.
+ * Map of immediate children
*/
private Map children;
/**
- * Number of non-null children
+ * Number of non-empty children
*/
private int childrenCount;
/**
- * Object associated with this child
+ * Object associated with this element
*/
private Object obj;
/**
- * QName associated with this child
+ * QName associated with this element
*/
private QName name;
/**
- * index associated with this child
+ * index associated with this element
*/
private int index;
/**
* Create a new instance of this class with a path element.
- * @param element path element of this child
+ * @param nameIndex path element of this child
*/
- Child(Path.PathElement element) {
- this.name = element.getName();
- this.index = element.getIndex();
+ private Element(Path.PathElement nameIndex) {
+ this.name = nameIndex.getName();
+ this.index = nameIndex.getIndex();
}
/**
- * Create a new instance of this class.
+ * Create a child of this node inside the path map.
+ * @param nameIndex position where child is created
+ * @return child
*/
- Child() {
+ private Element createChild(Path.PathElement nameIndex) {
+ Element element = new Element(nameIndex);
+ put(nameIndex, element);
+ return element;
}
/**
* Insert an empty child. Will shift all children having an index
* greater than or equal to the child inserted to the right.
- * @param element child's path element
+ * @param nameIndex position where child is inserted
*/
- public void insertChild(Path.PathElement element) {
- int index = getOneBasedIndex(element) - 1;
+ public void insert(Path.PathElement nameIndex) {
+ int index = getOneBasedIndex(nameIndex) - 1;
if (children != null) {
- ArrayList list = (ArrayList) children.get(element.getName());
+ ArrayList list = (ArrayList) children.get(nameIndex.getName());
if (list != null && list.size() > index) {
for (int i = index; i < list.size(); i++) {
- Child child = (Child) list.get(i);
- if (child != null) {
- child.index++;
+ Element element = (Element) list.get(i);
+ if (element != null) {
+ element.index++;
}
}
list.add(index, null);
@@ -186,112 +196,109 @@
}
/**
- * Remove a child. Will shift all children having an index greater than
- * the child removed to the left.
- * @param element child's path element
- * @return removed child, may be <code>null</code>
- */
- public Child removeChild(Path.PathElement element) {
- int index = getOneBasedIndex(element) - 1;
- if (children != null) {
- ArrayList list = (ArrayList) children.get(element.getName());
- if (list != null && list.size() > index) {
- for (int i = index + 1; i < list.size(); i++) {
- Child child = (Child) list.get(i);
- if (child != null) {
- child.index--;
- }
- }
- Child child = (Child) list.remove(index);
- if (child != null) {
- child.parent = null;
- childrenCount--;
- }
- if (obj == null && childrenCount == 0) {
- remove();
- }
- return child;
- }
- }
- return null;
- }
-
- /**
- * Return a child matching a path element. If a child does not exist
- * at that position and <code>create</code> is <code>true</code> a
- * new child will be created.
- * @param element child's path element
- * @param create flag indicating whether this child should be
- * created if not available
- */
- private Child getChild(Path.PathElement element, boolean create) {
- int index = getOneBasedIndex(element) - 1;
- Child child = null;
+ * Return an element matching a name and index.
+ * @param nameIndex position where child is located
+ * @return element matching <code>nameIndex</code> or <code>null</code> if
+ * none exists.
+ */
+ private Element getChild(Path.PathElement nameIndex) {
+ int index = getOneBasedIndex(nameIndex) - 1;
+ Element element = null;
if (children != null) {
- ArrayList list = (ArrayList) children.get(element.getName());
+ ArrayList list = (ArrayList) children.get(nameIndex.getName());
if (list != null && list.size() > index) {
- child = (Child) list.get(index);
+ element = (Element) list.get(index);
}
}
- if (child == null && create) {
- child = new Child();
- setChild(element, child);
- }
- return child;
+ return element;
}
/**
- * Add a child.
- * @param element child's path element
- * @param child child to add
+ * Link a child of this node. Position is given by <code>nameIndex</code>.
+ * @param nameIndex position where child should be located
+ * @param element element to add
*/
- private void setChild(Path.PathElement element, Child child) {
- int index = getOneBasedIndex(element) - 1;
+ public void put(Path.PathElement nameIndex, Element element) {
+ int index = getOneBasedIndex(nameIndex) - 1;
if (children == null) {
children = new HashMap();
}
- ArrayList list = (ArrayList) children.get(element.getName());
+ ArrayList list = (ArrayList) children.get(nameIndex.getName());
if (list == null) {
list = new ArrayList();
- children.put(element.getName(), list);
+ children.put(nameIndex.getName(), list);
}
while (list.size() < index) {
list.add(null);
}
if (list.size() == index) {
- list.add(child);
+ list.add(element);
} else {
- list.set(index, child);
+ list.set(index, element);
}
- child.parent = this;
- child.name = element.getName();
- child.index = element.getIndex();
+ element.parent = this;
+ element.name = nameIndex.getName();
+ element.index = nameIndex.getIndex();
childrenCount++;
}
/**
- * Remove this child. Delegates the call to the parent item.
+ * Remove a child. Will shift all children having an index greater than
+ * the child removed to the left. If there are no more children left in
+ * this child and no object is associated with this child, the child
+ * itself gets removed.
+ *
+ * @param nameIndex child's path element
+ * @return removed child, may be <code>null</code>
+ */
+ public Element remove(Path.PathElement nameIndex) {
+ int index = getOneBasedIndex(nameIndex) - 1;
+ if (children != null) {
+ ArrayList list = (ArrayList) children.get(nameIndex.getName());
+ if (list != null && list.size() > index) {
+ for (int i = index + 1; i < list.size(); i++) {
+ Element element = (Element) list.get(i);
+ if (element != null) {
+ element.index--;
+ }
+ }
+ Element element = (Element) list.remove(index);
+ if (element != null) {
+ element.parent = null;
+ childrenCount--;
+ }
+ if (childrenCount == 0 && obj == null) {
+ remove();
+ }
+ return element;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove this element. Delegates the call to the parent item.
*/
public void remove() {
if (parent != null) {
- parent.removeChild(getPathElement());
+ parent.remove(getPathElement());
}
}
/**
- * Return the object associated with this child
- * @return object associated with this child
+ * Return the object associated with this element
+ * @return object associated with this element
*/
public Object get() {
return obj;
}
/**
- * Set the object associated with this child
- * @param obj object associated with this child
+ * Set the object associated with this element
+ * @param obj object associated with this element
*/
public void set(Object obj) {
this.obj = obj;
@@ -302,7 +309,7 @@
}
/**
- * Return the name of this child
+ * Return the name of this element
* @return name
*/
public QName getName() {
@@ -310,7 +317,7 @@
}
/**
- * Return the index of this child
+ * Return the index of this element
* @return index
*/
public int getIndex() {
@@ -318,7 +325,7 @@
}
/**
- * Return a path element pointing to this child
+ * Return a path element pointing to this element
* @return path element
*/
public Path.PathElement getPathElement() {
@@ -359,7 +366,7 @@
}
/**
- * Checks whether this child has the specified path. Introduced to
+ * Checks whether this element has the specified path. Introduced to
* avoid catching a <code>MalformedPathException</code> for simple
* path comparisons.
* @param path path to compare to
@@ -371,11 +378,11 @@
}
/**
- * Checks whether this child has the specified path, given by
+ * Checks whether this element has the specified path, given by
* path elements.
* @param elements path elements to compare to
* @param len number of elements to compare to
- * @return <code>true</code> if this child has the path given;
+ * @return <code>true</code> if this element has the path given;
* otherwise <code>false</code>
*/
private boolean hasPath(Path.PathElement[] elements, int len) {
@@ -391,8 +398,8 @@
/**
* Return 1-based index of a path element.
*/
- private static int getOneBasedIndex(Path.PathElement element) {
- int index = element.getIndex();
+ private static int getOneBasedIndex(Path.PathElement nameIndex) {
+ int index = nameIndex.getIndex();
if (index == 0) {
return 1;
} else {
@@ -404,31 +411,31 @@
* Recursively invoked traversal method.
* @param visitor visitor to invoke
* @param includeEmpty if <code>true</code> invoke call back on every
- * child regardless, whether the associated object is empty
+ * element regardless, whether the associated object is empty
* or not; otherwise call back on non-empty children only
*/
- public void traverse(ChildVisitor visitor, boolean includeEmpty) {
+ public void traverse(ElementVisitor visitor, boolean includeEmpty) {
if (children != null) {
Iterator iter = children.values().iterator();
while (iter.hasNext()) {
ArrayList list = (ArrayList) iter.next();
for (int i = 0; i < list.size(); i++) {
- Child child = (Child) list.get(i);
- if (child != null) {
- child.traverse(visitor, includeEmpty);
+ Element element = (Element) list.get(i);
+ if (element != null) {
+ element.traverse(visitor, includeEmpty);
}
}
}
}
if (includeEmpty || obj != null) {
- visitor.childVisited(this);
+ visitor.elementVisited(this);
}
}
/**
- * Return the depth of this child. Defined to be <code>0</code> for the
- * root node and <code>n + 1</code> for some child if the depth of its
- * parent is <code>n</code>.
+ * Return the depth of this element. Defined to be <code>0</code> for the
+ * root element and <code>n + 1</code> for some element if the depth of
+ * its parent is <code>n</code>.
*/
public int getDepth() {
if (parent != null) {
@@ -442,8 +449,8 @@
* child of this node.
* @param other node to check
*/
- public boolean isAncestorOf(Child other) {
- Child parent = other.parent;
+ public boolean isAncestorOf(Element other) {
+ Element parent = other.parent;
while (parent != null) {
if (parent == this) {
return true;
@@ -454,23 +461,31 @@
}
/**
- * Return the parent of this child
- * @return parent or <code>null</code> if this is the root node
+ * Return the parent of this element
+ * @return parent or <code>null</code> if this is the root element
*/
- public Child getParent() {
+ public Element getParent() {
return parent;
}
+
+ /**
+ * Return the children count of this element
+ * @return children count
+ */
+ public int getChildrenCount() {
+ return childrenCount;
+ }
}
/**
- * Child visitor used in {@link PathMap#traverse}
+ * Element visitor used in {@link PathMap#traverse}
*/
- public interface ChildVisitor {
+ public interface ElementVisitor {
/**
- * Invoked for every child visited on a tree traversal
- * @param child child visited
+ * Invoked for every element visited on a tree traversal
+ * @param element element visited
*/
- void childVisited(Child child);
+ void elementVisited(Element element);
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java Mon Jun 13 01:45:49 2005
@@ -105,8 +105,7 @@
PropertyState persistentState = (PropertyState) transientState.getOverlayedState();
if (persistentState == null) {
// this property is 'new'
- persistentState = stateMgr.createNew(transientState.getName(),
- transientState.getParentUUID());
+ persistentState = stateMgr.createNew(transientState);
}
// copy state from transient state
persistentState.setDefinitionId(transientState.getDefinitionId());
@@ -119,6 +118,8 @@
transientState.removeListener(this);
// add listener to persistent state
persistentState.addListener(this);
+ // tell state manager to disconnect item state
+ stateMgr.disconnectTransientItemState(transientState);
// swap transient state with persistent state
state = persistentState;
// reset status
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java Mon Jun 13 01:45:49 2005
@@ -128,7 +128,7 @@
this.wspConfig = wspConfig;
this.rep = rep;
this.stateMgr = new TransactionalItemStateManager(stateMgr, this);
- this.hierMgr = new HierarchyManagerImpl(rep.getRootNodeUUID(),
+ this.hierMgr = new CachingHierarchyManager(rep.getRootNodeUUID(),
this.stateMgr, session.getNamespaceResolver());
this.session = session;
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java Mon Jun 13 01:45:49 2005
@@ -28,6 +28,7 @@
import org.apache.jackrabbit.core.observation.SynchronousEventListener;
import org.apache.jackrabbit.core.observation.EventImpl;
import org.apache.log4j.Logger;
+import org.apache.commons.collections.SequencedHashMap;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
@@ -37,9 +38,8 @@
import javax.jcr.Node;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
-import java.util.Map;
-import java.util.HashMap;
import java.util.ArrayList;
+import java.util.Iterator;
import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
@@ -78,12 +78,6 @@
*/
private final NamespaceResolver nsResolver;
- /**`
- * Map of nodes that been removed and may be re-added as result
- * of a move operation
- */
- private final Map zombieNodes = new HashMap();
-
/**
* Create a new instance of this class.
* @param session system session
@@ -178,9 +172,9 @@
private void save() throws IOException {
final ArrayList list = new ArrayList();
- lockMap.traverse(new PathMap.ChildVisitor() {
- public void childVisited(PathMap.Child child) {
- LockInfo info = (LockInfo) child.get();
+ lockMap.traverse(new PathMap.ElementVisitor() {
+ public void elementVisited(PathMap.Element element) {
+ LockInfo info = (LockInfo) element.get();
if (!info.sessionScoped) {
list.add(info);
}
@@ -222,17 +216,17 @@
// check whether node is already locked
Path path = node.getPrimaryPath();
- PathMap.Child child = lockMap.map(path, false);
+ PathMap.Element element = lockMap.map(path, false);
- LockInfo other = (LockInfo) child.get();
+ LockInfo other = (LockInfo) element.get();
if (other != null) {
- if (child.hasPath(path)) {
+ if (element.hasPath(path)) {
throw new LockException("Node already locked: " + node.safeGetJCRPath());
} else if (other.deep) {
throw new LockException("Parent node has deep lock.");
}
}
- if (info.deep && child.hasPath(path)) {
+ if (info.deep && element.hasPath(path)) {
throw new LockException("Some child node is locked.");
}
@@ -273,9 +267,9 @@
new NodeId(info.getUUID()));
Path path = node.getPrimaryPath();
- PathMap.Child child = lockMap.map(path, true);
- if (child != null) {
- child.set(null);
+ PathMap.Element element = lockMap.map(path, true);
+ if (element != null) {
+ element.set(null);
}
// set live flag to false
@@ -317,12 +311,12 @@
Path path = node.getPrimaryPath();
- PathMap.Child child = lockMap.map(path, false);
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = lockMap.map(path, false);
+ LockInfo info = (LockInfo) element.get();
if (info == null) {
throw new LockException("Node not locked: " + node.safeGetJCRPath());
}
- if (child.hasPath(path) || info.deep) {
+ if (element.hasPath(path) || info.deep) {
SessionImpl session = (SessionImpl) node.getSession();
Node lockHolder = (Node) session.getItemManager().getItem(
new NodeId(info.getUUID()));
@@ -341,12 +335,12 @@
// check whether node is locked by this session
Path path = node.getPrimaryPath();
- PathMap.Child child = lockMap.map(path, true);
- if (child == null) {
+ PathMap.Element element = lockMap.map(path, true);
+ if (element == null) {
throw new LockException("Node not locked: " + node.safeGetJCRPath());
}
- LockInfo info = (LockInfo) child.get();
+ LockInfo info = (LockInfo) element.get();
if (info == null) {
throw new LockException("Node not locked: " + node.safeGetJCRPath());
}
@@ -355,7 +349,7 @@
}
// remove lock in path map
- child.set(null);
+ element.set(null);
info.setLive(false);
// remove properties in content
@@ -368,11 +362,11 @@
* {@inheritDoc}
*/
public synchronized boolean holdsLock(NodeImpl node) throws RepositoryException {
- PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
- if (child == null) {
+ PathMap.Element element = lockMap.map(node.getPrimaryPath(), true);
+ if (element == null) {
return false;
}
- return child.get() != null;
+ return element.get() != null;
}
/**
@@ -381,12 +375,12 @@
public synchronized boolean isLocked(NodeImpl node) throws RepositoryException {
Path path = node.getPrimaryPath();
- PathMap.Child child = lockMap.map(path, false);
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = lockMap.map(path, false);
+ LockInfo info = (LockInfo) element.get();
if (info == null) {
return false;
}
- if (child.hasPath(path)) {
+ if (element.hasPath(path)) {
return true;
} else {
return info.deep;
@@ -408,10 +402,10 @@
public void checkLock(Path path, Session session)
throws LockException, RepositoryException {
- PathMap.Child child = lockMap.map(path, false);
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = lockMap.map(path, false);
+ LockInfo info = (LockInfo) element.get();
if (info != null) {
- if (child.hasPath(path) || info.deep) {
+ if (element.hasPath(path) || info.deep) {
if (!session.equals(info.getLockHolder())) {
throw new LockException("Node locked.");
}
@@ -428,9 +422,9 @@
NodeImpl node = (NodeImpl) session.getItemManager().
getItem(new NodeId(lockToken.uuid));
- PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
- if (child != null) {
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = lockMap.map(node.getPrimaryPath(), true);
+ if (element != null) {
+ LockInfo info = (LockInfo) element.get();
if (info != null) {
if (info.getLockHolder() == null) {
info.setLockHolder(session);
@@ -457,9 +451,9 @@
NodeImpl node = (NodeImpl) session.getItemManager().
getItem(new NodeId(lockToken.uuid));
- PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
- if (child != null) {
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = lockMap.map(node.getPrimaryPath(), true);
+ if (element != null) {
+ LockInfo info = (LockInfo) element.get();
if (info != null) {
if (session.equals(info.getLockHolder())) {
info.setLockHolder(null);
@@ -480,84 +474,210 @@
//----------------------------------------------< SynchronousEventListener >
/**
+ * Internal event class that holds old and new paths for moved nodes
+ */
+ private class HierarchyEvent {
+
+ /**
+ * UUID recorded in event
+ */
+ public final String uuid;
+
+ /**
+ * Path recorded in event
+ */
+ public final Path path;
+
+ /**
+ * Old path in move operation
+ */
+ private Path oldPath;
+
+ /**
+ * New path in move operation
+ */
+ private Path newPath;
+
+ /**
+ * Event type, may be {@link Event#NODE_ADDED},
+ * {@link Event#NODE_REMOVED} or a combination of both
+ */
+ private int type;
+
+ /**
+ * Create a new instance of this class.
+ * @param uuid uuid
+ * @param path path
+ * @param type event type
+ */
+ public HierarchyEvent(String uuid, Path path, int type) {
+ this.uuid = uuid;
+ this.path = path;
+ this.type = type;
+ }
+
+ /**
+ * Merge this event with another event. The result will be stored in
+ * this event
+ * @param event other event to merge with
+ */
+ public void merge(HierarchyEvent event) {
+ type |= event.type;
+ if (event.type == Event.NODE_ADDED) {
+ newPath = event.path;
+ oldPath = path;
+ } else {
+ oldPath = event.path;
+ newPath = path;
+ }
+ }
+
+ /**
+ * Return the event type. May be {@link Event#NODE_ADDED},
+ * {@link Event#NODE_REMOVED} or a combination of both.\
+ * @return event type
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Return the old path if this is a move operation
+ * @return old path
+ */
+ public Path getOldPath() {
+ return oldPath;
+ }
+
+ /**
+ * Return the new path if this is a move operation
+ * @return new path
+ */
+ public Path getNewPath() {
+ return newPath;
+ }
+ }
+
+ /**
* {@inheritDoc}
*/
public void onEvent(EventIterator events) {
- while (events.hasNext()) {
- EventImpl event = (EventImpl) events.nextEvent();
- switch (event.getType()) {
+ Iterator iter = consolidateEvents(events);
+ while (iter.hasNext()) {
+ HierarchyEvent event = (HierarchyEvent) iter.next();
+ switch (event.type) {
case Event.NODE_ADDED:
- try {
- nodeAdded(event.getChildUUID(),
- Path.create(event.getPath(), nsResolver, true));
- } catch (MalformedPathException e) {
- log.info("Unable to get event's path: " + e.getMessage());
- } catch (RepositoryException e) {
- log.info("Unable to get event's path: " + e.getMessage());
- }
+ nodeAdded(event.path);
break;
case Event.NODE_REMOVED:
- try {
- nodeRemoved(event.getChildUUID(),
- Path.create(event.getPath(), nsResolver, true));
- } catch (MalformedPathException e) {
- log.info("Unable to get event's path: " + e.getMessage());
- } catch (RepositoryException e) {
- log.info("Unable to get event's path: " + e.getMessage());
- }
+ nodeRemoved(event.path);
+ break;
+ case Event.NODE_ADDED | Event.NODE_REMOVED:
+ nodeMoved(event.getOldPath(), event.getNewPath());
break;
}
}
}
/**
- * Invoked when some node has been added. Relink the child inside our
- * zombie map to the new parent. Revitalize all locks inside the
- * zombie child hierarchy.
+ * Consolidate an event iterator obtained from observation, merging
+ * add and remove operations on nodes with the same UUID into a move
+ * operation.
+ */
+ private Iterator consolidateEvents(EventIterator events) {
+ SequencedHashMap eventMap = new SequencedHashMap();
+
+ while (events.hasNext()) {
+ EventImpl event = (EventImpl) events.nextEvent();
+ HierarchyEvent he;
+
+ try {
+ he = new HierarchyEvent(event.getChildUUID(),
+ Path.create(event.getPath(), nsResolver, true),
+ event.getType());
+ } catch (MalformedPathException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ continue;
+ } catch (RepositoryException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ continue;
+ }
+
+ HierarchyEvent heExisting = (HierarchyEvent) eventMap.get(he.uuid);
+ if (heExisting != null) {
+ heExisting.merge(he);
+ } else {
+ eventMap.put(he.uuid, he);
+ }
+ }
+ return eventMap.values().iterator();
+ }
+
+ /**
+ * Invoked when some node has been added. If the parent of that node
+ * exists, shift all name siblings of the new node having an index greater
+ * or equal.
+ * @param path path of added node
*/
- private synchronized void nodeAdded(String uuid, Path path) {
+ private synchronized void nodeAdded(Path path) {
try {
- PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
+ PathMap.Element parent = lockMap.map(path.getAncestor(1), true);
if (parent != null) {
- parent.insertChild(path.getNameElement());
+ parent.insert(path.getNameElement());
}
- PathMap.Child zombie = (PathMap.Child) zombieNodes.remove(uuid);
- if (zombie != null) {
- zombie.traverse(new PathMap.ChildVisitor() {
- public void childVisited(PathMap.Child child) {
- LockInfo info = (LockInfo) child.get();
- info.setLive(true);
- }
- }, false);
- lockMap.resurrect(path, zombie);
+ } catch (PathNotFoundException e) {
+ log.warn("Unable to determine path of added node's parent.", e);
+ return;
+ }
+ }
+
+ /**
+ * Invoked when some node has been moved. Relink the child inside our
+ * map to the new parent.
+ * @param oldPath old path
+ */
+ private synchronized void nodeMoved(Path oldPath, Path newPath) {
+ PathMap.Element element = lockMap.map(oldPath, true);
+ if (element != null) {
+ element.remove();
+ }
+
+ try {
+ PathMap.Element parent = lockMap.map(newPath.getAncestor(1), true);
+ if (parent != null) {
+ parent.insert(newPath.getNameElement());
+ }
+ if (element != null) {
+ lockMap.put(newPath, element);
}
} catch (PathNotFoundException e) {
- log.warn("Added node does not have parent, ignoring event.");
+ log.warn("Unable to determine path of moved node's parent.", e);
+ return;
}
}
/**
- * Invoked when some node has been removed. Unlink the child inside
- * our path map corresponding to that node. Disable all locks contained
- * in that subtree.
+ * Invoked when some node has been removed. Remove the child from our
+ * path map. Disable all locks contained in that subtree.
+ * @param path path of removed node
*/
- private synchronized void nodeRemoved(String uuid, Path path) {
+ private synchronized void nodeRemoved(Path path) {
try {
- PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
+ PathMap.Element parent = lockMap.map(path.getAncestor(1), true);
if (parent != null) {
- PathMap.Child child = parent.removeChild(path.getNameElement());
- if (child != null) {
- child.traverse(new PathMap.ChildVisitor() {
- public void childVisited(PathMap.Child child) {
- LockInfo info = (LockInfo) child.get();
+ PathMap.Element element = parent.remove(path.getNameElement());
+ if (element != null) {
+ element.traverse(new PathMap.ElementVisitor() {
+ public void elementVisited(PathMap.Element element) {
+ LockInfo info = (LockInfo) element.get();
info.setLive(false);
}
}, false);
- zombieNodes.put(uuid, child);
}
}
} catch (PathNotFoundException e) {
- log.warn("Removed node does not have parent, ignoring event.");
+ log.warn("Unable to determine path of moved node's parent.", e);
+ return;
}
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java?rev=190372&r1=190371&r2=190372&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java Mon Jun 13 01:45:49 2005
@@ -90,7 +90,7 @@
/**
* Listeners (weak references)
*/
- protected final transient Map listeners =
+ private final transient Map listeners =
Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
// the backing persistent item state (may be null)
@@ -127,12 +127,10 @@
/**
* Protected constructor
*
- * @param overlayedState backing persistent item state
* @param initialStatus the initial status of the new <code>ItemState</code> instance
* @param isTransient flag indicating whether this state is transient or not
*/
- protected ItemState(ItemState overlayedState, int initialStatus,
- boolean isTransient) {
+ protected ItemState(int initialStatus, boolean isTransient) {
switch (initialStatus) {
case STATUS_EXISTING:
case STATUS_EXISTING_MODIFIED:
@@ -144,8 +142,6 @@
log.debug(msg);
throw new IllegalArgumentException(msg);
}
- this.overlayedState = overlayedState;
- this.overlayedState.addListener(this);
this.isTransient = isTransient;
}