[
https://issues.apache.org/jira/browse/LUCENE-7870?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dawid Weiss resolved LUCENE-7870.
---------------------------------
Resolution: Invalid
I mark this as invalid then; I think we've reached the consensus that it's not
a bug, rather a clash of two different unfortunate loopholes.
> Use cl.loadClass(...) instead of Class.forName(..., cl) in SPIClassIterator
> ---------------------------------------------------------------------------
>
> Key: LUCENE-7870
> URL: https://issues.apache.org/jira/browse/LUCENE-7870
> Project: Lucene - Core
> Issue Type: Bug
> Affects Versions: 5.2.1, 6.1
> Environment: Eclipse Equinox 4.7
> Reporter: Andreas Sewe
>
> This issue is initially described in [Eclipse Bug
> 517935|https://bugs.eclipse.org/bugs/show_bug.cgi?id=517935] and prevents
> multiple versions of Lucene Core coexisting in an Equinox environment (FYI,
> Equinox is the OSGi container used by the Eclipse IDE).
> Here’s how the problem manifests: While Equinox cleanly separates two
> versions of the same Lucene Core bundle (e.g.,
> {{org.apache.lucene.core_5.2.1}} and {{org.apache.lucene.core_6.1.0}}) using
> two different bundle class loaders, it uses a single context classloader for
> all threads: the
> [{{ContextFinder}}|https://wiki.eclipse.org/Context_Class_Loader_Enhancements#Context_Finder_2].
> When asked to load a class, the {{ContextFinder}} *initiates* a search
> through all bundle class loaders currently “on“ the call stack; the class to
> be loaded is then *defined* by the respective bundle’s class loader.
> And here is where the use of {{Class.forName(..., classLoader)}} rather than
> {{classLoader.loadClass(...)}} causes problems. Consider the following
> sequence of events:
> # The {{NamedSPILoader}} of bundle {{o.a.l.core_5.2.1}} (re)loads some
> service (e.g., the {{Lucene50PostingFormat}}).
> ## It (through {{SPIClassIterator}}) first uses {{Class.forName}} with the
> bundle class loader of {{o.a.l.core_5.2.1}} (as per LUCENE-4713) to
> successfully load {{Lucene50PostingFormat}} from the {{o.a.l.core_5.2.1}}
> bundle.
> ## Then (through another {{SPIClassIterator}}) it uses {{Class.forName}} with
> the thread’s context class loader (here: {{ContextFinder}}) to load
> {{Lucene50PostingFormat}}. The {{ContextFinder}} delegates loading to the
> {{o.a.l.core_5.2.1}} bundle’s class loader, as that bundle is topmost on the
> call stack. This bundle class loader (again) successfully loads
> {{Lucene50PostingFormat}} from the {{o.a.l.core_5.2.1}} bundle.
> # Later, the {{NamedSPILoader}} of *another* bundle {{o.a.l.core_6.1.0}}
> loads the {{Lucene50PostingFormat}} service.
> ## It (through {{SPIClassIterator}}) first uses {{Class.forName}} with the
> bundle class loader of {{o.a.l.core_6.1.0}} to successfully load
> {{Lucene50PostingFormat}} from the {{o.a.l.core_6.1.0}} bundle.
> ## Then (through another {{SPIClassIterator}}) it uses {{Class.forName}} with
> the thread’s context class loader (the same {{ContextFinder}} again) to load
> {{Lucene50PostingFormat}}. As {{Class.forName}} remembers that the
> {{ContextFinder}} has successfully initiated the loading of
> {{Lucene50PostingFormat}} before, it simply returns the {{Class}} instance
> defined earlier in step _1.2_. But that class was defined by a different
> bundle class loader, namely that of {{o.a.l.core_5.2.1}}! This cache look up
> happens in native code; the {{ContextFinder}}‘s {{loadClass}} method isn’t
> even called, so there’s no way it can load the class from the
> {{o.a.l.core_6.1.0}} bundle, even though it now is topmost on the stack.
> ## Casting the {{Lucene50PostingFormat}} loading from bundle
> {{o.a.l.core_5.2.1}} to {{PostingFormat}} from bundle {{o.a.l.core_6.1.0}}
> then fails, leaving the {{o.a.l.core_6.1.0}} bundle in a broken state.
> It {{SPIClassIterator.next()}} would use {{classLoader.loadClass(...)}}
> rather than {{Class.forName(..., classLoader}}), then class loading in step
> _1.2_ wouldn’t *lock in* the {{Lucene50PostingFormat}} class from
> {{org.apache.lucene.core_5.2.1}}; instead, step _2.2_ would perform a
> completely independent look up that retrieves the class from the correct
> bundle. The cast in step _2.3_ would then succeed.
> At Eclipse Orbit, we plan to distribute a [patched
> version|https://git.eclipse.org/r/98804/] of Lucene Core, but obviously we
> would like to see the (one-line) change included in the upstream project.
> BTW, if you fear that bypassing {{Class.forName}} “caching” affects
> performance, then there’s no need to worry: Most {{ClassLoader}}
> implementations cache as well ({{findLoadedClass}}); it’s only
> {{ContextFinder}} that [overrides {{loadClass}}
> wholesale|https://git.eclipse.org/c/equinox/rt.equinox.framework.git/tree/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/ContextFinder.java?h=R4_7_maintenance],
> as it dynamically (based on the current call stack) delegates to the
> (caching) bundle class loaders.
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]