Index: src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (revision 605226) +++ src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (working copy) @@ -42,6 +42,7 @@ * collector if they are thus rendered weakly reachable. * * + * This implementation of ItemStateCache is thread-safe. */ public class ItemStateReferenceCache implements ItemStateCache, Dumpable { @@ -86,7 +87,7 @@ /** * {@inheritDoc} */ - public boolean isCached(ItemId id) { + public synchronized boolean isCached(ItemId id) { // check primary cache return refs.contains(id); } @@ -94,7 +95,7 @@ /** * {@inheritDoc} */ - public ItemState retrieve(ItemId id) { + public synchronized ItemState retrieve(ItemId id) { // fake call to update stats of secondary cache cache.retrieve(id); @@ -105,7 +106,7 @@ /** * {@inheritDoc} */ - public void cache(ItemState state) { + public synchronized void cache(ItemState state) { ItemId id = state.getId(); if (refs.contains(id)) { log.warn("overwriting cached entry " + id); @@ -120,7 +121,7 @@ /** * {@inheritDoc} */ - public void evict(ItemId id) { + public synchronized void evict(ItemId id) { // fake call to update stats of secondary cache cache.evict(id); // remove from primary cache @@ -130,14 +131,14 @@ /** * {@inheritDoc} */ - public void dispose() { + public synchronized void dispose() { cache.dispose(); } /** * {@inheritDoc} */ - public void evictAll() { + public synchronized void evictAll() { // fake call to update stats of secondary cache cache.evictAll(); // remove all weak references from primary cache @@ -147,7 +148,7 @@ /** * {@inheritDoc} */ - public void update(ItemId id) { + public synchronized void update(ItemId id) { // delegate cache.update(id); } @@ -155,7 +156,7 @@ /** * {@inheritDoc} */ - public boolean isEmpty() { + public synchronized boolean isEmpty() { // check primary cache return refs.isEmpty(); } @@ -163,23 +164,49 @@ /** * {@inheritDoc} */ - public int size() { + public synchronized int size() { // size of primary cache return refs.size(); } /** * {@inheritDoc} + *

+ * The returned Set is not synchronized. The caller must ensure that + * it manually synchronizes access to the Set. Example: + *

+     * ItemStateCache cache = new ItemStateReferenceCache(...);
+     * synchronized (cache) {
+     *     Set keys = cache.keySet();
+     *     for (Iterator it = keys.iterator(); it.hasNext(); ) {
+     *         Object item = it.next();
+     *         // do something with item
+     *     }
+     * }
+     * 
*/ - public Set keySet() { + public synchronized Set keySet() { // keys of primary cache return Collections.unmodifiableSet(refs.keySet()); } /** * {@inheritDoc} + *

+ * The returned collection is not synchronized. The caller must ensure that + * it manually synchronizes access to the collection. Example: + *

+     * ItemStateCache cache = new ItemStateReferenceCache(...);
+     * synchronized (cache) {
+     *     Collection values = cache.values();
+     *     for (Iterator it = values.iterator(); it.hasNext(); ) {
+     *         Object item = it.next();
+     *         // do something with item
+     *     }
+     * }
+     * 
*/ - public Collection values() { + public synchronized Collection values() { // values of primary cache return Collections.unmodifiableCollection(refs.values()); } @@ -188,7 +215,7 @@ /** * {@inheritDoc} */ - public void dump(PrintStream ps) { + public synchronized void dump(PrintStream ps) { ps.println("ItemStateReferenceCache (" + this + ")"); ps.println(); ps.print("[refs] "); Index: src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (revision 605226) +++ src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (working copy) @@ -35,7 +35,7 @@ * cache of weak references to ItemState objects issued by this * ItemStateManager */ - private final ItemStateReferenceCache cache; + private final ItemStateCache cache; /** * Shared item state manager @@ -140,7 +140,7 @@ } // check cache. synchronized to ensure an entry is not created twice. - synchronized (cache) { + synchronized (this) { state = cache.retrieve(id); if (state == null) { // regular behaviour @@ -336,8 +336,11 @@ // JCR-798: copy cached item states to array // to avoid ConcurrentModificationException - ItemState[] isa = (ItemState[]) cache.values().toArray( - new ItemState[cache.size()]); + ItemState[] isa; + synchronized (cache) { + isa = (ItemState[]) cache.values().toArray( + new ItemState[cache.size()]); + } for (int i = 0; i < isa.length; i++) { ItemState state = isa[i]; if (state != null) {