This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new a157a9e572d2 [backport camel-4.22.x] CAMEL-25528: camel-jbang - camel 
run adds camel.jbang.dependencies of the properties files given on the command 
line (#27658)
a157a9e572d2 is described below

commit a157a9e572d20b3fa33c3297829ecad16ccd4025
Author: Federico Mariani <[email protected]>
AuthorDate: Sat Oct 10 13:31:24 2026 +0200

    [backport camel-4.22.x] CAMEL-25528: camel-jbang - camel run adds 
camel.jbang.dependencies of the properties files given on the command line 
(#27658)
    
    CAMEL-25528: camel-jbang - camel run adds camel.jbang.dependencies of the 
properties files given on the command line
    
    camel run read camel.jbang.dependencies (and the runtime-specific variants) 
only from the application.properties of
    the current directory or --source-dir, so the dependencies declared in 
camel/application.properties were ignored
    when running camel run camel/*.yaml camel/application.properties from the 
parent directory (or with --properties),
    without any message. They are now added after the ones of the profile 
properties, which are not read twice.
    
    Map the Spring transaction classes of a transacted route 
(DataSourceTransactionManager, JdbcTransactionManager,
    DriverManagerDataSource, PlatformTransactionManager, TransactionTemplate 
and SpringTransactionPolicy) in
    camel-main-known-dependencies.properties, so they are downloaded on demand.
---
 .../modules/ROOT/pages/camel-jbang-running.adoc    |  14 +++
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  43 ++++++++
 .../RunPropertiesFilesDependenciesTest.java        | 122 +++++++++++++++++++++
 .../camel-main-known-dependencies.properties       |   6 +
 .../download/KnownDependenciesResolverTest.java    |  18 +++
 5 files changed, 203 insertions(+)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
index d561f88dd6fb..eaa5b97da9ee 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -152,6 +152,20 @@ Multiple dependencies can be separated by comma:
 camel run foo.java --dep=camel-saxon,com.foo:acme:1.0
 ----
 
+The dependencies can also be declared in `application.properties` with 
`camel.jbang.dependencies`
+(or `camel.jbang.dependencies.main`, `camel.jbang.dependencies.spring-boot` 
and `camel.jbang.dependencies.quarkus`
+for one runtime only):
+
+[source,properties]
+----
+camel.jbang.dependencies=camel:sql,com.foo:acme:1.0
+----
+
+They are read from the `application.properties` (and 
`application-<profile>.properties`) of the current directory
+or of `--source-dir`, and from the properties files given on the command line 
or with `--properties`,
+for example `camel run camel/*.yaml camel/application.properties` run from the 
parent directory of `camel`.
+They are added to the dependencies given with `--dep`.
+
 == Including resource directories
 
 To include entire directories of resource files (such as XSD schemas, WSDL 
files, etc.)
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index d9a773a89a65..780076bac691 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -299,6 +299,9 @@ public class Run extends CamelCommand {
                           " (ex. 
/path/to/file.properties,/path/to/other.properties")
     public String propertiesFiles;
 
+    // the profile properties files that are loaded (application.properties 
and application-<profile>.properties)
+    final Set<Path> profilePropertiesFiles = new HashSet<>();
+
     @Option(names = { "--prop", "--property" }, description = "Additional 
properties (override existing)")
     public String[] property;
 
@@ -1231,6 +1234,9 @@ public class Run extends CamelCommand {
         // Add runtime-specific dependencies
         addRuntimeSpecificDependenciesFromProperties(profileProperties);
 
+        // and the dependencies of the properties files given on the command 
line, such as camel/application.properties
+        addDependenciesFromPropertiesFiles(propertiesFiles);
+
         // Add plugin dependencies
         Map<String, Plugin> activePlugins = Collections.emptyMap();
         if (!skipPlugins) {
@@ -1368,6 +1374,42 @@ public class Run extends CamelCommand {
         }
     }
 
+    /**
+     * Adds the dependencies ({@code camel.jbang.dependencies} and the 
runtime-specific variants) declared in the local
+     * properties files given on the command line or with --properties, such 
as {@code camel/application.properties} run
+     * from the parent directory. The profile properties files 
(application.properties in the current directory or the
+     * source dir) are skipped, as their dependencies are already added.
+     */
+    void addDependenciesFromPropertiesFiles(String locations) throws Exception 
{
+        if (ObjectHelper.isEmpty(locations)) {
+            return;
+        }
+        for (String location : locations.split(",")) {
+            location = location.trim();
+            if (location.startsWith("file:")) {
+                location = location.substring(5);
+                if (location.startsWith("//")) {
+                    location = location.substring(2);
+                }
+            } else if (ResourceHelper.hasScheme(location) || 
location.startsWith("github:")
+                    || location.startsWith("gist:")) {
+                // only local files
+                continue;
+            }
+            if (location.isEmpty()) {
+                continue;
+            }
+            Path path = Paths.get(location).toAbsolutePath().normalize();
+            if (profilePropertiesFiles.contains(path) || 
!Files.isRegularFile(path)) {
+                continue;
+            }
+            Properties prop = new CamelCaseOrderedProperties();
+            RuntimeUtil.loadProperties(prop, path);
+            addDependencies(RuntimeUtil.getDependenciesAsArray(prop));
+            addRuntimeSpecificDependenciesFromProperties(prop);
+        }
+    }
+
     protected void addDependencies(String... deps) {
         var depsArray = Optional.ofNullable(deps).orElse(new String[0]);
         var depsList = Arrays.stream(depsArray).filter(tok -> 
!tok.isEmpty()).toList();
@@ -2113,6 +2155,7 @@ public class Run extends CamelCommand {
     private Properties doLoadAndInitProfileProperties(Path 
profilePropertiesPath) throws Exception {
         Properties answer = null;
         if (Files.exists(profilePropertiesPath)) {
+            
profilePropertiesFiles.add(profilePropertiesPath.toAbsolutePath().normalize());
             answer = loadProfilePropertiesFile(profilePropertiesPath);
             // logging level/color may be configured in the properties file
             loggingOptions.loggingLevel = answer.getProperty("loggingLevel", 
loggingOptions.loggingLevel);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunPropertiesFilesDependenciesTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunPropertiesFilesDependenciesTest.java
new file mode 100644
index 000000000000..1b1942bc5b6b
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunPropertiesFilesDependenciesTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-25528: camel.jbang.dependencies in a properties file given on the 
command line, such as
+ * camel/application.properties run from the parent directory, adds the 
dependencies as the application.properties of
+ * the current directory does.
+ */
+class RunPropertiesFilesDependenciesTest {
+
+    private static Run run(String... args) {
+        Run command = new Run(new CamelJBangMain());
+        CommandLine.populateCommand(command, args);
+        return command;
+    }
+
+    private static Path properties(Path dir) throws Exception {
+        Path camel = Files.createDirectories(dir.resolve("camel"));
+        return Files.writeString(camel.resolve("application.properties"), """
+                camel.jbang.dependencies=camel:spring,org.example:shared:1.0
+                camel.jbang.dependencies.main=org.example:main-only:1.0
+                
camel.jbang.dependencies.spring-boot=org.example:spring-boot-only:1.0
+                camel.jbang.dependencies.quarkus=org.example:quarkus-only:1.0
+                greeting=Hello
+                """);
+    }
+
+    @Test
+    void dependenciesOfAPropertiesFileOnTheCommandLine(@TempDir Path dir) 
throws Exception {
+        Path file = properties(dir);
+        Run run = run("--dep=org.example:from-option:1.0", "route.camel.yaml");
+
+        // as the run command passes the files it found among the files to run
+        run.addDependenciesFromPropertiesFiles("file:" + file);
+
+        
assertThat(run.dependencies).containsExactly("org.example:from-option:1.0", 
"camel:spring",
+                "org.example:shared:1.0", "org.example:main-only:1.0");
+    }
+
+    @Test
+    void runtimeSpecificDependencies(@TempDir Path dir) throws Exception {
+        Path file = properties(dir);
+
+        Run springBoot = run("--runtime=spring-boot", "route.camel.yaml");
+        springBoot.addDependenciesFromPropertiesFiles(file.toString());
+        assertThat(springBoot.dependencies).containsExactly("camel:spring", 
"org.example:shared:1.0",
+                "org.example:spring-boot-only:1.0");
+
+        Run quarkus = run("--runtime=quarkus", "route.camel.yaml");
+        quarkus.addDependenciesFromPropertiesFiles(file.toUri().toString());
+        assertThat(quarkus.dependencies).containsExactly("camel:spring", 
"org.example:shared:1.0",
+                "org.example:quarkus-only:1.0");
+    }
+
+    @Test
+    void dependenciesOfThePropertiesOption(@TempDir Path dir) throws Exception 
{
+        Path file = properties(dir);
+        Run run = run("--properties=" + file, "route.camel.yaml");
+
+        run.addDependenciesFromPropertiesFiles(run.propertiesFiles);
+
+        assertThat(run.dependencies).containsExactly("camel:spring", 
"org.example:shared:1.0",
+                "org.example:main-only:1.0");
+    }
+
+    @Test
+    void theProfilePropertiesAreNotAddedTwice(@TempDir Path dir) throws 
Exception {
+        Path file = properties(dir);
+        Run run = run("route.camel.yaml");
+        // the application.properties of the current directory, whose 
dependencies are added already
+        run.profilePropertiesFiles.add(file.toAbsolutePath().normalize());
+
+        run.addDependenciesFromPropertiesFiles("file:" + file);
+
+        assertThat(run.dependencies).isEmpty();
+    }
+
+    @Test
+    void remoteAndMissingFilesAreSkipped(@TempDir Path dir) throws Exception {
+        Run run = run("route.camel.yaml");
+
+        run.addDependenciesFromPropertiesFiles(
+                
"github:apache:camel-kamelets-examples:jbang/hello-java/application.properties,classpath:app.properties,file:"
+                                               + 
dir.resolve("missing.properties"));
+
+        assertThat(run.dependencies).isEmpty();
+    }
+
+    @Test
+    void aFileWithoutDependencies(@TempDir Path dir) throws Exception {
+        Path file = Files.writeString(dir.resolve("other.properties"), 
"greeting=Hello\n");
+        Run run = run("route.camel.yaml");
+
+        run.addDependenciesFromPropertiesFiles("file:" + file);
+
+        assertThat(run.dependencies).isEmpty();
+    }
+}
diff --git 
a/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
 
b/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
index 9b1946ae1f53..e8671780865b 100644
--- 
a/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
+++ 
b/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
@@ -23,6 +23,12 @@ org.springframework.stereotype.Service = camel:spring
 org.springframework.beans.factory.annotation.Value = camel:spring
 org.springframework.amqp.rabbit.connection.CachingConnectionFactory = 
camel:spring-rabbitmq
 org.springframework.jdbc.datasource.SimpleDriverDataSource = camel:sql
+org.springframework.jdbc.datasource.DriverManagerDataSource = camel:sql
+org.springframework.jdbc.datasource.DataSourceTransactionManager = camel:sql
+org.springframework.jdbc.support.JdbcTransactionManager = camel:sql
+org.springframework.transaction.PlatformTransactionManager = camel:spring
+org.springframework.transaction.support.TransactionTemplate = camel:spring
+org.apache.camel.spring.spi.SpringTransactionPolicy = camel:spring
 spring.datasource.url = 
org.springframework.boot:spring-boot-starter-jdbc:${spring-boot-version}
 
 quarkus.datasource.db-kind\=db2 = 
io.quarkus:quarkus-jdbc-db2:${quarkus-version}
diff --git 
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
 
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
index 8b3594ca7e21..fbe5b767eae8 100644
--- 
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
+++ 
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
@@ -55,6 +55,24 @@ public class KnownDependenciesResolverTest {
     public static class SomeClass {
     }
 
+    @Test
+    void theShippedMappingResolvesSpringTransactionClasses() {
+        // the beans of a transacted route: the Spring JDBC transaction 
managers and datasources come with camel-sql
+        // (spring-jdbc), the Spring transaction policy and spring-tx come 
with camel-spring
+        KnownDependenciesResolver resolver = new KnownDependenciesResolver(new 
SimpleCamelContext(), null, null);
+        resolver.loadKnownDependencies();
+
+        assertGav(resolver, 
"org.springframework.jdbc.datasource.DataSourceTransactionManager", 
"org.apache.camel",
+                "camel-sql");
+        assertGav(resolver, 
"org.springframework.jdbc.support.JdbcTransactionManager", "org.apache.camel", 
"camel-sql");
+        assertGav(resolver, 
"org.springframework.jdbc.datasource.DriverManagerDataSource", 
"org.apache.camel", "camel-sql");
+        assertGav(resolver, 
"org.springframework.transaction.PlatformTransactionManager", 
"org.apache.camel",
+                "camel-spring");
+        assertGav(resolver, 
"org.springframework.transaction.support.TransactionTemplate", 
"org.apache.camel",
+                "camel-spring");
+        assertGav(resolver, 
"org.apache.camel.spring.spi.SpringTransactionPolicy", "org.apache.camel", 
"camel-spring");
+    }
+
     @Test
     void anImportResolvesAnyClassOfAComponent() {
         // CAMEL-25239: any class of a component a source imports, not only 
the component class itself, such as the

Reply via email to