[
https://issues.apache.org/jira/browse/CAMEL-25833?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18128863#comment-18128863
]
Claus Ibsen commented on CAMEL-25833:
-------------------------------------
Analysis.
*How a dependency gets downloaded today*
* At startup: {{camel run}} reads {{camel.jbang.dependencies}} (and
{{camel.jbang.dependencies.main}} for this runtime) from
application.properties, merges it with {{--dep}}, and passes one list to
KameletMain as the initial property {{camel.jbang.dependencies}}.
{{CommandLineDependencyDownloader.doInit}} downloads it once.
* In a reload: the changed file goes to {{MainPropertiesReload}}, which applies
components and, with CAMEL-25767 (PR #28123), new beans. Nothing reads
{{camel.jbang.dependencies}} there. {{DependencyDownloaderPropertiesComponent}}
scans the properties for known classes, but only when the context initializes.
A running app downloads already in three cases, so the parts exist:
* classes of the 147 packages in known-third-party-libraries.properties (JDBC
drivers, pools, messaging and NoSQL clients), when the class is resolved;
* the Camel components, data formats and languages that a route uses;
* {{//DEPS}} lines in source files, on every load, also in a reload:
{{DefaultRoutesLoader}} parses the modelines, and {{JBangModelineParser}}
passes each dependency to the {{DependencyStrategy}} beans
({{DependencyDownloaderStrategy}} in KameletMain). Tested on main: adding {{#
//DEPS com.github.javafaker:javafaker:1.0.2}} to a running YAML route, with a
step that calls the class, worked at once.
So the gap is a library outside the known list, declared in a properties file.
That is the path the AI tools of the camel-jbang-mcp server give for an unknown
library ({{DependencyLookup}}, {{BeanRefChecks}}, {{GroovyImportChecks}}).
*How often it happens*
In the benchmark runs of Oct 15-19 (r15 to r18), the model wrote the line 29
times: 28 times commons-validator in transform-groovy step 2, once
camel-openapi-java. The app ran in dev mode, and the model restarted it after
the write every time, so no step failed because of this. No tool hint tells it
to restart. People are more likely to miss the restart: with CAMEL-25767, a
dependency and a bean added in one save give a WARN with
{{ClassNotFoundException}} (and with CAMEL-25834, also when a route changed in
the same scan).
*Proposed fix*
In KameletMain, override
{{autoConfigurationFromReloadedProperties(camelContext, properties, changed)}}
(protected, added by CAMEL-25767). When the properties contain
{{camel.jbang.dependencies}} or {{camel.jbang.dependencies.main}}, pass each
entry to the {{DependencyStrategy}} beans first, as a {{//DEPS}} line does, and
then let the beans and components be created.
Edge cases:
* A library that is loaded already in another version: {{alreadyOnClasspath}}
compares the exact version, so both jars would be on the classpath. Log that a
restart is needed to change the version, and do not download it.
* A dependency that is removed stays on the classpath until a restart: log that.
* A library resolved on its own can bring other versions of jars that are
loaded already. The components downloaded on demand have the same risk today.
* {{camel.jbang.repos}} changed in the same save is not applied.
* A download that fails (offline, a wrong name) fails the reload of the file,
which reports it.
It builds on CAMEL-25767, so it follows PR #28123.
_Claude Code on behalf of davsclaus_
> camel-jbang - camel run --dev: a dependency added to camel.jbang.dependencies
> while the app runs is not downloaded
> ------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25833
> URL: https://issues.apache.org/jira/browse/CAMEL-25833
> Project: Camel
> Issue Type: Improvement
> Components: camel-jbang
> Reporter: Claus Ibsen
> Priority: Major
> Fix For: 4.24.0
>
>
> {{camel run}} reads {{camel.jbang.dependencies}} from application.properties
> before the app starts, and hands the list to KameletMain. KameletMain
> downloads it once, when the app starts
> ({{CommandLineDependencyDownloader.doInit}}). In dev mode, a dependency that
> is added while the app runs is not downloaded. Only a restart picks it up.
> With CAMEL-25767 (PR #28123), a reload creates the beans that are new in
> {{camel.beans.*}}. A bean whose class comes from such a dependency then
> fails. Reproduced with {{camel run --source-dir=. --dev}} and the camel
> launcher built from that PR, by appending to application.properties while the
> app runs:
> {noformat}
> camel.jbang.dependencies = com.github.javafaker:javafaker:1.0.2
> camel.beans.faker = #class:com.github.javafaker.Faker
> {noformat}
> {noformat}
> INFO RouteWatcherReloadStrategy : Reloading properties:
> file:application.properties. (Only Camel routes and components can be updated
> with changes)
> WARN FileWatcherResourceReloadStrategy : Error reloading routes from file:
> application.properties due to: com.github.javafaker.Faker. This exception is
> ignored.
> java.lang.ClassNotFoundException: com.github.javafaker.Faker
> at
> org.apache.camel.impl.engine.DefaultClassResolver.resolveMandatoryClass(DefaultClassResolver.java:136)
> at
> org.apache.camel.main.BaseMainSupport.createBeanViaInferredBuilder(BaseMainSupport.java:2772)
> at
> org.apache.camel.main.BaseMainSupport.bindBeansToRegistry(BaseMainSupport.java:2867)
> at
> org.apache.camel.main.BaseMainSupport.bindReloadedBeans(BaseMainSupport.java:2831)
> ...
> {noformat}
> With the same two lines in application.properties before the app starts,
> javafaker is downloaded and the bean is bound. Classes that camel-jbang knows
> (such as {{org.apache.activemq.artemis}}, in
> known-third-party-libraries.properties) are downloaded when they are used, so
> they work in a reload too. Other libraries do not.
> This is how a coding agent adds a library. The AI tools of the
> camel-jbang-mcp server tell it to declare
> {{camel.jbang.dependencies=<groupId>:<artifactId>:<version>}} in
> application.properties ({{DependencyLookup}}, {{BeanRefChecks}},
> {{GroovyImportChecks}}). The agent does that while the app runs in dev mode.
> The tools do not say that a restart is needed.
> Proposal: when a reloaded properties file changes
> {{camel.jbang.dependencies}}, download the new dependencies before the beans
> and components of that reload are created, as
> {{CommandLineDependencyDownloader}} does at startup
> ({{DependencyDownloader.downloadDependency}}). A dependency that is removed
> stays on the classpath until a restart, which can be logged.
> Related, found in the same test: when application.properties is saved
> together with another file (here a route, found in the same scan of the
> folder), the error is not logged at all.
> {{RouteWatcherReloadStrategy.onReloadBatch}} (CAMEL-25041, 4.23.0) catches
> the failure of the batch, logs it at DEBUG, and reloads the files one at a
> time. By then the properties component has the new values, so the properties
> file has no changes left, and nothing fails. The routes reload, the bean is
> missing, and the log does not say why. This is not only about dependencies:
> the failure of any properties reload in a batch is lost.
> Found while testing CAMEL-25767.
> _Claude Code on behalf of davsclaus_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)