LUCENE-2260: Fix AttributeSource to not hold a strong reference to the Attribute/AttributeImpl classes which prevents unloading of custom attributes loaded by other classloaders. The same fix applies to VirtualMethod (3.1 only).

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@909360 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2010-02-12 11:20:35 +00:00
parent 7efc00511e
commit 11f6b9185e
3 changed files with 29 additions and 15 deletions

View File

@ -118,6 +118,11 @@ Bug fixes
termIndexInterval (default 128) * ~2.1 billion = ~274 billion. termIndexInterval (default 128) * ~2.1 billion = ~274 billion.
(Tom Burton-West via Mike McCandless) (Tom Burton-West via Mike McCandless)
* 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)
New features New features
* LUCENE-2128: Parallelized fetching document frequencies during weight * LUCENE-2128: Parallelized fetching document frequencies during weight

View File

@ -17,11 +17,12 @@ package org.apache.lucene.util;
* limitations under the License. * limitations under the License.
*/ */
import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.IdentityHashMap; import java.util.WeakHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -54,8 +55,8 @@ public class AttributeSource {
public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory(); public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory();
private static final class DefaultAttributeFactory extends AttributeFactory { private static final class DefaultAttributeFactory extends AttributeFactory {
private static final IdentityHashMap<Class<? extends Attribute>, Class<? extends AttributeImpl>> attClassImplMap = private static final WeakHashMap<Class<? extends Attribute>, WeakReference<Class<? extends AttributeImpl>>> attClassImplMap =
new IdentityHashMap<Class<? extends Attribute>, Class<? extends AttributeImpl>>(); new WeakHashMap<Class<? extends Attribute>, WeakReference<Class<? extends AttributeImpl>>>();
private DefaultAttributeFactory() {} private DefaultAttributeFactory() {}
@ -72,12 +73,15 @@ public class AttributeSource {
private static Class<? extends AttributeImpl> getClassForInterface(Class<? extends Attribute> attClass) { private static Class<? extends AttributeImpl> getClassForInterface(Class<? extends Attribute> attClass) {
synchronized(attClassImplMap) { synchronized(attClassImplMap) {
Class<? extends AttributeImpl> clazz = attClassImplMap.get(attClass); final WeakReference<Class<? extends AttributeImpl>> ref = attClassImplMap.get(attClass);
Class<? extends AttributeImpl> clazz = (ref == null) ? null : ref.get();
if (clazz == null) { if (clazz == null) {
try { try {
attClassImplMap.put(attClass, attClassImplMap.put(attClass,
clazz = Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader()) new WeakReference<Class<? extends AttributeImpl>>(
.asSubclass(AttributeImpl.class) clazz = Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader())
.asSubclass(AttributeImpl.class)
)
); );
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not find implementing class for " + attClass.getName()); throw new IllegalArgumentException("Could not find implementing class for " + attClass.getName());
@ -173,8 +177,8 @@ public class AttributeSource {
} }
/** a cache that stores all interfaces for known implementation classes for performance (slow reflection) */ /** a cache that stores all interfaces for known implementation classes for performance (slow reflection) */
private static final IdentityHashMap<Class<? extends AttributeImpl>,LinkedList<Class<? extends Attribute>>> knownImplClasses = private static final WeakHashMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>> knownImplClasses =
new IdentityHashMap<Class<? extends AttributeImpl>,LinkedList<Class<? extends Attribute>>>(); new WeakHashMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>>();
/** <b>Expert:</b> Adds a custom AttributeImpl instance with one or more Attribute interfaces. /** <b>Expert:</b> Adds a custom AttributeImpl instance with one or more Attribute interfaces.
* <p><font color="red"><b>Please note:</b> It is not guaranteed, that <code>att</code> is added to * <p><font color="red"><b>Please note:</b> It is not guaranteed, that <code>att</code> is added to
@ -187,18 +191,20 @@ public class AttributeSource {
public void addAttributeImpl(final AttributeImpl att) { public void addAttributeImpl(final AttributeImpl att) {
final Class<? extends AttributeImpl> clazz = att.getClass(); final Class<? extends AttributeImpl> clazz = att.getClass();
if (attributeImpls.containsKey(clazz)) return; if (attributeImpls.containsKey(clazz)) return;
LinkedList<Class<? extends Attribute>> foundInterfaces; LinkedList<WeakReference<Class<? extends Attribute>>> foundInterfaces;
synchronized(knownImplClasses) { synchronized(knownImplClasses) {
foundInterfaces = knownImplClasses.get(clazz); foundInterfaces = knownImplClasses.get(clazz);
if (foundInterfaces == null) { if (foundInterfaces == null) {
knownImplClasses.put(clazz, foundInterfaces = new LinkedList<Class<? extends Attribute>>()); // 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<WeakReference<Class<? extends Attribute>>>());
// find all interfaces that this attribute instance implements // find all interfaces that this attribute instance implements
// and that extend the Attribute interface // and that extend the Attribute interface
Class<?> actClazz = clazz; Class<?> actClazz = clazz;
do { do {
for (Class<?> curInterface : actClazz.getInterfaces()) { for (Class<?> curInterface : actClazz.getInterfaces()) {
if (curInterface != Attribute.class && Attribute.class.isAssignableFrom(curInterface)) { if (curInterface != Attribute.class && Attribute.class.isAssignableFrom(curInterface)) {
foundInterfaces.add(curInterface.asSubclass(Attribute.class)); foundInterfaces.add(new WeakReference<Class<? extends Attribute>>(curInterface.asSubclass(Attribute.class)));
} }
} }
actClazz = actClazz.getSuperclass(); actClazz = actClazz.getSuperclass();
@ -207,7 +213,10 @@ public class AttributeSource {
} }
// add all interfaces of this AttributeImpl to the maps // add all interfaces of this AttributeImpl to the maps
for (Class<? extends Attribute> curInterface : foundInterfaces) { for (WeakReference<Class<? extends Attribute>> curInterfaceRef : foundInterfaces) {
final Class<? extends Attribute> curInterface = 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 // Attribute is a superclass of this interface
if (!attributes.containsKey(curInterface)) { if (!attributes.containsKey(curInterface)) {
// invalidate state to force recomputation in captureState() // invalidate state to force recomputation in captureState()

View File

@ -20,7 +20,7 @@ package org.apache.lucene.util;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.WeakHashMap;
import java.util.Set; import java.util.Set;
/** /**
@ -64,8 +64,8 @@ public final class VirtualMethod<C> {
private final Class<C> baseClass; private final Class<C> baseClass;
private final String method; private final String method;
private final Class<?>[] parameters; private final Class<?>[] parameters;
private final IdentityHashMap<Class<? extends C>, Integer> cache = private final WeakHashMap<Class<? extends C>, Integer> cache =
new IdentityHashMap<Class<? extends C>, Integer>(); new WeakHashMap<Class<? extends C>, Integer>();
/** /**
* Creates a new instance for the given {@code baseClass} and method declaration. * Creates a new instance for the given {@code baseClass} and method declaration.