You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/02/24 15:49:13 UTC
svn commit: r915810 [1/2] - in /jackrabbit/trunk:
jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy...
Author: mduerig
Date: Wed Feb 24 14:49:12 2010
New Revision: 915810
URL: http://svn.apache.org/viewvc?rev=915810&view=rev
Log:
JCR-2498: Implement caching mechanism for ItemInfo batches
Added:
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java (with props)
jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/ItemInfoCache.java (with props)
Modified:
jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java
jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/Spi2davRepositoryServiceFactory.java
jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java
jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Spi2davexRepositoryServiceFactory.java
jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java
jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/Spi2jcrRepositoryServiceFactory.java
Modified: jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java Wed Feb 24 14:49:12 2010
@@ -60,6 +60,7 @@
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.ItemId;
import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.LockInfo;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
@@ -233,6 +234,7 @@
* Dummy RepositoryService
*/
private static final class RepositoryServiceImpl implements RepositoryService {
+
public static final RepositoryService INSTANCE = new RepositoryServiceImpl();
private RepositoryServiceImpl() {
@@ -255,6 +257,10 @@
return null;
}
+ public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+ return null;
+ }
+
public Map<String, QValue[]> getRepositoryDescriptors() throws RepositoryException {
return Collections.emptyMap();
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Wed Feb 24 14:49:12 2010
@@ -98,6 +98,7 @@
import org.apache.jackrabbit.spi.EventFilter;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.LockInfo;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
@@ -166,6 +167,11 @@
*/
private Subscription subscription;
+ /**
+ * A cache for item infos as supplied by {@link RepositoryService#getItemInfoCache(SessionInfo)}
+ */
+ private ItemInfoCache cache;
+
public WorkspaceManager(RepositoryService service, SessionInfo sessionInfo,
CacheBehaviour cacheBehaviour, int pollTimeout,
boolean enableObservation)
@@ -450,8 +456,11 @@
/**
* @return a new instance of <code>TransientItemStateFactory</code>.
*/
- private TransientItemStateFactory createItemStateFactory() {
- WorkspaceItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo, getItemDefinitionProvider());
+ private TransientItemStateFactory createItemStateFactory() throws RepositoryException {
+ cache = service.getItemInfoCache(sessionInfo);
+ WorkspaceItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo,
+ getItemDefinitionProvider(), cache);
+
TransientItemStateFactory tisf = new TransientISFactory(isf, getItemDefinitionProvider());
return tisf;
}
@@ -619,6 +628,7 @@
service.dispose(subscription);
}
service.dispose(sessionInfo);
+ cache.dispose();
} catch (Exception e) {
log.warn("Exception while disposing WorkspaceManager: " + e);
} finally {
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java Wed Feb 24 14:49:12 2010
@@ -98,7 +98,7 @@
if (uniqueId == null) {
throw new IllegalArgumentException();
}
- for (NodeEntryImpl ne : attic) {
+ for (NodeEntry ne : attic) {
if (uniqueId.equals(ne.getUniqueID())) {
return ne;
}
@@ -111,7 +111,7 @@
attic.add(movedEntry);
}
- boolean remove(NodeEntryImpl movedEntry) {
+ boolean remove(NodeEntry movedEntry) {
if (attic.contains(movedEntry)) {
return attic.remove(movedEntry);
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java Wed Feb 24 14:49:12 2010
@@ -166,20 +166,20 @@
* <code>entry</code>. Implementors may choose to delay the actual call to
* {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()} for this
* <code>entry</code> and for any of its child entries. They need to ensure however that
- * {@link #applyPending(NodeEntry)} properly invalidates the respective state when called.
+ * {@link #applyPending(HierarchyEntry)} properly invalidates the respective state when called.
*
- * @param entry The <code>NodeEntry</code> to invalidate.
+ * @param entry The <code>HierarchyEntry</code> to invalidate.
* @param recursive Invalidate state of child entries if <code>true</code>.
*/
- public void invalidate(NodeEntry entry, boolean recursive);
+ public void invalidate(HierarchyEntry entry, boolean recursive);
/**
* Apply any pending {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()
- * invalidation} of the underyling {@link org.apache.jackrabbit.jcr2spi.state.ItemState} of
+ * invalidation} of the underlying {@link org.apache.jackrabbit.jcr2spi.state.ItemState} of
* this <code>entry</code>.
*
* @param entry The affected <code>NodeEntry</code>.
*/
- public void applyPending(NodeEntry entry);
+ public void applyPending(HierarchyEntry entry);
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java Wed Feb 24 14:49:12 2010
@@ -23,6 +23,7 @@
import org.apache.jackrabbit.jcr2spi.operation.Operation;
import org.apache.jackrabbit.jcr2spi.state.ItemState;
import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.Path;
@@ -173,4 +174,11 @@
*/
public void complete(Operation transientOperation) throws RepositoryException;
+ /**
+ * The required generation of this <code>HierarchyEntry</code> . This is used by the
+ * {@link ItemInfoCache} to determine wheter an item info in the cache is up to date or not.
+ * That is whether the generation of the item info in the cache is the same or more recent
+ * as the required generation of this entry.
+ */
+ public long getGeneration();
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -29,6 +29,7 @@
import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
import org.apache.jackrabbit.jcr2spi.state.ItemState.MergeResult;
import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.PathFactory;
@@ -44,6 +45,13 @@
private static Logger log = LoggerFactory.getLogger(HierarchyEntryImpl.class);
/**
+ * The required generation of this entry. This is used by the {@link ItemInfoCache} to determine
+ * wheter an item info in the cache is up to date or not. That is whether the generation of the
+ * item info in the cache is the same or more recent as the required generation of this entry.
+ */
+ public long generation;
+
+ /**
* Cached soft reference to the target ItemState.
*/
private Reference<ItemState> target;
@@ -170,6 +178,24 @@
return state;
}
+ protected EntryFactory.InvalidationStrategy getInvalidationStrategy() {
+ return factory.getInvalidationStrategy();
+ }
+
+ /**
+ * Invalidates the underlying {@link ItemState}. If <code>recursive</code> is
+ * true also invalidates the underlying item states of all child entries.
+ * @param recursive
+ */
+ protected void invalidateInternal(boolean recursive) {
+ ItemState state = internalGetItemState();
+ if (state == null) {
+ log.debug("Skip invalidation for unresolved HierarchyEntry " + name);
+ } else {
+ state.invalidate();
+ }
+ }
+
//-----------------------------------------------------< HierarchyEntry >---
/**
* @see HierarchyEntry#getName()
@@ -260,12 +286,11 @@
* @see HierarchyEntry#invalidate(boolean)
*/
public void invalidate(boolean recursive) {
- ItemState state = internalGetItemState();
- if (state == null) {
- log.debug("Skip invalidation for unresolved HierarchyEntry " + name);
- } else {
- state.invalidate();
- }
+ getInvalidationStrategy().invalidate(this, recursive);
+ }
+
+ public void calculateStatus() {
+ getInvalidationStrategy().applyPending(this);
}
/**
@@ -396,7 +421,13 @@
internalRemove(false);
}
+ public long getGeneration() {
+ calculateStatus();
+ return generation;
+ }
+
//--------------------------------------------------------------------------
+
/**
* @param staleParent
*/
@@ -421,4 +452,109 @@
}
}
}
+
+ // ----------------------------------------------< InvalidationStrategy >---
+ /**
+ * An implementation of <code>InvalidationStrategy</code> which lazily invalidates
+ * the underlying {@link ItemState}s.
+ */
+ static class LazyInvalidation implements EntryFactory.InvalidationStrategy {
+
+ /**
+ * Marker for entries with a pending recursive invalidation.
+ */
+ private static long INVALIDATION_PENDING = -1;
+
+ /**
+ * Number of the current generation
+ */
+ private long currentGeneration;
+
+ /**
+ * Increment for obtaining the next generation from the current generation.
+ */
+ private int nextGeneration;
+
+ /**
+ * A recursive invalidation is being processed if <code>true</code>.
+ * This flag is for preventing re-entrance.
+ */
+ private boolean invalidating;
+
+ /**
+ * Records a pending recursive {@link ItemState#invalidate() invalidation} for
+ * <code>entry</code> if <code>recursive</code> is <code>true</code>. Otherwise
+ * invalidates the entry right away.
+ * {@inheritDoc}
+ */
+ public void invalidate(HierarchyEntry entry, boolean recursive) {
+ HierarchyEntryImpl he = (HierarchyEntryImpl) entry;
+ if (recursive) {
+ he.generation = INVALIDATION_PENDING;
+ if (!invalidating) {
+ nextGeneration = 1;
+ }
+ } else {
+ if (!invalidating) {
+ nextGeneration = 1;
+ }
+ he.invalidateInternal(false);
+ }
+ }
+
+ /**
+ * Checks whether <code>entry</code> itself has a invalidation pending.
+ * If so, the <code>entry</code> is invalidated. Otherwise check
+ * whether an invalidation occurred after the entry has last been
+ * invalidated. If so, search the path to the root for an originator of
+ * the pending invalidation.
+ * If such an originator is found, invalidate each entry on the path.
+ * Otherwise this method does nothing.
+ * {@inheritDoc}
+ */
+ public void applyPending(HierarchyEntry entry) {
+ if (!invalidating) {
+ invalidating = true;
+ currentGeneration += nextGeneration;
+ nextGeneration = 0;
+ try {
+ HierarchyEntryImpl he = (HierarchyEntryImpl) entry;
+ if (he.generation == INVALIDATION_PENDING) {
+ he.invalidateInternal(true);
+ he.generation = currentGeneration;
+ } else if (he.generation < currentGeneration) {
+ resolvePendingInvalidation(he);
+ }
+ } finally {
+ invalidating = false;
+ }
+ }
+ }
+
+ /**
+ * Search the path to the root for an originator of a pending invalidation of
+ * this <code>entry</code>. If such an originator is found, invalidate each
+ * entry on the path. Otherwise do nothing.
+ *
+ * @param entry
+ */
+ private void resolvePendingInvalidation(HierarchyEntryImpl entry) {
+ if (entry != null) {
+
+ // First recursively travel up to the first parent node
+ // which has invalidation pending or to the root node if
+ // no such node exists.
+ if (entry.generation != INVALIDATION_PENDING) {
+ resolvePendingInvalidation(entry.parent);
+ }
+
+ // Then travel the path backwards invalidating as required
+ if (entry.generation == INVALIDATION_PENDING) {
+ entry.invalidateInternal(true);
+ }
+ entry.generation = currentGeneration;
+ }
+ }
+ }
+
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -116,12 +116,6 @@
private RevertInfo revertInfo;
/**
- * Information regarding the invalidation status of the underlying {@link ItemState}
- * of this entry. The semantics depend on the {@link EntryFactory.InvalidationStrategy}.
- */
- private long invalidationStatus;
-
- /**
* Creates a new <code>NodeEntryImpl</code>
*
* @param parent the <code>NodeEntry</code> that owns this child item
@@ -171,15 +165,6 @@
return true;
}
- @Override
- public void invalidate(boolean recursive) {
- getInvalidationStrategy().invalidate(this, recursive);
- }
-
- public void calculateStatus() {
- getInvalidationStrategy().applyPending(this);
- }
-
/**
* If 'recursive' is true, the complete hierarchy below this entry is
* traversed and reloaded. Otherwise only this entry and the direct
@@ -205,7 +190,7 @@
/**
* Calls {@link HierarchyEntryImpl#revert()} and moves all properties from the
- * attic back into th properties map. If this HierarchyEntry has been
+ * attic back into the properties map. If this HierarchyEntry has been
* transiently moved, it is in addition moved back to its old parent.
* Similarly reordering of child node entries is reverted.
*
@@ -885,7 +870,7 @@
}
}
- NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries.remove(this);
+ NodeEntry entry = parent.childNodeEntries.remove(this);
if (entry != this) {
// should never occur
String msg = "Internal error. Attempt to move NodeEntry (" + getName() + ") which is not connected to its parent.";
@@ -1083,7 +1068,7 @@
void internalRemoveChildEntry(HierarchyEntry childEntry) {
if (childEntry.denotesNode()) {
if (childNodeEntries.remove((NodeEntry) childEntry) == null) {
- childNodeAttic.remove((NodeEntryImpl) childEntry);
+ childNodeAttic.remove((NodeEntry) childEntry);
}
} else {
Name propName = childEntry.getName();
@@ -1101,16 +1086,8 @@
}
}
- private EntryFactory.InvalidationStrategy getInvalidationStrategy() {
- return factory.getInvalidationStrategy();
- }
-
- /**
- * Invalidates the underlying {@link ItemState}. If <code>recursive</code> is
- * true also invalidates the underlying item states of all child entries.
- * @param recursive
- */
- private void invalidateInternal(boolean recursive) {
+ @Override
+ protected void invalidateInternal(boolean recursive) {
if (recursive) {
// invalidate all child entries including properties present in the
// attic (removed props shadowed by a new property with the same name).
@@ -1119,7 +1096,7 @@
ce.invalidate(true);
}
}
- super.invalidate(true);
+ super.invalidateInternal(true);
}
/**
@@ -1651,15 +1628,15 @@
private final NodeEntryImpl oldParent;
private final Name oldName;
private final int oldIndex;
- private final NodeEntryImpl oldSuccessor;
- private final NodeEntryImpl oldPredecessor;
+ private final NodeEntry oldSuccessor;
+ private final NodeEntry oldPredecessor;
private RevertInfo() throws InvalidItemStateException, RepositoryException {
this.oldParent = parent;
this.oldName = name;
this.oldIndex = getIndex();
- this.oldSuccessor = (NodeEntryImpl) ((ChildNodeEntriesImpl) parent.childNodeEntries).getNext(NodeEntryImpl.this);
- this.oldPredecessor = (NodeEntryImpl) ((ChildNodeEntriesImpl) parent.childNodeEntries).getPrevious(NodeEntryImpl.this);
+ this.oldSuccessor = ((ChildNodeEntriesImpl) parent.childNodeEntries).getNext(NodeEntryImpl.this);
+ this.oldPredecessor = ((ChildNodeEntriesImpl) parent.childNodeEntries).getPrevious(NodeEntryImpl.this);
}
private boolean isMoved() {
@@ -1668,7 +1645,7 @@
private void dispose(boolean persisted) {
if (!persisted) {
- NodeEntryImpl ne = NodeEntryImpl.this;
+ NodeEntry ne = NodeEntryImpl.this;
ChildNodeEntriesImpl parentCNEs = (ChildNodeEntriesImpl) parent.childNodeEntries;
parentCNEs.reorderAfter(ne, revertInfo.oldPredecessor);
try {
@@ -1684,133 +1661,4 @@
}
}
- // ----------------------------------------------< InvalidationStrategy >---
- /**
- * An implementation of <code>InvalidationStrategy</code> which lazily invalidates
- * the underlying {@link ItemState}s.
- */
- static class LazyInvalidation implements EntryFactory.InvalidationStrategy {
-
- /**
- * Marker for entries with a pending recursive invalidation.
- */
- private static long INVALIDATION_PENDING = -1;
-
- /**
- * Time stamp of the last time a recursive invalidation occurred.
- */
- private long lastInvalidation;
-
- /**
- * A recursive invalidation is being processed if <code>true</code>.
- * This flag is for preventing re-entrance.
- */
- private boolean invalidating;
-
- /**
- * Actual time stamp
- */
- private long timeStamp;
-
- /**
- * @return time stamp used to mark entries
- */
- private long getTimeStamp() {
- return timeStamp++;
- }
-
- /**
- * Records a pending recursive {@link ItemState#invalidate() invalidation} for
- * <code>entry</code> if <code>recursive</code> is <code>true</code>. Otherwise
- * invalidates the entry right away.
- * {@inheritDoc}
- */
- public void invalidate(NodeEntry entry, boolean recursive) {
- if (recursive) {
- ((NodeEntryImpl)entry).invalidationStatus = INVALIDATION_PENDING;
- if (!invalidating) {
- lastInvalidation = getTimeStamp();
- }
- } else {
- ((NodeEntryImpl)entry).invalidateInternal(false);
- }
- }
-
- /**
- * Checks whether <code>entry</code> itself has a invalidation pending.
- * If so, the <code>entry</code> is invalidated. Otherwise check
- * whether an invalidation occurred after the entry has last been
- * invalidated. If so, search the path to the root for an originator of
- * the pending invalidation.
- * If such an originator is found, invalidate each entry on the path.
- * Otherwise this method does nothing.
- * {@inheritDoc}
- */
- public void applyPending(NodeEntry entry) {
- if (!invalidating) {
- invalidating = true;
- try {
- NodeEntryImpl ne = (NodeEntryImpl) entry;
- if (ne.invalidationStatus == INVALIDATION_PENDING) {
- ne.invalidateInternal(true);
- ne.invalidationStatus = getTimeStamp();
- } else if (ne.invalidationStatus <= lastInvalidation) {
- resolvePendingInvalidation(ne);
- }
- } finally {
- invalidating = false;
- }
- }
- }
-
- /**
- * Search the path to the root for an originator of a pending invalidation of
- * this <code>entry</code>. If such an originator is found, invalidate each
- * entry on the path. Otherwise do nothing.
- *
- * @param entry
- */
- private void resolvePendingInvalidation(NodeEntryImpl entry) {
- if (entry != null) {
-
- // First recursively travel up to the first parent node
- // which has invalidation pending or to the root node if
- // no such node exists.
- if (entry.invalidationStatus != INVALIDATION_PENDING) {
- resolvePendingInvalidation(entry.parent);
- }
-
- // Then travel the path backwards invalidating as required
- if (entry.invalidationStatus == INVALIDATION_PENDING) {
- entry.invalidateInternal(true);
- }
- entry.invalidationStatus = getTimeStamp();
- }
- }
- }
-
- /**
- * An implementation of <code>InvalidationStrategy</code> which eagerly invalidates
- * the underlying {@link ItemState}s.
- */
- static class EagerInvalidation implements EntryFactory.InvalidationStrategy {
-
- /**
- * Calls {@link ItemState#invalidate()} for the underlying item state of this
- * <code>entry</code> and - if <code>recursive</code> is <code>true</code> -
- * recursively for all item states of all child entries
- * {@inheritDoc}
- */
- public void invalidate(NodeEntry entry, boolean recursive) {
- ((NodeEntryImpl) entry).invalidateInternal(recursive);
- }
-
- /**
- * Does nothing since invalidation has occurred already.
- * {@inheritDoc}
- */
- public void applyPending(NodeEntry entry) {
- // Empty
- }
- }
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -141,7 +141,4 @@
}
}
- public void calculateStatus() {
- parent.calculateStatus();
- }
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java Wed Feb 24 14:49:12 2010
@@ -137,7 +137,7 @@
break;
case Status.INVALIDATED:
case Status.REMOVED:
- // ignore. operations already have been completed
+ he.invalidate(false);
break;
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Wed Feb 24 14:49:12 2010
@@ -18,11 +18,8 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Set;
import javax.jcr.ItemExistsException;
@@ -37,6 +34,7 @@
import org.apache.jackrabbit.spi.ChildInfo;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.NodeInfo;
@@ -46,6 +44,7 @@
import org.apache.jackrabbit.spi.PropertyInfo;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache.Entry;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,24 +52,24 @@
/**
* <code>WorkspaceItemStateFactory</code>...
*/
-public class WorkspaceItemStateFactory extends AbstractItemStateFactory implements ItemStateFactory {
-
+public class WorkspaceItemStateFactory extends AbstractItemStateFactory {
private static Logger log = LoggerFactory.getLogger(WorkspaceItemStateFactory.class);
private final RepositoryService service;
private final SessionInfo sessionInfo;
private final ItemDefinitionProvider definitionProvider;
+ public final ItemInfoCache cache;
+
public WorkspaceItemStateFactory(RepositoryService service, SessionInfo sessionInfo,
- ItemDefinitionProvider definitionProvider) {
+ ItemDefinitionProvider definitionProvider, ItemInfoCache cache) throws RepositoryException {
+
this.service = service;
this.sessionInfo = sessionInfo;
this.definitionProvider = definitionProvider;
+ this.cache = cache;
}
- /**
- * @see ItemStateFactory#createRootState(NodeEntry)
- */
public NodeState createRootState(NodeEntry entry) throws ItemNotFoundException, RepositoryException {
IdFactory idFactory = service.getIdFactory();
PathFactory pf = service.getPathFactory();
@@ -79,98 +78,182 @@
}
/**
- * Creates the node with information retrieved from the
- * <code>RepositoryService</code>.
- *
- * @see ItemStateFactory#createNodeState(NodeId,NodeEntry)
+ * Creates the node with information retrieved from the <code>RepositoryService</code>.
*/
- public NodeState createNodeState(NodeId nodeId, NodeEntry entry)
- throws ItemNotFoundException, RepositoryException {
- // build new node state from server information
- try {
- Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
- NodeState nodeState = createItemStates(nodeId, infos, entry, false);
+ public NodeState createNodeState(NodeId nodeId, NodeEntry entry) throws ItemNotFoundException,
+ RepositoryException {
- if (nodeState == null) {
- throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo.");
+ try {
+ // Get item info from cache and use it if up to date
+ long generation = entry.getGeneration();
+ Entry<NodeInfo> cached = cache.getNodeInfo(nodeId);
+ NodeInfo info;
+ if (isUpToDate(cached, generation)) {
+ info = cached.info;
+ }
+ else {
+ // otherwise retreive item info from service and cache the whole batch
+ Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
+ info = first(infos, cache, generation);
+ if (info == null) {
+ throw new ItemNotFoundException("NodeId: " + nodeId);
+ }
}
- return nodeState;
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
+
+ assertMatchingPath(info, entry);
+ return createNodeState(info, entry);
+ }
+ catch (PathNotFoundException e) {
+ throw new ItemNotFoundException(e);
}
}
/**
- * @see ItemStateFactory#createDeepNodeState(NodeId,NodeEntry)
+ * Creates the node with information retrieved from the <code>RepositoryService</code>.
+ * Intermediate entries are created as needed.
*/
- public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
+ public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws ItemNotFoundException,
+ RepositoryException {
+
try {
- Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
- return createItemStates(nodeId, infos, anyParent, true);
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
+ // Get item info from cache
+ Iterator<? extends ItemInfo> infos = null;
+ Entry<NodeInfo> cached = cache.getNodeInfo(nodeId);
+ NodeInfo info;
+ if (cached == null) {
+ // or from service if not in cache
+ infos = service.getItemInfos(sessionInfo, nodeId);
+ info = first(infos, null, 0);
+ if (info == null) {
+ throw new ItemNotFoundException("NodeId: " + nodeId);
+ }
+ }
+ else {
+ info = cached.info;
+ }
+
+ // Build the hierarchy entry for the item info
+ HierarchyEntry entry = createHierarchyEntries(info, anyParent);
+ if (entry == null || !entry.denotesNode()) {
+ throw new ItemNotFoundException(
+ "HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+ }
+ else {
+ // Now we can check wheter the item info from the cache is up to date
+ long generation = entry.getGeneration();
+ if (isOutdated(cached, generation)) {
+ // if not, retreive the item info from the service and put the whole batch into the cache
+ infos = service.getItemInfos(sessionInfo, nodeId);
+ info = first(infos, cache, generation);
+ }
+ else if (infos != null) {
+ // Otherwise put the whole batch retreived from the service earlier into the cache
+ cache.put(info, generation);
+ first(infos, cache, generation);
+ }
+
+ assertMatchingPath(info, entry);
+ return createNodeState(info, (NodeEntry) entry);
+ }
+ }
+ catch (PathNotFoundException e) {
+ throw new ItemNotFoundException(e);
}
}
/**
- * Creates the PropertyState with information retrieved from the
- * <code>RepositoryService</code>.
- *
- * @see ItemStateFactory#createPropertyState(PropertyId,PropertyEntry)
+ * Creates the PropertyState with information retrieved from the <code>RepositoryService</code>.
*/
- public PropertyState createPropertyState(PropertyId propertyId,
- PropertyEntry entry)
+ public PropertyState createPropertyState(PropertyId propertyId, PropertyEntry entry)
throws ItemNotFoundException, RepositoryException {
+
try {
- PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
+ // Get item info from cache and use it if up to date
+ Entry<PropertyInfo> cached = cache.getPropertyInfo(propertyId);
+ PropertyInfo info;
+ if (isUpToDate(cached, entry.getGeneration())) {
+ info = cached.info;
+ }
+ else {
+ // otherwise retreive item info from service and cache the whole batch
+ info = service.getPropertyInfo(sessionInfo, propertyId);
+ cache.put(info, entry.getGeneration());
+ }
+
assertMatchingPath(info, entry);
return createPropertyState(info, entry);
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
+ }
+ catch (PathNotFoundException e) {
+ throw new ItemNotFoundException(e);
}
}
/**
- * @see ItemStateFactory#createDeepPropertyState(PropertyId,NodeEntry)
+ * Creates the PropertyState with information retrieved from the <code>RepositoryService</code>.
+ * Intermediate entries are created as needed.
*/
- public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
+ public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent)
+ throws RepositoryException {
+
try {
- PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
- PropertyState propState = createDeepPropertyState(info, anyParent, null);
- assertValidState(propState, info);
- return propState;
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
+ // Get item info from cache
+ Entry<PropertyInfo> cached = cache.getPropertyInfo(propertyId);
+ PropertyInfo info;
+ if (cached == null) {
+ // or from service if not in cache
+ info = service.getPropertyInfo(sessionInfo, propertyId);
+ }
+ else {
+ info = cached.info;
+ }
+
+ // Build the hierarchy entry for the item info
+ HierarchyEntry entry = createHierarchyEntries(info, anyParent);
+
+ if (entry == null || entry.denotesNode()) {
+ throw new ItemNotFoundException(
+ "HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+ }
+ else {
+ // Now we can check wheter the item info from the cache is up to date
+ long generation = entry.getGeneration();
+ if (isOutdated(cached, generation)) {
+ // if not, retreive the item info from the service and put the whole batch into the cache
+ info = service.getPropertyInfo(sessionInfo, propertyId);
+ cache.put(info, generation);
+ }
+
+ assertMatchingPath(info, entry);
+ return createPropertyState(info, (PropertyEntry) entry);
+ }
+
+ }
+ catch (PathNotFoundException e) {
+ throw new ItemNotFoundException(e);
}
}
- /**
- * @see ItemStateFactory#getChildNodeInfos(NodeId)
- * @param nodeId
- */
- public Iterator<ChildInfo> getChildNodeInfos(NodeId nodeId)
- throws ItemNotFoundException, RepositoryException {
+ public Iterator<ChildInfo> getChildNodeInfos(NodeId nodeId) throws ItemNotFoundException,
+ RepositoryException {
+
return service.getChildInfos(sessionInfo, nodeId);
}
- /**
- * @see ItemStateFactory#getNodeReferences(NodeState,org.apache.jackrabbit.spi.Name,boolean)
- */
public Iterator<PropertyId> getNodeReferences(NodeState nodeState, Name propertyName, boolean weak) {
NodeEntry entry = nodeState.getNodeEntry();
- // shortcut
- if (entry.getUniqueID() == null
- || !entry.hasPropertyEntry(NameConstants.JCR_UUID)) {
+
+ // Shortcut
+ if (entry.getUniqueID() == null || !entry.hasPropertyEntry(NameConstants.JCR_UUID)) {
// for sure not referenceable
Set<PropertyId> t = Collections.emptySet();
return t.iterator();
}
- // nodestate has a unique ID and is potentially mix:referenceable
- // => try to retrieve references
+ // Has a unique ID and is potentially mix:referenceable. Try to retrieve references
try {
return service.getReferences(sessionInfo, entry.getWorkspaceId(), propertyName, weak);
- } catch (RepositoryException e) {
+ }
+ catch (RepositoryException e) {
log.debug("Unable to determine references to {}", nodeState);
Set<PropertyId> t = Collections.emptySet();
return t.iterator();
@@ -178,80 +261,48 @@
}
//------------------------------------------------------------< private >---
+
/**
- *
- * @param nodeId
- * @param itemInfos
- * @param entry
- * @param isDeep
- * @return
- * @throws ItemNotFoundException
- * @throws RepositoryException
+ * Returns the first item in the iterator if it exists and denotes a node.
+ * Otherwise returns <code>null</code>. If <code>cache</code> is not
+ * <code>null</code>, caches all items by the given <code>generation</code>.
+ * @param generation
*/
- private synchronized NodeState createItemStates(NodeId nodeId, Iterator<? extends ItemInfo> itemInfos,
- NodeEntry entry, boolean isDeep)
- throws ItemNotFoundException, RepositoryException {
- NodeState nodeState;
- ItemInfos infos = new ItemInfos(itemInfos);
-
- // first entry in the iterator is the originally requested Node.
- NodeInfo first = first(infos);
- if (first == null) {
- throw new ItemNotFoundException("Node with id " + nodeId + " could not be found.");
- }
- else {
- if (isDeep) {
- // for a deep state, the hierarchy entry does not correspond to
- // the given NodeEntry -> retrieve NodeState before executing
- // validation check.
- nodeState = createDeepNodeState(first, entry, infos);
- assertValidState(nodeState, first);
- } else {
- // 'isDeep' == false -> the given NodeEntry must match to the
- // first ItemInfo retrieved from the iterator.
- assertMatchingPath(first, entry);
- nodeState = createNodeState(first, entry);
+ private static NodeInfo first(Iterator<? extends ItemInfo> infos, ItemInfoCache cache, long generation) {
+ ItemInfo first = null;
+ if (infos.hasNext()) {
+ first = infos.next();
+ if (cache != null) {
+ cache.put(first, generation);
}
- }
- // deal with all additional ItemInfos that may be present.
- // Assuming locality of the itemInfos, we keep an estimate of a parent entry.
- // This reduces the part of the hierarchy to traverse. For large batches this
- // optimization results in about 25% speed up.
- NodeEntry approxParentEntry = nodeState.getNodeEntry();
- while (infos.hasNext()) {
- ItemInfo info = infos.next();
- if (info.denotesNode()) {
- approxParentEntry = createDeepNodeState((NodeInfo) info, approxParentEntry, infos).getNodeEntry();
- } else {
- createDeepPropertyState((PropertyInfo) info, approxParentEntry, infos);
+ if (!first.denotesNode()) {
+ first = null;
}
}
- return nodeState;
- }
- private static NodeInfo first(ItemInfos infos) {
- if (infos.hasNext()) {
- ItemInfo first = infos.next();
- if (first.denotesNode()) {
- return (NodeInfo) first;
+ if (cache != null) {
+ while (infos.hasNext()) {
+ cache.put(infos.next(), generation);
}
}
- return null;
+ return (NodeInfo) first;
}
/**
- * Creates the node with information retrieved from <code>info</code>.
+ * Create the node state with the information from <code>info</code>.
*
* @param info the <code>NodeInfo</code> to use to create the <code>NodeState</code>.
- * @param entry
+ * @param entry the hierarchy entry for of this state
* @return the new <code>NodeState</code>.
* @throws ItemNotFoundException
* @throws RepositoryException
*/
- private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException, RepositoryException {
- // make sure the entry has the correct ItemId
+ private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException,
+ RepositoryException {
+
+ // Make sure the entry has the correct ItemId
// this may not be the case, if the hierarchy has not been completely
// resolved yet -> if uniqueID is present, set it on this entry or on
// the appropriate parent entry
@@ -282,7 +333,7 @@
entry.setPropertyEntries(propNames);
} catch (ItemExistsException e) {
// should not get here
- log.warn("Internal error", e);
+ log.error("Internal error", e);
}
// unless the child-info are omitted by the SPI impl -> make sure
@@ -309,15 +360,16 @@
}
/**
- * Creates the property with information retrieved from <code>info</code>.
+ * Create the property state with the information from <code>info</code>.
*
- * @param info the <code>PropertyInfo</code> to use to create the
- * <code>PropertyState</code>.
- * @param entry
+ * @param info the <code>PropertyInfo</code> to use to create the <code>PropertyState</code>.
+ * @param entry the hierarchy entry for of this state
* @return the new <code>PropertyState</code>.
+ * @throws RepositoryException
*/
private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry)
throws RepositoryException {
+
// make sure uuid part of id is correct
String uniqueID = info.getId().getUniqueID();
if (uniqueID != null) {
@@ -349,136 +401,78 @@
}
/**
+ * Create missing hierarchy entries on the path from <code>anyParent</code> to the path
+ * of the <code>itemInfo</code>.
*
* @param info
* @param anyParent
- * @return
+ * @return the hierarchy entry for <code>info</code>
* @throws RepositoryException
*/
- private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent, ItemInfos infos) throws RepositoryException {
- try {
- // node for nodeId exists -> build missing entries in hierarchy
- // Note, that the path contained in NodeId does not reveal which
- // entries are missing -> calculate relative path.
- Path anyParentPath = anyParent.getWorkspacePath();
- Path relPath = anyParentPath.computeRelativePath(info.getPath());
- Path.Element[] missingElems = relPath.getElements();
-
- if (startsWithIllegalElement(missingElems)) {
- log.error("Relative path to NodeEntry starts with illegal element -> ignore NodeInfo with path " + info.getPath());
- return null;
- }
-
- NodeEntry entry = anyParent;
- for (int i = 0; i < missingElems.length; i++) {
- if (missingElems[i].denotesParent()) {
- // Walk up the hierarchy for 'negative' paths
- // until the smallest common root is found
- entry = entry.getParent();
+ private HierarchyEntry createHierarchyEntries(ItemInfo info, NodeEntry anyParent)
+ throws RepositoryException {
+
+ // Calculate relative path of missing entries
+ Path anyParentPath = anyParent.getWorkspacePath();
+ Path relPath = anyParentPath.computeRelativePath(info.getPath());
+ Path.Element[] missingElems = relPath.getElements();
+
+ NodeEntry entry = anyParent;
+ int last = missingElems.length - 1;
+ for (int i = 0; i <= last; i++) {
+ if (missingElems[i].denotesParent()) {
+ // Walk up the hierarchy for 'negative' paths
+ // until the smallest common root is found
+ entry = entry.getParent();
+ }
+ else if (missingElems[i].denotesName()) {
+ // Add missing elements starting from the smallest common root
+ Name name = missingElems[i].getName();
+ int index = missingElems[i].getNormalizedIndex();
+
+ if (i == last && !info.denotesNode()) {
+ return entry.getOrAddPropertyEntry(name);
}
- else if (missingElems[i].denotesName()) {
- // Add missing elements starting from the smallest common root
- Name name = missingElems[i].getName();
- int index = missingElems[i].getNormalizedIndex();
- entry = createIntermediateNodeEntry(entry, name, index, infos);
+ else {
+ entry = createNodeEntry(entry, name, index);
}
- // else denotesCurrent -> ignore
}
-
- return createNodeState(info, entry);
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
}
+ return entry;
}
- /**
- *
- * @param info
- * @param anyParent
- * @return
- * @throws RepositoryException
- */
- private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent, ItemInfos infos) throws RepositoryException {
- try {
- // prop for propertyId exists -> build missing entries in hierarchy
- // Note, that the path contained in PropertyId does not reveal which
- // entries are missing -> calculate relative path.
- Path anyParentPath = anyParent.getWorkspacePath();
- Path relPath = anyParentPath.computeRelativePath(info.getPath());
- Path.Element[] missingElems = relPath.getElements();
-
- // make sure the missing elements don't start with . or .. in which
- // case the info is not within the tree as it is expected
- // (see also JCR-1797)
- if (startsWithIllegalElement(missingElems)) {
- log.error("Relative path to PropertyEntry starts with illegal element -> ignore PropertyInfo with path " + info.getPath());
- return null;
- }
-
- NodeEntry entry = anyParent;
- int i = 0;
- // NodeEntries except for the very last 'missingElem'
- while (i < missingElems.length - 1) {
- if (missingElems[i].denotesParent()) {
- // Walk up the hierarchy for 'negative' paths
- // until the smallest common root is found
- entry = entry.getParent();
- }
- else if (missingElems[i].denotesName()) {
- // Add missing elements starting from the smallest common root
- Name name = missingElems[i].getName();
- int index = missingElems[i].getNormalizedIndex();
- entry = createIntermediateNodeEntry(entry, name, index, infos);
- }
- // else denotesCurrent -> ignore
- i++;
+ private NodeEntry createNodeEntry(NodeEntry parentEntry, Name name, int index) throws RepositoryException {
+ Entry<NodeInfo> cached = cache.getNodeInfo(parentEntry.getWorkspaceId());
+ if (isUpToDate(cached, parentEntry.getGeneration())) {
+ Iterator<ChildInfo> childInfos = cached.info.getChildInfos();
+ if (childInfos != null) {
+ parentEntry.setNodeEntries(childInfos);
}
- // create PropertyEntry for the last element if not existing yet
- Name propName = missingElems[i].getName();
- PropertyEntry propEntry = entry.getOrAddPropertyEntry(propName);
-
- return createPropertyState(info, propEntry);
- } catch (PathNotFoundException e) {
- throw new ItemNotFoundException(e.getMessage());
}
+
+ return parentEntry.getOrAddNodeEntry(name, index, null);
}
/**
- *
- * @param parentEntry
- * @param name
- * @param index
+ * Returns true iff <code>cache</code> is not <code>null</code> and
+ * the cached entry is up to date.
+ * @param cacheEntry
+ * @param generation
* @return
- * @throws RepositoryException
*/
- private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry,
- Name name, int index,
- ItemInfos infos) throws RepositoryException {
- if (infos != null) {
- Iterator<ChildInfo> childInfos = infos.getChildInfos(parentEntry.getWorkspaceId());
- if (childInfos != null) {
- parentEntry.setNodeEntries(childInfos);
- }
- }
- NodeEntry entry = parentEntry.getOrAddNodeEntry(name, index, null);
- return entry;
+ private static boolean isUpToDate(Entry<?> cacheEntry, long generation) {
+ return cacheEntry != null && cacheEntry.generation >= generation;
}
/**
- * Validation check: make sure the state is not null (was really created)
- * and matches with the specified ItemInfo (path).
- *
- * @param state
- * @param info
- * @throws ItemNotFoundException
- * @throws RepositoryException
+ * Returns true iff <code>cache</code> is not <code>null</code> and
+ * the cached entry is not up to date.
+ * @param cacheEntry
+ * @param generation
+ * @return
*/
- private static void assertValidState(ItemState state, ItemInfo info)
- throws ItemNotFoundException, RepositoryException {
- if (state == null) {
- throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
- }
- assertMatchingPath(info, state.getHierarchyEntry());
+ private static boolean isOutdated(Entry<?> cacheEntry, long generation) {
+ return cacheEntry != null && cacheEntry.generation < generation;
}
/**
@@ -488,35 +482,18 @@
*
* @param info
* @param entry
- * @throws ItemNotFoundException
* @throws RepositoryException
*/
- private static void assertMatchingPath(ItemInfo info, HierarchyEntry entry)
- throws ItemNotFoundException, RepositoryException {
+ private static void assertMatchingPath(ItemInfo info, HierarchyEntry entry) throws RepositoryException {
Path infoPath = info.getPath();
- if (!infoPath.equals(entry.getWorkspacePath())) {
+ Path wspPath = entry.getWorkspacePath();
+ if (!infoPath.equals(wspPath)) {
// TODO: handle external move of nodes (parents) identified by uniqueID
- throw new ItemNotFoundException("HierarchyEntry does not belong the given ItemInfo.");
+ throw new ItemNotFoundException("HierarchyEntry " + infoPath + " does not match ItemInfo " + wspPath);
}
}
/**
- * Returns true if the given <code>missingElems</code> start with
- * the root element, in which case the info is not within
- * the tree as it is expected.
- * See also #JCR-1797 for the corresponding enhancement request.
- *
- * @param missingElems
- * @return true if the first element doesn't denote a named element.
- */
- private static boolean startsWithIllegalElement(Path.Element[] missingElems) {
- if (missingElems.length > 0) {
- return missingElems[0].denotesRoot();
- }
- return false;
- }
-
- /**
* @param entry
* @param degree
* @return the ancestor entry at the specified degree.
@@ -529,91 +506,9 @@
degree--;
}
if (degree != 0) {
+ log.error("Parent of degree {} does not exist.", degree);
throw new IllegalArgumentException();
}
return parent;
}
-
- //--------------------------------------------------------------------------
- /**
- * Iterator
- */
- private class ItemInfos implements Iterator<ItemInfo> {
-
- private final List<ItemInfo> prefetchQueue = new ArrayList<ItemInfo>();
- private final Map<NodeId, NodeInfo> nodeInfos = new HashMap<NodeId, NodeInfo>();
- private final Iterator<? extends ItemInfo> infos;
-
- private ItemInfos(Iterator<? extends ItemInfo> infos) {
- super();
- this.infos = infos;
- }
-
- // ------------------------------------------------------< Iterator >---
- /**
- * @see Iterator#hasNext()
- */
- public boolean hasNext() {
- if (!prefetchQueue.isEmpty()) {
- return true;
- } else {
- return prefetch();
- }
- }
-
- /**
- * @see Iterator#next()
- */
- public ItemInfo next() {
- if (prefetchQueue.isEmpty()) {
- throw new NoSuchElementException();
- } else {
- ItemInfo next = prefetchQueue.remove(0);
- if (next instanceof NodeInfo) {
- nodeInfos.remove(((NodeInfo) next).getId());
- }
- return next;
- }
- }
-
- /**
- * @see Iterator#remove()
- */
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- // -------------------------------------------------------< private >---
- /**
- * @param parentId
- * @return The children <code>NodeInfo</code>s for the parent identified
- * by the given <code>parentId</code> or <code>null</code> if the parent
- * has not been read yet, has already been processed (childInfo is up
- * to date) or does not provide child infos.
- */
- private Iterator<ChildInfo> getChildInfos(NodeId parentId) {
- NodeInfo nodeInfo = nodeInfos.get(parentId);
- while (nodeInfo == null && prefetch()) {
- nodeInfo = nodeInfos.get(parentId);
- }
- return nodeInfo == null? null : nodeInfo.getChildInfos();
- }
-
- /**
- * @return <code>true</code> if the next info could be retrieved.
- */
- private boolean prefetch() {
- if (infos.hasNext()) {
- ItemInfo info = infos.next();
- prefetchQueue.add(info);
- if (info.denotesNode()) {
- NodeInfo nodeInfo = (NodeInfo) info;
- nodeInfos.put(nodeInfo.getId(), nodeInfo);
- }
- return true;
- } else {
- return false;
- }
- }
- }
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java Wed Feb 24 14:49:12 2010
@@ -46,6 +46,7 @@
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.ItemId;
import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.LockInfo;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
@@ -78,8 +79,9 @@
public abstract class AbstractJCR2SPITest extends TestCase implements RepositoryService {
private static final String DEFAULT_WSP = "default";
+ private RepositoryService repositoryService;
+
protected ItemInfoStore itemInfoStore;
- protected RepositoryService repositoryService;
protected RepositoryConfig config;
protected Repository repository;
@@ -223,7 +225,7 @@
protected RepositoryConfig getRepositoryConfig() {
return new AbstractRepositoryConfig() {
public RepositoryService getRepositoryService() throws RepositoryException {
- return repositoryService;
+ return AbstractJCR2SPITest.this;
}
};
}
@@ -258,6 +260,9 @@
return repositoryService.getRepositoryDescriptors();
}
+ public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+ return repositoryService.getItemInfoCache(sessionInfo);
+ }
//-----------------------------------< SessionInfo creation and release >---
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java Wed Feb 24 14:49:12 2010
@@ -16,10 +16,10 @@
*/
package org.apache.jackrabbit.jcr2spi;
+import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig;
-import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
/**
* <code>AbstractRepositoryConfig</code>...
@@ -29,6 +29,7 @@
private static Logger log = LoggerFactory.getLogger(AbstractRepositoryConfig.class);
private static final int DEFAULT_ITEM_CACHE_SIZE = 5000;
+ private static final int DEFAULT_INFO_CACHE_SIZE = 5000;
private static final int DEFAULT_POLL_TIMEOUT = 3000; // 3 seconds
public CacheBehaviour getCacheBehaviour() {
@@ -43,4 +44,8 @@
return DEFAULT_POLL_TIMEOUT;
}
+ public int getInfoCacheSize() {
+ return DEFAULT_INFO_CACHE_SIZE;
+ }
+
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java Wed Feb 24 14:49:12 2010
@@ -187,8 +187,7 @@
String uuid = refNode.getUUID();
Node refNode2 = (Node) testSession.getItem(refNode.getPath());
- // TODO: for generic jsr 170 test isSame must be replace by 'Item.isSame'
- assertSame(refNode2, testSession.getNodeByUUID(uuid));
+ assertTrue(refNode2.isSame(testSession.getNodeByUUID(uuid)));
// add some modification
refNode2.addMixin(mixLockable);
@@ -202,9 +201,9 @@
assertItemStatus(refNode2, Status.STALE_DESTROYED);
// the uuid must be transferred to the 'moved' node
Node n = testSession.getNodeByUUID(uuid);
- // TODO: for generic jsr 170 test assertSame must be replace by 'Item.isSame'
- assertSame(n, testSession.getItem(destPath));
- assertSame(refNode2, testSession.getItem(srcPath));
+ assertTrue(n.isSame(testSession.getItem(destPath)));
+ // assertSame(refNode2, testSession.getItem(srcPath));
+ assertTrue(refNode2.isSame(testSession.getItem(srcPath)));
}
public void testExternalRemoval() throws RepositoryException, NotExecutableException {
@@ -225,8 +224,7 @@
assertItemStatus(refNode2, Status.REMOVED);
// the uuid must be transferred to the 'moved' node
Node n = testSession.getNodeByUUID(uuid);
- // TODO: for generic jsr 170 test assertSame must be replace by 'Item.isSame'
- assertSame(n, testSession.getItem(destPath));
+ assertTrue(n.isSame(testSession.getItem(destPath)));
}
public void testExternalRemoval2() throws RepositoryException, NotExecutableException {
@@ -311,10 +309,10 @@
assertItemStatus(childN.getProperty(jcrPrimaryType), Status.NEW);
assertTrue(testSession.itemExists(childNPath));
- assertSame(childN, testSession.getItem(childNPath));
+ assertTrue(childN.isSame(testSession.getItem(childNPath)));
assertTrue(testSession.itemExists(childPPath));
- assertSame(childP, testSession.getItem(childPPath));
+ assertTrue(childP.isSame(testSession.getItem(childPPath)));
testSession.refresh(false);
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java Wed Feb 24 14:49:12 2010
@@ -265,7 +265,7 @@
Property pAgain = (Property) rw.getItem(prop1Path);
// TODO: for generic jsr 170 test: change assert to p.isSame(pAgain)
- assertSame(p, pAgain);
+ assertTrue(p.isSame(pAgain));
assertEquals("string1", p.getString());
} finally {
rw.logout();
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java Wed Feb 24 14:49:12 2010
@@ -83,8 +83,6 @@
Node n = superuser.getNodeByUUID(uuid);
assertTrue("After successful moving a referenceable node node, accessing the node by uuid must return the same node.", n.isSame(moveNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", n == moveNode);
}
/**
@@ -100,7 +98,5 @@
Node n = superuser.getNodeByUUID(uuid);
assertTrue("After successful moving a referenceable node node, accessing the node by uuid must return the same node.", n.isSame(moveNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", n == moveNode);
}
}
\ No newline at end of file
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java Wed Feb 24 14:49:12 2010
@@ -178,8 +178,6 @@
doMove(oldPath, destinationPath);
Item movedItem = superuser.getItem(newPath);
assertTrue("Moved Node must be the same after the move.", movedItem.isSame(moveNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", movedItem == moveNode);
}
/**
@@ -202,8 +200,6 @@
Item movedItem = superuser.getItem(destinationPath);
assertTrue("Moved Node must be the same after the move.", movedItem.isSame(moveNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", movedItem == moveNode);
}
/**
@@ -216,8 +212,6 @@
//move the node
doMove(moveNode.getPath(), destinationPath);
assertTrue("Parent of moved node must be the destination parent node.", moveNode.getParent().isSame(destParentNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", moveNode.getParent() == destParentNode);
}
/**
@@ -232,8 +226,6 @@
superuser.save();
assertTrue("Parent of moved node must be the destination parent node.", moveNode.getParent().isSame(destParentNode));
- // NOTE: implementation specific test
- assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", moveNode.getParent() == destParentNode);
}
/**
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java Wed Feb 24 14:49:12 2010
@@ -93,9 +93,7 @@
int i = 0;
while (it.hasNext()) {
Node child = it.nextNode();
- // TODO: check for sameNess must be replaced by 'isSame' for generic JCR impl
- // assertTrue(child.isSame(children[i]));
- assertSame(child, children[i]);
+ assertTrue(child.isSame(children[i]));
i++;
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java Wed Feb 24 14:49:12 2010
@@ -37,6 +37,7 @@
import org.apache.jackrabbit.jcr2spi.AbstractJCR2SPITest;
import org.apache.jackrabbit.spi.ChildInfo;
import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.NodeInfo;
import org.apache.jackrabbit.spi.PropertyId;
@@ -44,6 +45,7 @@
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.commons.ItemInfoCacheImpl;
import org.apache.jackrabbit.spi.commons.ItemInfoBuilder.NodeInfoBuilder;
import org.apache.jackrabbit.spi.commons.ItemInfoBuilder.PropertyInfoBuilder;
@@ -73,6 +75,12 @@
private static int OP_COUNT = 500;
/**
+ * Size of the item info cache.
+ * @see ItemInfoCache
+ */
+ private static int ITEM_INFO_CACHE_SIZE = 50000;
+
+ /**
* Ratios of the number of items in the whole content tree compared to the number of items
* in a batch of a {@link RepositoryService#getItemInfos(SessionInfo, NodeId)} call.
* The array contains one ratio per run
@@ -92,6 +100,15 @@
private final Random rnd = new Random(12345);
/**
+ * This implementation ovverides the default cache size with the value of
+ * {@value #ITEM_INFO_CACHE_SIZE}
+ */
+ @Override
+ public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+ return new ItemInfoCacheImpl(ITEM_INFO_CACHE_SIZE);
+ }
+
+ /**
* This implementation adds a tree of nodes and properties up to certain {@link #TREE_DEPTH}.
* Each node has {@link #NODE_COUNT} child nodes and {@link #PROPERTY_COUNT} properties.
* {@inheritDoc}
Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java Wed Feb 24 14:49:12 2010
@@ -35,6 +35,7 @@
import org.apache.jackrabbit.commons.cnd.ParseException;
import org.apache.jackrabbit.spi.ItemId;
import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.NodeInfo;
import org.apache.jackrabbit.spi.QValue;
@@ -95,6 +96,7 @@
//---------------------< may be overwritten by subclasses>------------------
+
/**
* Checks whether the <code>workspaceName</code> is valid.
* @param workspaceName name of the workspace to check
@@ -115,6 +117,12 @@
return super.createSessionInfo(credentials, workspaceName == null? defaulWsp : workspaceName);
}
+ // -----------------------------------------------------< cache >---
+
+ public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) {
+ return new ItemInfoCacheImpl();
+ }
+
//-----------------------------< reading >----------------------------------
/**
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java?rev=915810&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java Wed Feb 24 14:49:12 2010
@@ -0,0 +1,148 @@
+/*
+ * 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.spi.commons;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.NodeInfo;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.PropertyInfo;
+import org.apache.jackrabbit.spi.RepositoryService;
+
+/**
+ * This implementation of {@link ItemInfoCache} has a default size of 5000 items.
+ * Item infos are put into the cache after they have been read from the {@link RepositoryService}.
+ * If the cache is full, the oldest item is discared. Reading items removes the from the cache.
+ *
+ * The undrlying idea here is, that {@link ItemInfo}s which are supplied by the
+ * <code>RepositoryService</code> but not immediatly needed are put into the cache to avoid further
+ * round trips to <code>RepositoryService</code>. When they are needed later, they are read
+ * from the cache. There is no need to keep them in this cache after that point since they are
+ * present in the hierarchy from then on.
+ */
+public class ItemInfoCacheImpl implements ItemInfoCache {
+
+ /**
+ * Default size of the cache.
+ */
+ public static final int DEFAULT_CACHE_SIZE = 5000;
+
+ private final int cacheSize;
+ private final LinkedMap entries;
+
+ /**
+ * Create a new instance with the default cache size.
+ * @see #DEFAULT_CACHE_SIZE
+ */
+ public ItemInfoCacheImpl() {
+ this(DEFAULT_CACHE_SIZE);
+ }
+
+ /**
+ * Create a new instance with a given cache size.
+ * @param cacheSize
+ */
+ public ItemInfoCacheImpl(int cacheSize) {
+ super();
+ this.cacheSize = cacheSize;
+ entries = new LinkedMap(cacheSize);
+ }
+
+ /**
+ * This implementation removes the item from the cache
+ * if it is present. Furthermore if the <code>nodeId</code>
+ * id uuid based, and no item is found by the <code>nodeId</code>
+ * a second lookup is done by the path.
+ */
+ public Entry<NodeInfo> getNodeInfo(NodeId nodeId) {
+ Object entry = entries.remove(nodeId);
+ if (entry == null) {
+ entry = entries.remove(nodeId.getPath());
+ }
+
+ return node(entry);
+ }
+
+ /**
+ * This implementation removes the item from the cache
+ * if it is present. Furthermore if the <code>propertyId</code>
+ * id uuid based, and no item is found by the <code>propertyId</code>
+ * a second lookup is done by the path.
+ */
+ public Entry<PropertyInfo> getPropertyInfo(PropertyId propertyId) {
+ Object entry = entries.remove(propertyId);
+ if (entry == null) {
+ entry = entries.remove(propertyId.getPath());
+ }
+
+ return property(entry);
+ }
+
+ /**
+ * This implementation cached the item by its id and if the id
+ * id uuid based but has no path, also by its path.
+ */
+ public void put(ItemInfo info, long generation) {
+ ItemId id = info.getId();
+ Entry<? extends ItemInfo> entry = info.denotesNode()
+ ? new Entry<NodeInfo>((NodeInfo) info, generation)
+ : new Entry<PropertyInfo>((PropertyInfo) info, generation);
+
+ put(id, entry);
+ if (id.getUniqueID() != null && id.getPath() == null) {
+ put(info.getPath(), entry);
+ }
+ }
+
+ public void dispose() {
+ entries.clear();
+ }
+
+ // -----------------------------------------------------< private >---
+
+ @SuppressWarnings("unchecked")
+ private void put(Object key, Entry<? extends ItemInfo> entry) {
+ entries.remove(key);
+ if (entries.size() >= cacheSize) {
+ entries.remove(entries.firstKey()); // xxx AbstractLinkedMap#firstKey() Javadoc is wrong. See COLLECTIONS-353
+ }
+ entries.put(key, entry);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Entry<NodeInfo> node(Object entry) {
+ if (entry != null && ((Entry<? extends ItemInfo>) entry).info.denotesNode()) {
+ return (Entry<NodeInfo>) entry;
+ }
+ else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Entry<PropertyInfo> property(Object entry) {
+ if (entry != null && !((Entry<? extends ItemInfo>) entry).info.denotesNode()) {
+ return (Entry<PropertyInfo>) entry;
+ }
+ else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java Wed Feb 24 14:49:12 2010
@@ -24,14 +24,17 @@
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
-import javax.jcr.nodetype.NodeTypeExistsException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeTypeExistsException;
import org.apache.jackrabbit.spi.Batch;
+import org.apache.jackrabbit.spi.ChildInfo;
import org.apache.jackrabbit.spi.EventBundle;
import org.apache.jackrabbit.spi.EventFilter;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
import org.apache.jackrabbit.spi.LockInfo;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
@@ -42,16 +45,14 @@
import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.spi.PropertyInfo;
import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.QValueFactory;
import org.apache.jackrabbit.spi.QueryInfo;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.SessionInfo;
import org.apache.jackrabbit.spi.Subscription;
-import org.apache.jackrabbit.spi.QNodeTypeDefinition;
-import org.apache.jackrabbit.spi.QValue;
-import org.apache.jackrabbit.spi.ChildInfo;
-import org.apache.jackrabbit.spi.ItemInfo;
/**
* Log wrapper for a {@link RepositoryService}.
@@ -117,6 +118,14 @@
}, "getRepositoryDescriptors()", new Object[]{});
}
+ public ItemInfoCache getItemInfoCache(final SessionInfo sessionInfo) throws RepositoryException {
+ return (ItemInfoCache) execute(new Callable() {
+ public Object call() throws RepositoryException {
+ return service.getItemInfoCache(sessionInfo);
+ }
+ }, "getItemInfoCache(SessionInfo)", new Object[]{sessionInfo});
+ }
+
public SessionInfo obtain(final Credentials credentials, final String workspaceName)
throws RepositoryException {