From fa10b01da0b923ef71795d16f97826c0e604ee20 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Wed, 24 Aug 2022 23:10:36 +0200 Subject: [PATCH] Fix classloading deadlock in analysis factories / AnalysisSPILoader initialization. This closes #11701 --- lucene/CHANGES.txt | 3 ++ .../lucene/analysis/CharFilterFactory.java | 29 +++++++++++++++---- .../lucene/analysis/TokenFilterFactory.java | 29 +++++++++++++++---- .../lucene/analysis/TokenizerFactory.java | 29 +++++++++++++++---- 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 565f01bc624..8b2935f9986 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -124,6 +124,9 @@ Bug Fixes * LUCENE-10644: Facets#getAllChildren testing should ignore child order. (Yuting Gan) +* LUCENE-10665, GITHUB#11701: Fix classloading deadlock in analysis factories / AnalysisSPILoader + initialization. (Uwe Schindler) + Build --------------------- diff --git a/lucene/core/src/java/org/apache/lucene/analysis/CharFilterFactory.java b/lucene/core/src/java/org/apache/lucene/analysis/CharFilterFactory.java index 3dddd0dde07..a03bd09a097 100644 --- a/lucene/core/src/java/org/apache/lucene/analysis/CharFilterFactory.java +++ b/lucene/core/src/java/org/apache/lucene/analysis/CharFilterFactory.java @@ -27,22 +27,39 @@ */ public abstract class CharFilterFactory extends AbstractAnalysisFactory { - private static final AnalysisSPILoader loader = - new AnalysisSPILoader<>(CharFilterFactory.class); + /** + * This static holder class prevents classloading deadlock by delaying init of factories until + * needed. + */ + private static final class Holder { + private static final AnalysisSPILoader LOADER = + new AnalysisSPILoader<>(CharFilterFactory.class); + + private Holder() {} + + static AnalysisSPILoader getLoader() { + if (LOADER == null) { + throw new IllegalStateException( + "You tried to lookup a CharFilterFactory by name before all factories could be initialized. " + + "This likely happens if you call CharFilterFactory#forName from a CharFilterFactory's ctor."); + } + return LOADER; + } + } /** looks up a charfilter by name from context classpath */ public static CharFilterFactory forName(String name, Map args) { - return loader.newInstance(name, args); + return Holder.getLoader().newInstance(name, args); } /** looks up a charfilter class by name from context classpath */ public static Class lookupClass(String name) { - return loader.lookupClass(name); + return Holder.getLoader().lookupClass(name); } /** returns a list of all available charfilter names */ public static Set availableCharFilters() { - return loader.availableServices(); + return Holder.getLoader().availableServices(); } /** looks up a SPI name for the specified char filter factory */ @@ -65,7 +82,7 @@ public static String findSPIName(Class serviceClass * given classpath/classloader! */ public static void reloadCharFilters(ClassLoader classloader) { - loader.reload(classloader); + Holder.getLoader().reload(classloader); } /** Default ctor for compatibility with SPI */ diff --git a/lucene/core/src/java/org/apache/lucene/analysis/TokenFilterFactory.java b/lucene/core/src/java/org/apache/lucene/analysis/TokenFilterFactory.java index 57cbbfee9ab..9f692302baa 100644 --- a/lucene/core/src/java/org/apache/lucene/analysis/TokenFilterFactory.java +++ b/lucene/core/src/java/org/apache/lucene/analysis/TokenFilterFactory.java @@ -27,22 +27,39 @@ */ public abstract class TokenFilterFactory extends AbstractAnalysisFactory { - private static final AnalysisSPILoader loader = - new AnalysisSPILoader<>(TokenFilterFactory.class); + /** + * This static holder class prevents classloading deadlock by delaying init of factories until + * needed. + */ + private static final class Holder { + private static final AnalysisSPILoader LOADER = + new AnalysisSPILoader<>(TokenFilterFactory.class); + + private Holder() {} + + static AnalysisSPILoader getLoader() { + if (LOADER == null) { + throw new IllegalStateException( + "You tried to lookup a TokenFilterFactory by name before all factories could be initialized. " + + "This likely happens if you call TokenFilterFactory#forName from a TokenFilterFactory's ctor."); + } + return LOADER; + } + } /** looks up a tokenfilter by name from context classpath */ public static TokenFilterFactory forName(String name, Map args) { - return loader.newInstance(name, args); + return Holder.getLoader().newInstance(name, args); } /** looks up a tokenfilter class by name from context classpath */ public static Class lookupClass(String name) { - return loader.lookupClass(name); + return Holder.getLoader().lookupClass(name); } /** returns a list of all available tokenfilter names from context classpath */ public static Set availableTokenFilters() { - return loader.availableServices(); + return Holder.getLoader().availableServices(); } /** looks up a SPI name for the specified token filter factory */ @@ -65,7 +82,7 @@ public static String findSPIName(Class serviceClas * given classpath/classloader! */ public static void reloadTokenFilters(ClassLoader classloader) { - loader.reload(classloader); + Holder.getLoader().reload(classloader); } /** Default ctor for compatibility with SPI */ diff --git a/lucene/core/src/java/org/apache/lucene/analysis/TokenizerFactory.java b/lucene/core/src/java/org/apache/lucene/analysis/TokenizerFactory.java index 30115a1cd36..7cfc7b38108 100644 --- a/lucene/core/src/java/org/apache/lucene/analysis/TokenizerFactory.java +++ b/lucene/core/src/java/org/apache/lucene/analysis/TokenizerFactory.java @@ -27,22 +27,39 @@ */ public abstract class TokenizerFactory extends AbstractAnalysisFactory { - private static final AnalysisSPILoader loader = - new AnalysisSPILoader<>(TokenizerFactory.class); + /** + * This static holder class prevents classloading deadlock by delaying init of factories until + * needed. + */ + private static final class Holder { + private static final AnalysisSPILoader LOADER = + new AnalysisSPILoader<>(TokenizerFactory.class); + + private Holder() {} + + static AnalysisSPILoader getLoader() { + if (LOADER == null) { + throw new IllegalStateException( + "You tried to lookup a TokenizerFactory by name before all factories could be initialized. " + + "This likely happens if you call TokenizerFactory#forName from a TokenizerFactory ctor."); + } + return LOADER; + } + } /** looks up a tokenizer by name from context classpath */ public static TokenizerFactory forName(String name, Map args) { - return loader.newInstance(name, args); + return Holder.getLoader().newInstance(name, args); } /** looks up a tokenizer class by name from context classpath */ public static Class lookupClass(String name) { - return loader.lookupClass(name); + return Holder.getLoader().lookupClass(name); } /** returns a list of all available tokenizer names from context classpath */ public static Set availableTokenizers() { - return loader.availableServices(); + return Holder.getLoader().availableServices(); } /** looks up a SPI name for the specified tokenizer factory */ @@ -65,7 +82,7 @@ public static String findSPIName(Class serviceClass) * given classpath/classloader! */ public static void reloadTokenizers(ClassLoader classloader) { - loader.reload(classloader); + Holder.getLoader().reload(classloader); } /** Default ctor for compatibility with SPI */