feat(runtime): drop-in /modules classpath directory in the container image (#6592) - #6594
Merged
Merged
Conversation
…image (#6592) #6400 added classpath loading of AOT compiled modules, but there was no supported way to get such a module jar onto the classpath of the shipped image: the entrypoint used Spring Boot's JarLauncher (java -jar), so verifying #6400 meant exploding the fat jar and copying the module into BOOT-INF/lib - mutating the published artifact. The image now launches through PropertiesLauncher with -Dloader.path=/modules and ships an empty /modules directory. A downstream image COPYs module jars there, or they are volume-mounted at run time; the platform jar is consumed verbatim. LOADER_PATH overrides the location - no Dirigible configuration property is involved. ClassPathIndex appends the same loader.path / LOADER_PATH jars to the javac classpath, so registry sources can still be compiled against a drop-in module's classes (they could be, back when the module jar was hand-copied into BOOT-INF/lib). Verified with the built image (module jar = a gen.aotdemo controller + its .compiled marker + registry payload): - empty /modules is a no-op: boot log, 1051-entry compile classpath and startup time (19.2s vs 18.3s) match the JarLauncher baseline; a normalised diff of both boot logs shows only concurrent-init ordering noise - /modules with the module jar: payload expanded into registry/public/aot-demo, compile classpath 1052 entries, "Registered [1] class(es) from AOT compiled module(s) on the classpath", GET /services/java/aot-demo/gen/aotdemo/AotDemoController/ping -> 200, and no javac activity for the module - classpath order confirmed against the shipped PropertiesLauncher bytecode: loader.path entries precede BOOT-INF/classes + BOOT-INF/lib. Shadowing a platform class is therefore possible in principle but not by accident - module packages are gen.* / custom.*, which the platform does not use. Documented in the engine-java README + guide and in the Dockerfile itself. engine-java 72/72 unit tests green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
delchev
force-pushed
the
feat/modules-drop-in-classpath
branch
from
August 7, 2026 08:51
2a15dc7 to
21e80ea
Compare
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.
Closes #6592.
#6400 added classpath loading of AOT compiled modules (
CompiledModuleClassProvider→JavaLoader.installCompiledModules, no runtimejavac), but there was no supported way to get such a module jar onto the classpath of the shipped image: the entrypoint used Spring Boot'sJarLauncher(java -jar), which has no mechanism for external jars. Verifying #6400 required exploding the fat jar and copying the module intoBOOT-INF/lib, i.e. mutating the published artifact.What changes
build/application/Dockerfile— launch throughPropertiesLauncherwith-Dloader.path=/modules, and ship an empty/modulesdirectory. A downstream imageCOPYs AOT module jars there, or they are volume-mounted at run time; the platform jar is consumed verbatim.--add-opensflags unchanged.LOADER_PATH(comma-separated, honored natively by the launcher) is the override for other locations — no new Dirigible configuration property.ClassPathIndex— appends the sameloader.path/LOADER_PATHjars to thejavacclasspath, so registry.javasources can still be compiled against a drop-in module's classes. (They could be, back when the module jar was hand-copied intoBOOT-INF/lib; without this the drop-in route would silently lose that.) A directory contributes its*.jarfiles in a stable order, a jar contributes itself, missing segments are skipped.components/engine/engine-java/README.mdand in the module's guide (CLAUDE.md) covering what goes into/modules, theLOADER_PATHoverride and the relationship to feat(engine-java): classpath loading of AOT compiled modules (no runtime javac) #6400, plus a comment in the Dockerfile.The derived OpenTelemetry images (
open-telemetry/dirigible/*/Dockerfile) inherit the entrypoint and only replace/dirigible.jarat the same path, so they need no change.Verification
Built the image locally and ran it both ways. Module jar under test: a
gen.aotdemo.AotDemoControllerclass, itsMETA-INF/dirigible/aot-demo/.compiledmarker and a registry payload file.Empty
/modulesis a strict no-op — compared against ajava -jar(JarLauncher) baseline of the same jar:/modulesStarted DirigibleApplication in 18.27 seconds19.20 seconds(in-container; 20.7s in the first local run)1051entries1051entries/actuator/health/readinessA normalised diff of the two boot logs (timestamps/thread ids/numbers stripped) shows only concurrent-init ordering noise — no structural difference.
PropertiesLauncherreadsStart-Classfrom the jar manifest, so the application boots exactly as with-jar.Module jar in
/modules(-v .../modules:/modules:ro):No
javacactivity for the module — the only log mention is the controller registration.Classpath order (requirement 2 of the issue): confirmed against the shipped
PropertiesLauncherbytecode —getClassPathUrls()adds theloader.pathpaths first, thengetClassPathUrlsForRoot()(BOOT-INF/classes+BOOT-INF/lib). So a drop-in jar can shadow a platform class in principle, but not by accident: module packages aregen.*/custom.*, which the platform does not use. Stated explicitly in the docs.Tests:
ClassPathIndexTestcovers theloader.pathresolution (directory → its jars in stable order, jar → itself, blank/missing/empty → nothing). engine-java 72/72 green; formatter and-P releasejavadoc clean on the module.🤖 Generated with Claude Code