Index: lucene/core/src/java/org/apache/lucene/codecs/Codec.java =================================================================== --- lucene/core/src/java/org/apache/lucene/codecs/Codec.java (revision 1365957) +++ lucene/core/src/java/org/apache/lucene/codecs/Codec.java (working copy) @@ -86,6 +86,21 @@ return loader.availableServices(); } + /** + * Reloads the codec list from the given {@link ClassLoader}. + * Changes to the codecs are visible after the method ends, all + * iterators ({@link #availableCodecs()},...) stay consistent. + * + *

NOTE: Only new codecs are added, existing ones are + * never removed or replaced. + * + *

This method is expensive and should only be called for discovery + * of new codecs on the given classpath/classloader! + */ + public static void reloadCodecs(ClassLoader classloader) { + loader.reload(classloader); + } + private static Codec defaultCodec = Codec.forName("Lucene40"); /** expert: returns the default codec used for newly created Index: lucene/core/src/java/org/apache/lucene/codecs/PostingsFormat.java =================================================================== --- lucene/core/src/java/org/apache/lucene/codecs/PostingsFormat.java (revision 1365957) +++ lucene/core/src/java/org/apache/lucene/codecs/PostingsFormat.java (working copy) @@ -70,4 +70,19 @@ public static Set availablePostingsFormats() { return loader.availableServices(); } + + /** + * Reloads the postings format list from the given {@link ClassLoader}. + * Changes to the postings formats are visible after the method ends, all + * iterators ({@link #availablePostingsFormats()},...) stay consistent. + * + *

NOTE: Only new postings formats are added, existing ones are + * never removed or replaced. + * + *

This method is expensive and should only be called for discovery + * of new postings formats on the given classpath/classloader! + */ + public static void reloadPostingsFormats(ClassLoader classloader) { + loader.reload(classloader); + } } Index: lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java =================================================================== --- lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java (revision 1365957) +++ lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java (working copy) @@ -28,16 +28,34 @@ * Helper class for loading named SPIs from classpath (e.g. Codec, PostingsFormat). * @lucene.internal */ -// TODO: would be nice to have case insensitive lookups. public final class NamedSPILoader implements Iterable { - private final Map services; + private volatile Map services = Collections.emptyMap(); private final Class clazz; public NamedSPILoader(Class clazz) { + this(clazz, Thread.currentThread().getContextClassLoader()); + } + + public NamedSPILoader(Class clazz, ClassLoader classloader) { this.clazz = clazz; - final SPIClassIterator loader = SPIClassIterator.get(clazz); - final LinkedHashMap services = new LinkedHashMap(); + reload(classloader); + } + + /** + * Reloads the internal SPI list from the given {@link ClassLoader}. + * Changes to the service list are visible after the method ends, all + * iterators ({@link #iterator()},...) stay consistent. + * + *

NOTE: Only new service providers are added, existing ones are + * never removed or replaced. + * + *

This method is expensive and should only be called for discovery + * of new service providers on the given classpath/classloader! + */ + public void reload(ClassLoader classloader) { + final LinkedHashMap services = new LinkedHashMap(this.services); + final SPIClassIterator loader = SPIClassIterator.get(clazz, classloader); while (loader.hasNext()) { final Class c = loader.next(); try { Index: solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java =================================================================== --- solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java (revision 1365958) +++ solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java (working copy) @@ -37,6 +37,8 @@ import org.apache.lucene.analysis.util.TokenFilterFactory; import org.apache.lucene.analysis.util.TokenizerFactory; import org.apache.lucene.analysis.util.AnalysisSPILoader; +import org.apache.lucene.codecs.Codec; +import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.util.WeakIdentityMap; import org.apache.solr.common.ResourceLoader; import org.apache.solr.handler.admin.CoreAdminHandler; @@ -112,7 +114,7 @@ this.classLoader = createClassLoader(null, parent); addToClassLoader("./lib/", null); - + // reloadCodecSPI(); -> already done by the line above this.coreProperties = coreProperties; } @@ -143,6 +145,7 @@ void addToClassLoader(final String baseDir, final FileFilter filter) { File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir); this.classLoader = replaceClassLoader(classLoader, base, filter); + reloadCodecSPI(); } /** @@ -163,11 +166,17 @@ return pathname.equals(file); } }); + reloadCodecSPI(); } else { log.error("Can't find (or read) file to add to classloader: " + file); } } + private void reloadCodecSPI() { + Codec.reloadCodecs(this.classLoader); + PostingsFormat.reloadPostingsFormats(this.classLoader); + } + private static URLClassLoader replaceClassLoader(final URLClassLoader oldLoader, final File base, final FileFilter filter) {