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

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


The following commit(s) were added to refs/heads/main by this push:
     new 172ae77f2703 CAMEL-24661: camel-jbang - camel run 
--runtime=quarkus|spring-boot passes the execution limits and names the Quarkus 
log file after the application (#26532)
172ae77f2703 is described below

commit 172ae77f2703a2cb70db87d5b190bc21f450f0e0
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 17 09:56:23 2026 +0200

    CAMEL-24661: camel-jbang - camel run --runtime=quarkus|spring-boot passes 
the execution limits and names the Quarkus log file after the application 
(#26532)
    
    * CAMEL-24661: camel-jbang - camel run --runtime=quarkus|spring-boot passes 
the execution limits and names the Quarkus log file after the application
    
    The exported Quarkus and Spring Boot runs only carried --jvm-args and
    --jfr to the child JVM, so --max-seconds, --max-messages and
    --max-idle-seconds were ignored (the existing-project path and Camel
    Main already passed them). The camel.main.durationMax* properties are
    now built once and used by all the run paths.
    
    The Quarkus run also named its log file after the --name option, which
    defaults to CamelJBang, while the export derives the application name
    from the first route file and the application reports that name. camel
    log and the TUI Log tab look for <reported name>.log and found nothing.
    The name is now read back from the exported application.properties as
    the Camel Main run already does.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
    
    * CAMEL-24661: camel-jbang - assert the execution limits on each 
existing-project run path directly
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
    
    ---------
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../modules/ROOT/pages/camel-jbang-running.adoc    |   6 ++
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  73 +++++++++------
 .../core/commands/RunExportedProjectTest.java      | 101 +++++++++++++++++++++
 3 files changed, 153 insertions(+), 27 deletions(-)

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 64017405c2aa..ea0b9443b16b 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -258,6 +258,12 @@ Spring Boot runs via `spring-boot:run`, and Quarkus via 
`quarkus:dev` (or `quark
 This is the same JVM you would get from `camel export`, and is what the Camel 
TUI uses when
 launching examples and folders.
 
+The options `--profile`, `--port`, `--prop`, `--max-seconds`, 
`--max-messages`, `--max-idle-seconds`,
+`--jvm-args` and `--jfr` are passed to the application on all three runtimes. 
The application logs to a
+file in `~/.camel` so `camel log` and the TUI can read the logs: `<pid>.log` 
for Spring Boot, and
+`<name>.log` for Quarkus and Camel Main (`<name>` is the name the application 
reports, from `--name` or
+else the first route file).
+
 Limitations compared to the `jbang` runtime:
 
 - Startup is slower, as the project must be built with Maven first (the first 
run also downloads
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 3a69b153d406..33cf67066483 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
@@ -1464,6 +1464,38 @@ public class Run extends CamelCommand {
         return existing.trim() + " " + extra;
     }
 
+    /**
+     * The execution limits ({@code --max-seconds}, {@code --max-messages} and 
{@code --max-idle-seconds}) as
+     * {@code camel.main.durationMax*} system properties, which all the 
runtimes honour.
+     */
+    List<String> buildDurationLimitArgs() {
+        List<String> args = new ArrayList<>();
+        if (executionLimitOptions.maxSeconds > 0) {
+            args.add("-Dcamel.main.durationMaxSeconds=" + 
executionLimitOptions.maxSeconds);
+        }
+        if (executionLimitOptions.maxMessages > 0) {
+            args.add("-Dcamel.main.durationMaxMessages=" + 
executionLimitOptions.maxMessages);
+        }
+        if (executionLimitOptions.maxIdleSeconds > 0) {
+            args.add("-Dcamel.main.durationMaxIdleSeconds=" + 
executionLimitOptions.maxIdleSeconds);
+        }
+        return args;
+    }
+
+    /**
+     * The JVM arguments the exported Quarkus or Spring Boot project is run 
with ({@code jvm.args} and
+     * {@code spring-boot.run.jvmArguments}): {@code --jvm-args}, the flight 
recording and the execution limits. Returns
+     * null when there are none.
+     */
+    String buildExportedRunJvmArgs() {
+        String args = mergeJvmArgs(jvmArgs, buildJfrJvmArgs());
+        List<String> limits = buildDurationLimitArgs();
+        if (!limits.isEmpty()) {
+            args = mergeJvmArgs(args, String.join(" ", limits));
+        }
+        return args != null && !args.isBlank() ? args.trim() : null;
+    }
+
     protected int runQuarkus() throws Exception {
         if (background) {
             printer().printErr("Run Camel Quarkus with --background is not 
supported");
@@ -1568,7 +1600,10 @@ public class Run extends CamelCommand {
             return exit;
         }
 
-        appNameRef.set(eq.name);
+        // the exported project may have derived the application name from the 
source files, and the log file
+        // must be named after the name the application reports (which is what 
camel log and the TUI look for)
+        String appName = resolveExportedAppName(runDirPath, eq.name);
+        appNameRef.set(appName);
 
         // prepare quarkus for logging to file
         Path appProps = Paths.get(eq.exportDir, 
"src/main/resources/application.properties");
@@ -1576,7 +1611,7 @@ public class Run extends CamelCommand {
             String content = Files.readString(appProps);
             content += "\n# logging to file\n"
                        + "quarkus.log.file.enabled=true\n"
-                       + "quarkus.log.file.path=${user.home}/.camel/" + 
eq.name + ".log\n"
+                       + "quarkus.log.file.path=${user.home}/.camel/" + 
appName + ".log\n"
                        + "quarkus.log.file.format=" + QUARKUS_LOG_FILE_FORMAT 
+ "\n";
             Files.writeString(appProps, content);
         }
@@ -1592,9 +1627,9 @@ public class Run extends CamelCommand {
         mvnCmd.add("--quiet");
         mvnCmd.add("--file");
         mvnCmd.add(runDirPath.toRealPath().resolve("pom.xml").toString());
-        String quarkusRunJvmArgs = mergeJvmArgs(jvmArgs, buildJfrJvmArgs());
-        if (quarkusRunJvmArgs != null && !quarkusRunJvmArgs.isBlank()) {
-            mvnCmd.add("-Djvm.args=" + quarkusRunJvmArgs.trim());
+        String quarkusRunJvmArgs = buildExportedRunJvmArgs();
+        if (quarkusRunJvmArgs != null) {
+            mvnCmd.add("-Djvm.args=" + quarkusRunJvmArgs);
         }
         mvnCmd.add("package");
         mvnCmd.add("quarkus:" + (dev ? "dev" : "run"));
@@ -1793,15 +1828,7 @@ public class Run extends CamelCommand {
         if (dev) {
             javaCmd.addAll(buildCamelMainReloadArgs());
         }
-        if (executionLimitOptions.maxSeconds > 0) {
-            javaCmd.add("-Dcamel.main.durationMaxSeconds=" + 
executionLimitOptions.maxSeconds);
-        }
-        if (executionLimitOptions.maxMessages > 0) {
-            javaCmd.add("-Dcamel.main.durationMaxMessages=" + 
executionLimitOptions.maxMessages);
-        }
-        if (executionLimitOptions.maxIdleSeconds > 0) {
-            javaCmd.add("-Dcamel.main.durationMaxIdleSeconds=" + 
executionLimitOptions.maxIdleSeconds);
-        }
+        javaCmd.addAll(buildDurationLimitArgs());
         javaCmd.add("-jar");
         javaCmd.add(jar.toAbsolutePath().toString());
 
@@ -1820,7 +1847,7 @@ public class Run extends CamelCommand {
      * The application name as configured in the exported project ({@code 
camel.main.name}), which is what the running
      * application reports to the CLI and the TUI.
      */
-    private static String resolveExportedAppName(Path runDirPath, String 
fallback) {
+    static String resolveExportedAppName(Path runDirPath, String fallback) {
         Path props = 
runDirPath.resolve("src/main/resources/application.properties");
         if (Files.exists(props)) {
             try (InputStream is = Files.newInputStream(props)) {
@@ -1864,15 +1891,7 @@ public class Run extends CamelCommand {
         if (serverOptions.port != -1) {
             args.add("-D" + portKey + "=" + serverOptions.port);
         }
-        if (executionLimitOptions.maxSeconds > 0) {
-            args.add("-Dcamel.main.durationMaxSeconds=" + 
executionLimitOptions.maxSeconds);
-        }
-        if (executionLimitOptions.maxMessages > 0) {
-            args.add("-Dcamel.main.durationMaxMessages=" + 
executionLimitOptions.maxMessages);
-        }
-        if (executionLimitOptions.maxIdleSeconds > 0) {
-            args.add("-Dcamel.main.durationMaxIdleSeconds=" + 
executionLimitOptions.maxIdleSeconds);
-        }
+        args.addAll(buildDurationLimitArgs());
         if (property != null) {
             for (String p : property) {
                 String s = p.trim();
@@ -2328,9 +2347,9 @@ public class Run extends CamelCommand {
         mvnCmd.add("--quiet");
         mvnCmd.add("--file");
         mvnCmd.add(runDirPath.toRealPath().resolve("pom.xml").toString());
-        String springBootRunJvmArgs = mergeJvmArgs(jvmArgs, buildJfrJvmArgs());
-        if (springBootRunJvmArgs != null && !springBootRunJvmArgs.isBlank()) {
-            mvnCmd.add("-Dspring-boot.run.jvmArguments=" + 
springBootRunJvmArgs.trim());
+        String springBootRunJvmArgs = buildExportedRunJvmArgs();
+        if (springBootRunJvmArgs != null) {
+            mvnCmd.add("-Dspring-boot.run.jvmArguments=" + 
springBootRunJvmArgs);
         }
         mvnCmd.add("spring-boot:run");
         pb.command(mvnCmd);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExportedProjectTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExportedProjectTest.java
new file mode 100644
index 000000000000..a5be3bc8f9c1
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExportedProjectTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies how {@code camel run --runtime=quarkus|spring-boot} runs the 
exported project: the execution limits are
+ * passed to the child JVM like for Camel Main, and the log file is named 
after the name the application reports.
+ */
+class RunExportedProjectTest extends CamelCommandBaseTestSupport {
+
+    @TempDir
+    Path dir;
+
+    private static Run run(String... args) {
+        Run command = new Run(new CamelJBangMain());
+        CommandLine.populateCommand(command, args);
+        return command;
+    }
+
+    @Test
+    void durationLimitsArePassedToExportedRun() {
+        String args = run("--max-seconds=30", "--max-messages=5", 
"--max-idle-seconds=10", "route.yaml")
+                .buildExportedRunJvmArgs();
+
+        assertThat(args).isEqualTo("-Dcamel.main.durationMaxSeconds=30 
-Dcamel.main.durationMaxMessages=5"
+                                   + " 
-Dcamel.main.durationMaxIdleSeconds=10");
+    }
+
+    @Test
+    void durationLimitsAreMergedWithJvmArgs() {
+        String args = run("--jvm-args=-Xmx512m", "--max-seconds=30", 
"route.yaml").buildExportedRunJvmArgs();
+
+        assertThat(args).isEqualTo("-Xmx512m 
-Dcamel.main.durationMaxSeconds=30");
+    }
+
+    @Test
+    void noJvmArgsWhenNothingToPass() {
+        assertThat(run("route.yaml").buildExportedRunJvmArgs()).isNull();
+    }
+
+    @Test
+    void jfrIsPassedAsJvmArgument() {
+        String args = run("--jfr", "route.yaml").buildExportedRunJvmArgs();
+
+        assertThat(args).contains("-XX:StartFlightRecording");
+    }
+
+    @Test
+    void durationLimitsAreTheSameForAllRuntimes() {
+        Run run = run("--max-seconds=30", "route.yaml");
+
+        List<String> limits = run.buildDurationLimitArgs();
+        
assertThat(limits).containsExactly("-Dcamel.main.durationMaxSeconds=30");
+        // exported project (camel run foo.yaml --runtime=quarkus|spring-boot)
+        assertThat(run.buildExportedRunJvmArgs()).isEqualTo(String.join(" ", 
limits));
+        // existing project (camel run pom.xml), per runtime
+        
assertThat(run.buildExistingQuarkusJvmArgs("my-app")).containsAll(limits);
+        assertThat(run.buildExistingSpringBootJvmArgs()).containsAll(limits);
+        assertThat(run.buildExistingCamelMainSystemProperties("my-app", 
dir.resolve("log4j2.properties")))
+                .containsAll(limits);
+    }
+
+    @Test
+    void appNameIsTheExportedCamelMainName() throws Exception {
+        Path resources = dir.resolve("src/main/resources");
+        Files.createDirectories(resources);
+        Files.writeString(resources.resolve("application.properties"), 
"camel.main.name=my-route\n");
+
+        assertThat(Run.resolveExportedAppName(dir, 
"CamelJBang")).isEqualTo("my-route");
+    }
+
+    @Test
+    void appNameFallsBackWhenNotExported() {
+        assertThat(Run.resolveExportedAppName(dir, 
"CamelJBang")).isEqualTo("CamelJBang");
+    }
+
+}

Reply via email to