Index: BindableRepositoryFactory.java =================================================================== --- BindableRepositoryFactory.java (revision 900832) +++ BindableRepositoryFactory.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.core.jndi; +import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.map.ReferenceMap; import javax.jcr.RepositoryException; @@ -24,35 +25,77 @@ import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; +import java.util.Set; /** - * BindableRepositoryFactory is an object factory that when given - * a reference for a BindableRepository object, will create an - * instance of the corresponding BindableRepository. + * BindableRepositoryFactory is an object factory that when given a reference for a + * BindableRepository object, will create an instance of the corresponding BindableRepository. */ public class BindableRepositoryFactory implements ObjectFactory { /** - * cache using java.naming.Reference objects as keys and - * storing soft references to BindableRepository instances + * cache using java.naming.Reference objects as keys and storing soft references to + * BindableRepository instances */ - private static final Map cache = new ReferenceMap(); + static final Map cache = new ReferenceMap(); /** * {@inheritDoc} */ - public synchronized Object getObjectInstance( - Object obj, Name name, Context nameCtx, Hashtable environment) + public synchronized Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws RepositoryException { synchronized (cache) { Object instance = cache.get(obj); if (instance == null && obj instanceof Reference) { - instance = new BindableRepository((Reference) obj); + instance = new BindableRepository((Reference) obj) { + private static final long serialVersionUID = -8609106126568988192L; + + public void shutdown() { + super.shutdown(); + /* + * If the repository gets shutdown, it must be removed from cache, because it can not be used + * anymore. Therefore we override this method to invalidate the cache. Maybe a + * RepositoryStateListener should be introduced to allow other components to react on repository + * state changes. + */ + invalidateCacheObject(this); + } + }; cache.put(obj, instance); } return instance; } } + /** + * Invalidates the given object in this factory's cache. If this factory's cache does not contain the given object + * it returns false, true if the object has been successfully invalidated. + * + * @param cachedObject + * the object that should be invalidated. + * @return true, if the cachedObject has been invalidated successfully, false otherwise. + */ + private boolean invalidateCacheObject(Object cachedObject) { + synchronized (cache) { + Map invertedCacheMap = MapUtils.invertMap(cache); + Object cachedObjectKey = invertedCacheMap.get(cachedObject); + try { + return cache.remove(cachedObjectKey) != null; + } catch (NullPointerException e) { + /** + * As told by the java.util.Map#remove(Object) API documentation it is implementation-specific if null + * keys are allowed or not. Therefore a NullPointerException may occur if the cache implementation does + * not support null values and the give cachedObject is null. Nevertheless we must be prepared that a + * cacheObject is mapped under a null value. + * + * The cachedObjectKey can be null under two circumstances: + * 1. the cache does not contain the given cachedObject, therefore the cachedObjectKey is null. + * 2. the cache implementation allows null keys and the cachedObject is mapped via a null key. + */ + return false; + } + } + } }