Skip to content

Load BEAUti templates from the class path, not only module layers - #123

Merged
walterxie merged 1 commit into
masterfrom
fix/classpath-template-loading
Jul 26, 2026
Merged

Load BEAUti templates from the class path, not only module layers#123
walterxie merged 1 commit into
masterfrom
fix/classpath-template-loading

Conversation

@alexeid

@alexeid alexeid commented Jul 24, 2026

Copy link
Copy Markdown
Member

Problem

BeautiDoc discovers fxtemplates/*.xml only inside resolved module layers:

  • processTemplateloadResourceFromModules scans ModuleLayer.boot() + plugin layers for the main template (Standard.xml).
  • getTemplateSources enumerates the merge templates via BEASTClassLoader.listResources, which scans the same layers.

That's correct for the installed launcher and for mvn exec:exec -m beast.fx/…Beauti, where the beast modules are on the module path and resolved. But when BEAUti's dependencies are on the class path — embedded in another tool, or under a test runner where everything loads in the unnamed module — no beast.fx module is resolved, both scans find nothing, and template loading fails with:

java.io.IOException: Cannot find template: fxtemplates/Standard.xml

…even though the template is right there in the beast-fx jar at beast.fx/fxtemplates/Standard.xml.

This came up trying to write a headless BEAUti test for a downstream package (bModelTest), where beast-fx is an ordinary jar dependency on the class path.

Change

A class-path fallback in both discovery paths, in BeautiDoc:

  • loadResourceFromModules — after the module scan, try the context class loader's getResourceAsStream on the namespaced name (beast.fx/fxtemplates/Standard.xml), then the bare name. Works on any class loader.
  • getTemplateSources — after the module scan, enumerate fxtemplates/*.xml by scanning java.class.path entries (jars via ZipFile, directories by listing), reading each from its resource URL via a new UrlTemplateSource.

Both are additive and last-precedence: in a module-path launch the module scan finds everything first, and these fallbacks contribute only duplicates, which getTemplateSources already drops by file name. The content is always read from the jar (or classes dir) via the resource URL — never copied out to disk.

Scope is contained to BEAUti template discovery (getTemplateSources has a single caller). No change to BEASTClassLoader.

Note: a manifest-only Class-Path jar (used by some launchers for very long class paths) is not followed by the java.class.path scan; in that layout the module-layer scan is expected to have covered it.

Verification

  • beast-fx BeautiSimpleTest passes unchanged.
  • Downstream (bModelTest, beast-fx on the class path under surefire): before this change, BEAUti startup failed with Cannot find template: fxtemplates/Standard.xml; after it, Standard.xml loads from the beast-fx jar and bModelTest.xml is discovered and merged from the package classes — confirmed in the logs (Loading template from module resource: fxtemplates/Standard.xml, Processing class-path resource …/bmodeltest/fxtemplates/bModelTest.xml).

BeautiDoc discovered fxtemplates only inside resolved module layers:
processTemplate -> loadResourceFromModules scans ModuleLayer.boot() plus
plugin layers, and getTemplateSources enumerates them via
BEASTClassLoader.listResources, which does the same. That is correct for
the installed launcher and for 'mvn exec -m beast.fx/...Beauti', where the
beast modules are on the module path and resolved.

But when BEAUti's dependencies are on the class path instead -- embedded
in another tool, or under a test runner where everything loads in the
unnamed module -- no beast.fx module is resolved, so the scans find
nothing and template loading fails with 'Cannot find template:
fxtemplates/Standard.xml', even though the template is right there in the
beast-fx jar.

Add a class-path fallback in both places:

  - loadResourceFromModules: after the module scan, try the context
    class loader's getResourceAsStream on the namespaced name
    (beast.fx/fxtemplates/Standard.xml) and then the bare name.
  - getTemplateSources: after the module scan, enumerate fxtemplates/*.xml
    by scanning java.class.path entries (jars and directories) and read
    each from its resource URL.

Both are additive and last-precedence: in a module-path launch the module
scan still finds everything first and these fallbacks contribute only
duplicates, which are dropped by name. The content is still read from the
jar, never copied out.

beast-fx BeautiSimpleTest still passes unchanged.
@alexeid
alexeid requested a review from walterxie July 24, 2026 06:45
@walterxie
walterxie merged commit 8570c76 into master Jul 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants