Add ability to pass options to javaagents (#50)#379
Conversation
Support the `-javaagent:<jar>=<options>` form of the flag by introducing a
`javaagent { }` extension with an `agentOptions` map keyed by dependency
coordinate (`group:name` for module dependencies, project path for project
dependencies). Keying by coordinate keeps options independent of the resolved
jar file name so they survive version bumps.
A shared `AgentOptionsResolver` resolves the coordinate-keyed options against
the configuration's resolved artifacts into a `fileName -> options` map, which
is threaded into all four contexts that build a `-javaagent:` argument: the
application `run` task, the `test` task, application distribution start
scripts, and jib container entrypoints. Agents without a matching entry are
attached with no suffix, preserving existing behavior.
Per the design discussion on the issue, options are a single project-level
value shared across contexts; per-context overrides are left as a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBTxQkVXBjJoBSfKPWyY3K
| } | ||
|
|
||
| val localAgentPaths = extraConfig.get().javaagentFiles.get() | ||
| val agentOptions = extraConfig.get().agentOptions.get() |
There was a problem hiding this comment.
🟠 High jib/JavaagentJibExtension.kt:49
extendContainerBuildPlan() throws IllegalStateException whenever agentOptions is unset. For Jib integrations like tel.schich.tinyjib where agentOptions is never configured, this breaks previously working builds even when no agent options are needed. The call to extraConfig.get().agentOptions.get() on line 49 unconditionally finalizes the MapProperty, but no default is provided, so any consumer that doesn't explicitly set agentOptions fails. Consider calling .getOrElse(emptyMap()) instead of .get() so unset options default to an empty map.
| val agentOptions = extraConfig.get().agentOptions.get() | |
| val agentOptions = extraConfig.get().agentOptions.getOrElse(emptyMap()) |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @jib-common/src/main/kotlin/com/ryandens/javaagent/jib/JavaagentJibExtension.kt around line 49:
`extendContainerBuildPlan()` throws `IllegalStateException` whenever `agentOptions` is unset. For Jib integrations like `tel.schich.tinyjib` where `agentOptions` is never configured, this breaks previously working builds even when no agent options are needed. The call to `extraConfig.get().agentOptions.get()` on line 49 unconditionally finalizes the `MapProperty`, but no default is provided, so any consumer that doesn't explicitly set `agentOptions` fails. Consider calling `.getOrElse(emptyMap())` instead of `.get()` so unset options default to an empty map.
| final String key = coordinateKey(artifact.getId().getComponentIdentifier()); | ||
| final String options = coordinates.get(key); | ||
| if (options != null) { | ||
| byFileName.put(artifact.getFile().getName(), options); |
There was a problem hiding this comment.
🟡 Medium javaagent/AgentOptionsResolver.java:60
optionsByFileName keys the options map by artifact.getFile().getName(), so two agents whose resolved jars share the same basename (e.g. com.acme:agent:1.0 and org.other:agent:1.0, both agent-1.0.jar) collide. The second put overwrites the first, causing one agent's options to be lost and both -javaagent: arguments to receive whichever options survived. Using the file name as the join key cannot distinguish the two artifacts, so consider keying by a more stable identifier such as the coordinate or the full file path.
Also found in 1 other location(s)
plugin/src/main/kotlin/com/ryandens/javaagent/JavaagentAwareStartScriptGenerator.kt:111
JavaagentAwareStartScriptGenerator.Fake.writelooks up options withoptions[jar.name], but the resolver map is keyed only by the jar file name. If two configured agents resolve to different artifacts that share the same basename (for example two project agents both published asagent.jar), one entry overwrites the other and the generated start script applies the wrong=<options>suffix or drops one agent's options entirely.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @plugin/src/main/java/com/ryandens/javaagent/AgentOptionsResolver.java around line 60:
`optionsByFileName` keys the options map by `artifact.getFile().getName()`, so two agents whose resolved jars share the same basename (e.g. `com.acme:agent:1.0` and `org.other:agent:1.0`, both `agent-1.0.jar`) collide. The second `put` overwrites the first, causing one agent's options to be lost and both `-javaagent:` arguments to receive whichever options survived. Using the file name as the join key cannot distinguish the two artifacts, so consider keying by a more stable identifier such as the coordinate or the full file path.
Also found in 1 other location(s):
- plugin/src/main/kotlin/com/ryandens/javaagent/JavaagentAwareStartScriptGenerator.kt:111 -- `JavaagentAwareStartScriptGenerator.Fake.write` looks up options with `options[jar.name]`, but the resolver map is keyed only by the jar file name. If two configured agents resolve to different artifacts that share the same basename (for example two project agents both published as `agent.jar`), one entry overwrites the other and the generated start script applies the wrong `=<options>` suffix or drops one agent's options entirely.
11e0ae1 to
7072c0e
Compare
Closes #50.
What
Adds support for the
-javaagent:<jar>=<options>form of the flag so agents like the Prometheus JMX Exporter (=12345:config.yaml) can be configured.API
A new
javaagent { }extension exposes anagentOptionsmap. Options are keyed by dependency coordinate —group:namefor module dependencies, project path for project dependencies — so they are independent of the resolved jar file name and survive version bumps.dependencies { javaagent("io.prometheus.jmx:jmx_prometheus_javaagent:0.20.0") } javaagent { agentOptions.put("io.prometheus.jmx:jmx_prometheus_javaagent", "12345:config.yaml") }How
JavaagentExtension— newagentOptionsMapProperty, created once byJavaagentBasePlugin.AgentOptionsResolver— resolves the coordinate-keyed options against the configuration's resolved artifacts (viaResolvedArtifactResultcomponent identifiers) into afileName -> optionsmap. Lazy and configuration-cache safe; written in Java for the same task-input serialization reason asJavaForkOptionsConfigurer.-javaagent:sites: applicationruntask,testtask, application distribution start scripts, and jib container entrypoints. Agents without a matching entry get no suffix (existing behavior preserved).Design decisions
Both settled up front (see the issue discussion):
tasks.run { javaagent { ... } }, etc.) are a deliberate follow-up.Known limitation
Options containing spaces work for the
run/testtasks (list-basedJvmArgumentProvider) but may hit quoting issues in the single-token distribution start-script opt.Tests
=<options>.simple-agentnow echoes itsagentArgsso options are observable end-to-end.plugin+jibfunctional/unit suites andspotlessCheckpass.🤖 Generated with Claude Code
https://claude.ai/code/session_01FBTxQkVXBjJoBSfKPWyY3K
Note
Add options support to javaagent configuration via coordinate-keyed
agentOptionsmapJavaagentExtensionDSL with anagentOptionsmap property, where keys are dependency coordinates (e.g.group:name) and values are option strings appended as=<options>to-javaagentflags.AgentOptionsResolverutility resolves coordinate keys to file-name-keyed maps in a configuration-cache-safe way.JavaExec/Testtasks viaJavaForkOptionsConfigurer, application run task viaJavaagentApplicationRunPlugin, distribution start scripts viaJavaagentAwareStartScriptGenerator, and Jib container entrypoints viaJavaagentJibPlugin.📊 Macroscope summarized 11e0ae1. 13 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.