diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index e708888739c..1f53d682f98 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -133,6 +133,12 @@ Other * LUCENE-7719: Generalized the UnifiedHighlighter's support for AutomatonQuery for character & binary automata. Added AutomatonQuery.isBinary. (David Smiley) +* LUCENE-7873: Due to serious problems with context class loaders in several + frameworks (OSGI, Java 9 Jigsaw), the lookup of Codecs, PostingsFormats, + DocValuesFormats and all analysis factories was changed to only inspect the + current classloader that defined the interface class (lucene-core.jar). + See MIGRATE.txt for more information! (Uwe Schindler, Dawid Weiss) + ======================= Lucene 6.7.0 ======================= New Features diff --git a/lucene/MIGRATE.txt b/lucene/MIGRATE.txt index 89b2d7623a4..117b49c7999 100644 --- a/lucene/MIGRATE.txt +++ b/lucene/MIGRATE.txt @@ -1,5 +1,46 @@ # Apache Lucene Migration Guide +## Changed SPI lookups for codecs and analysis changed (LUCENE-7873) ## + +Due to serious problems with context class loaders in several frameworks +(OSGI, Java 9 Jigsaw), the lookup of Codecs, PostingsFormats, DocValuesFormats +and all analysis factories was changed to only inspect the current classloader +that defined the interface class (`lucene-core.jar`). Normal applications +should not encounter any issues with that change, because the application +classloader (unnamed module in Java 9) can load all SPIs from all JARs +from classpath. + +For any code that relies on the old behaviour (e.g., certain web applications +or components in application servers) one can manually instruct the Lucene +SPI implementation to also inspect the context classloader. To do this, +add this code to the early startup phase of your application before any +Apache Lucene component is used: + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + // Codecs: + PostingsFormat.reloadPostingsFormats(cl); + DocValuesFormat.reloadDocValuesFormats(cl); + Codec.reloadCodecs(cl); + // Analysis: + CharFilterFactory.reloadCharFilters(cl); + TokenFilterFactory.reloadTokenFilters(cl); + TokenizerFactory.reloadTokenizers(cl); + +This code will reload all service providers from the given class loader +(in our case the context class loader). Of course, instead of specifying +the context class loader, it is receommended to use the application's main +class loader or the module class loader. + +If you are migrating your project to Java 9 Jigsaw module system, keep in mind +that Lucene currently does not yet support `module-info.java` declarations of +service provider impls (`provides` statement). It is therefore recommended +to keep all of Lucene in one Uber-Module and not try to split Lucene into +several modules. As soon as Lucene will migrate to Java 9 as minimum requirement, +we will work on improving that. + +For OSGI, the same applies. You have to create a bundle with all of Lucene for +SPI to work correctly. + ## Query.hashCode and Query.equals are now abstract methods (LUCENE-7277) Any custom query subclasses should redeclare equivalence relationship according diff --git a/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java b/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java index 8dacf634233..13f5028e9ec 100644 --- a/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java +++ b/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java @@ -48,7 +48,7 @@ public final class AnalysisSPILoader { } public AnalysisSPILoader(Class clazz, String[] suffixes) { - this(clazz, suffixes, Thread.currentThread().getContextClassLoader()); + this(clazz, suffixes, null); } public AnalysisSPILoader(Class clazz, String[] suffixes, ClassLoader classloader) { diff --git a/lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java b/lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java index b882ec7c87e..56ef574c87e 100644 --- a/lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java +++ b/lucene/core/src/java/org/apache/lucene/util/NamedSPILoader.java @@ -35,7 +35,7 @@ public final class NamedSPILoader implements private final Class clazz; public NamedSPILoader(Class clazz) { - this(clazz, Thread.currentThread().getContextClassLoader()); + this(clazz, null); } public NamedSPILoader(Class clazz, ClassLoader classloader) { diff --git a/lucene/core/src/java/org/apache/lucene/util/SPIClassIterator.java b/lucene/core/src/java/org/apache/lucene/util/SPIClassIterator.java index 79a9573a456..6c26397c801 100644 --- a/lucene/core/src/java/org/apache/lucene/util/SPIClassIterator.java +++ b/lucene/core/src/java/org/apache/lucene/util/SPIClassIterator.java @@ -48,13 +48,11 @@ public final class SPIClassIterator implements Iterator> { private final Enumeration profilesEnum; private Iterator linesIterator; - /** Creates a new SPI iterator to lookup services of type {@code clazz} using the context classloader. */ + /** Creates a new SPI iterator to lookup services of type {@code clazz} using + * the same {@link ClassLoader} as the argument. */ public static SPIClassIterator get(Class clazz) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) { - cl = clazz.getClassLoader(); - } - return new SPIClassIterator<>(clazz, cl); + return new SPIClassIterator<>(clazz, + Objects.requireNonNull(clazz.getClassLoader(), () -> clazz + " has no classloader.")); } /** Creates a new SPI iterator to lookup services of type {@code clazz} using the given classloader. */