Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 908654) +++ CHANGES.txt (working copy) @@ -27,12 +27,17 @@ * LUCENE-2158: At high indexing rates, NRT reader could temporarily lose deletions. (Mike McCandless) -API Changes - * LUCENE-2182: DEFAULT_ATTRIBUTE_FACTORY was failing to load implementation class when interface was loaded by a different class loader. (Uwe Schindler, reported on java-user by Ahmed El-dawy) + * LUCENE-2260: Fixed AttributeSource to not hold a strong + reference to the Attribute/AttributeImpl classes which prevents + unloading of custom attributes loaded by other classloaders + (e.g. in Solr plugins). (Uwe Schindler) + +API Changes + * LUCENE-2190: Added setNextReader method to CustomScoreQuery, which is necessary with per-segment searching to notify the subclass which reader the int doc, passed to customScore, refers to. (Paul Index: src/java/org/apache/lucene/util/AttributeSource.java =================================================================== --- src/java/org/apache/lucene/util/AttributeSource.java (revision 908654) +++ src/java/org/apache/lucene/util/AttributeSource.java (working copy) @@ -17,11 +17,12 @@ * limitations under the License. */ +import java.lang.ref.WeakReference; import java.util.Collections; import java.util.NoSuchElementException; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.IdentityHashMap; +import java.util.WeakHashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; @@ -55,7 +56,7 @@ public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory(); private static final class DefaultAttributeFactory extends AttributeFactory { - private static final IdentityHashMap/*,Class>*/ attClassImplMap = new IdentityHashMap(); + private static final WeakHashMap/*, WeakReference>>*/ attClassImplMap = new WeakHashMap(); private DefaultAttributeFactory() {} @@ -71,12 +72,13 @@ private static Class getClassForInterface(Class attClass) { synchronized(attClassImplMap) { - Class clazz = (Class) attClassImplMap.get(attClass); + final WeakReference ref = (WeakReference) attClassImplMap.get(attClass); + Class clazz = (ref == null) ? null : ((Class) ref.get()); if (clazz == null) { try { - attClassImplMap.put(attClass, + attClassImplMap.put(attClass, new WeakReference( clazz = Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader()) - ); + )); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Could not find implementing class for " + attClass.getName()); } @@ -173,7 +175,8 @@ } /** a cache that stores all interfaces for known implementation classes for performance (slow reflection) */ - private static final IdentityHashMap/*,LinkedList>>*/ knownImplClasses = new IdentityHashMap(); + private static final WeakHashMap/*,LinkedList>>>*/ + knownImplClasses = new WeakHashMap(); /** Adds a custom AttributeImpl instance with one or more Attribute interfaces. */ public void addAttributeImpl(final AttributeImpl att) { @@ -183,6 +186,8 @@ synchronized(knownImplClasses) { foundInterfaces = (LinkedList) knownImplClasses.get(clazz); if (foundInterfaces == null) { + // we have a strong reference to the class instance holding all interfaces in the list (parameter "att"), + // so all WeakReferences are never evicted by GC knownImplClasses.put(clazz, foundInterfaces=new LinkedList()); // find all interfaces that this attribute instance implements // and that extend the Attribute interface @@ -192,7 +197,7 @@ for (int i = 0; i < interfaces.length; i++) { final Class curInterface = interfaces[i]; if (curInterface != Attribute.class && Attribute.class.isAssignableFrom(curInterface)) { - foundInterfaces.add(curInterface); + foundInterfaces.add(new WeakReference(curInterface)); } } actClazz = actClazz.getSuperclass(); @@ -202,7 +207,10 @@ // add all interfaces of this AttributeImpl to the maps for (Iterator it = foundInterfaces.iterator(); it.hasNext(); ) { - final Class curInterface = (Class) it.next(); + final WeakReference curInterfaceRef = (WeakReference) it.next(); + final Class curInterface = (Class) curInterfaceRef.get(); + assert (curInterface != null) : + "We have a strong reference on the class holding the interfaces, so they should never get evicted"; // Attribute is a superclass of this interface if (!attributes.containsKey(curInterface)) { // invalidate state to force recomputation in captureState()