Fix BEASTClassLoader dual-loader class identity split#66
Merged
Conversation
When beast-base is reachable via both the JPMS module path and the classpath in the same JVM (a common surefire/IDE configuration), `BEASTClassLoader.addServices(...)` and `addService(...)` registered every provider class against `fallbackClassLoader()` (the thread context = app class-loader), even when the class actually lived in a named boot-layer module owned by a different loader. The result: `BEASTClassLoader.forName(...)` returned the app-loader copy of (e.g.) `beast.base.evolution.tree.Node`, while caller code compiled against `import beast.base...` resolved to the module-loader copy. The two `Class<?>` instances were incompatible, producing ClassCastException and an apparently-empty data-type registry. Add `resolveLoaderFor(provider)`: walk the boot layer's modules and prefer the loader of the module that owns the provider's package. Fall back to `fallbackClassLoader()` only when no named module owns the package (genuine classpath-only providers). This makes the version.xml registration path consistent with the JPMS `provides` registration path (mergeAllProviders / collectProviders), which already used the module's class-loader.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #65.
Summary
resolveLoaderFor(provider)toBEASTClassLoader: prefer the class-loader of the boot-layer module that owns the provider's package; fall back to the thread-context loader only when no named module claims the package.addServices(String, Map)and staticaddService(String, String, String)instead of unconditionalfallbackClassLoader().This brings the version.xml registration path in line with the JPMS
providesregistration path (mergeAllProviders/collectProviders), which already usedm.getClassLoader()from the providing module.Why
When
beast-baseis reachable via both the JPMS module path and the classpath in the same JVM (typical of surefire and IDE runs), the old code stored the app class-loader against every version.xml provider.BEASTClassLoader.forName()then returned the app-loader copy of (e.g.)beast.base.evolution.tree.Node, while application code resolved through the module-loader copy. The twoClass<?>instances were incompatible, producingClassCastExceptionand an empty data-type registry. See #65 for the full diagnosis and reproduction.Test plan
mvn -pl beast-pkgmgmt test— 40/40 pass with the patch.LPhyBeast/pom.xmlchange<beast.version>2.8.0-beta4</beast.version>to<beast.version>2.8.0-SNAPSHOT</beast.version>and runmvn -pl lphybeast test. The previously-failingH5N1TutorialTest.testDPGand the twoLPhyScriptsToBEASTTestcases should pass.