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 be3dd651866f CAMEL-24699: camel-jbang - camel run on an existing 
Quarkus or Camel Main project logs to ~/.camel (#26526)
be3dd651866f is described below

commit be3dd651866fb9f2c7f805cc9d2b1997c5b16950
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 17 08:43:07 2026 +0200

    CAMEL-24699: camel-jbang - camel run on an existing Quarkus or Camel Main 
project logs to ~/.camel (#26526)
    
    * CAMEL-24699: camel-jbang - camel run on an existing Quarkus or Camel Main 
project logs to ~/.camel
    
    An existing Maven project run with camel run pom.xml now logs to
    ~/.camel/<name>.log for all three runtimes, so camel log, the TUI Log
    tab and the MCP log resource can read it. Quarkus gets the
    quarkus.log.file.* properties via jvm.args, Camel Main a rendered
    log4j2 configuration in target/ via -Dlog4j2.configurationFile on the
    Maven command line (camel:run runs Camel inside the Maven JVM). The name
    is camel.main.name from application.properties or the artifactId, and is
    passed to the application so it matches the file the readers look for.
    
    The Camel Main path passed --profile, --jfr and --jvm-args via
    -Dcamel.jvmArgs, which is not a camel-maven-plugin parameter, so they
    were dropped: the profile is now a plain -D and JVM options go through
    MAVEN_OPTS. camel:run also does not compile the project, so compile is
    run first. --port, --prop and the --max-* limits are passed to all three
    runtimes.
    
    The TUI ran Maven projects with its own launcher (only Spring Boot
    logged to a file, same camel.jvmArgs problem); it now runs them with
    camel run pom.xml so there is one implementation.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
    
    * CAMEL-24699: camel-jbang - docs: Spring Boot logs to <pid>.log, Quarkus 
and Camel Main to <name>.log
    
    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    |   3 +-
 .../modules/ROOT/pages/camel-jbang-tips.adoc       |  20 +-
 .../modules/ROOT/pages/camel-jbang-tui.adoc        |  13 +-
 .../apache/camel/dsl/jbang/core/commands/Run.java  | 212 ++++++++++++++------
 .../core/commands/RunExistingProjectTest.java      | 178 +++++++++++++++++
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |   1 -
 .../dsl/jbang/core/commands/tui/LaunchManager.java | 214 ++-------------------
 7 files changed, 372 insertions(+), 269 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 cefdef8abcff..64017405c2aa 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -280,7 +280,8 @@ camel run foo.camel.yaml --runtime=quarkus 
--quarkus-version=3.9.4
 ----
 
 When running an existing Maven project (`camel run pom.xml`) the runtime is 
detected from the
-`pom.xml`, as such a project cannot run in-process.
+`pom.xml`, as such a project cannot run in-process. The application logs to a 
file in `~/.camel`
+so `camel log` and the TUI can read the logs; see 
xref:camel-jbang-tips.adoc#_running_a_maven_based_project[Running a Maven based 
project].
 
 == Running local Kamelets
 
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tips.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tips.adoc
index 3553b63d3195..970e6ddd8446 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tips.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tips.adoc
@@ -210,14 +210,24 @@ See the 
https://maven.apache.org/guides/mini/guide-encryption.html[Maven encrypt
 
 == Running a Maven based project
 
-Camel CLI can do a _best effort_ run of an existing Maven-based project for 
migration purposes:
+Camel CLI can run an existing Maven-based project:
 
 [source,bash]
 ----
 camel run pom.xml
 ----
 
-Camel CLI scans `src/main/java` and `src/main/resources` for files to include.
-
-NOTE: This is not a fully compatible way to run Maven projects. It cannot 
start Spring Boot or Quarkus
-applications — use the proper plugins for those. This command is mainly useful 
for migrating from old projects.
+The runtime is detected from the `pom.xml` (Spring Boot, Quarkus, or Camel 
Main), and the project is
+built and run with Maven (`spring-boot:run`, `quarkus:dev` with `--dev` or 
`quarkus:run`, and `camel:run`).
+The project itself is not modified: the `camel-cli-connector` dependency is 
injected into a temporary
+`camel-jbang-run-pom.xml`, so the application shows up in `camel ps`, and the 
application logs to a file
+in `~/.camel` so `camel log`, the TUI and the MCP server can read the logs the 
same as for any other
+`camel run`: `<pid>.log` for Spring Boot, and `<name>.log` for Quarkus and 
Camel Main, whose logging is
+configured before the process id is known (`<name>` is `camel.main.name` from 
the project's
+`application.properties`, or else the Maven artifactId, and is passed to the 
application as its name).
+The temporary files are removed when the application stops.
+
+The options `--profile`, `--port`, `--prop`, `--max-seconds`, 
`--max-messages`, `--max-idle-seconds`,
+`--jvm-args` and `--jfr` are passed to the application (Camel Main runs inside 
the Maven JVM, so
+`--jvm-args` and `--jfr` are set via `MAVEN_OPTS`). A Camel Main project is 
expected to use log4j2
+for logging (the same as `camel export --runtime=main` generates).
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
index c44e615e4382..3b601f254a46 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
@@ -81,8 +81,10 @@ camel tui /path/to/my-project
 
 The TUI opens the directory in the Source tab so you can browse the project 
files immediately.
 When a `pom.xml` is present, the runtime is auto-detected (Spring Boot, 
Quarkus, or Camel Main).
-Press *F10* to run the project -- Maven projects are launched with the 
appropriate goal
-(`spring-boot:run`, `quarkus:dev`, or `camel:run`), and plain directories are 
run with `camel run`.
+Press *F10* to run the project -- Maven projects are run with `camel run 
pom.xml`, which launches
+them with the appropriate goal (`spring-boot:run`, `quarkus:dev`, or 
`camel:run`), and plain
+directories are run with `camel run`. Either way the application logs to 
`~/.camel` so the Log tab
+shows its logs.
 
 This is a quick way to explore and run any Camel project without starting it 
separately first.
 
@@ -117,10 +119,9 @@ Once added, start your application normally and the TUI 
will discover it automat
 No additional configuration is needed -- the connector auto-detects on the 
classpath and
 registers the application with the local Camel CLI.
 
-TIP: When you open a Spring Boot project via *F2 > Open Project* and run it 
with *F10*,
-the TUI automatically injects the `camel-cli-connector-starter` dependency if 
it's not
-already in your `pom.xml`. This means the TUI can monitor the application 
without
-modifying your project.
+TIP: When you open a Spring Boot, Quarkus or Camel Main project via *F2 > Open 
Project* and run
+it with *F10*, the CLI connector dependency is automatically injected if it's 
not already in your
+`pom.xml`. This means the TUI can monitor the application without modifying 
your project.
 
 See xref:camel-jbang-managing.adoc[Managing Integrations] for more details.
 
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 dc81b39f169d..3a69b153d406 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
@@ -149,6 +149,11 @@ public class Run extends CamelCommand {
 
     private static final String OPENAPI_GENERATED_FILE = 
CommandLineHelper.CAMEL_JBANG_WORK_DIR + "/generated-openapi.yaml";
     private static final String CLIPBOARD_GENERATED_FILE = 
CommandLineHelper.CAMEL_JBANG_WORK_DIR + "/generated-clipboard";
+    // the same layout as the camel-jbang console logging, so the log file 
reads the same for every runtime
+    static final String QUARKUS_LOG_FILE_FORMAT
+            = "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %i --- [%15.15t] %-40.40c{3.} : 
%s%e%n";
+    // the file with the log4j2 configuration that an existing Camel Main 
project is run with
+    static final String CAMEL_MAIN_RUN_LOG_CONFIG = 
"camel-jbang-run-log4j2.properties";
 
     private static final Pattern PACKAGE_PATTERN = Pattern.compile(
             "^\\s*package\\s+([a-zA-Z][.\\w]*)\\s*;.*$", Pattern.MULTILINE);
@@ -1572,7 +1577,7 @@ public class Run extends CamelCommand {
             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.format=%d{yyyy-MM-dd HH:mm:ss.SSS} 
%5p %i --- [%15.15t] %-40.40c{3.} : %s%e%n\n";
+                       + "quarkus.log.file.format=" + QUARKUS_LOG_FILE_FORMAT 
+ "\n";
             Files.writeString(appProps, content);
         }
 
@@ -1832,19 +1837,90 @@ public class Run extends CamelCommand {
         return fallback;
     }
 
+    /**
+     * The application name of an existing Maven project: {@code 
camel.main.name} from its application.properties, or
+     * else the Maven artifactId (or the project directory name). The name is 
also passed to the application as
+     * {@code camel.main.name}, so the name it reports to the CLI and the TUI 
matches the {@code <name>.log} file that
+     * {@code camel log} and the TUI read when there is no {@code <pid>.log}.
+     */
+    static String resolveExistingProjectAppName(Path projectDir, Model model) {
+        String name = resolveExportedAppName(projectDir, null);
+        if (name == null && model != null && model.getArtifactId() != null && 
!model.getArtifactId().isBlank()) {
+            name = model.getArtifactId().trim();
+        }
+        if (name == null) {
+            name = projectDir.getFileName().toString();
+        }
+        return name;
+    }
+
+    /**
+     * The {@code -D} system properties an existing Maven project is run with: 
the HTTP port ({@code --port}, as the
+     * given property), the execution limits ({@code --max-seconds}, {@code 
--max-messages} and
+     * {@code --max-idle-seconds}) and the additional properties ({@code 
--prop}).
+     */
+    List<String> buildExistingProjectSystemProperties(String portKey) {
+        List<String> args = new ArrayList<>();
+        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);
+        }
+        if (property != null) {
+            for (String p : property) {
+                String s = p.trim();
+                if (!s.isEmpty()) {
+                    args.add(s.startsWith("-D") ? s : "-D" + s);
+                }
+            }
+        }
+        return args;
+    }
+
+    /**
+     * The Quarkus logging properties that make an existing Quarkus project 
also log to the {@code <name>.log} file in
+     * the {@code ~/.camel} directory (the same as the exported project does 
via its application.properties).
+     */
+    static List<String> buildQuarkusLogFileJvmArgs(String appName) {
+        Path logFile = CommandLineHelper.getCamelDir().resolve(appName + 
".log");
+        String fileName = logFile.toAbsolutePath().toString().replace("\\", 
"/");
+        List<String> args = new ArrayList<>();
+        args.add("-Dquarkus.log.file.enabled=true");
+        args.add("-Dquarkus.log.file.path=" + fileName);
+        // quoted as the format has spaces (quarkus:dev parses jvm.args as a 
command line)
+        args.add("-Dquarkus.log.file.format=\"" + QUARKUS_LOG_FILE_FORMAT + 
"\"");
+        return args;
+    }
+
     /**
      * Writes a log4j2 configuration to the exported Camel Main project that 
logs to the console, and to the
      * {@code <name>.log} file in the {@code ~/.camel} directory.
      */
     private void writeCamelMainRunLogConfig(Path runDirPath, String appName) 
throws IOException {
+        
writeCamelMainRunLogConfig(runDirPath.resolve("src/main/resources/log4j2.properties"),
 appName,
+                loggingOptions.loggingLevel);
+    }
+
+    /**
+     * Writes the log4j2 configuration used by {@code camel run 
--runtime=main} to the given file. It logs to the
+     * console, and to the {@code <name>.log} file in the {@code ~/.camel} 
directory (which is deleted first, so it does
+     * not start with the output of a previous run).
+     */
+    static void writeCamelMainRunLogConfig(Path target, String appName, String 
loggingLevel) throws IOException {
         Path logFile = CommandLineHelper.getCamelDir().resolve(appName + 
".log");
         Files.deleteIfExists(logFile);
         String fileName = logFile.toAbsolutePath().toString().replace("\\", 
"/");
         try (InputStream is = 
Run.class.getClassLoader().getResourceAsStream("camel-main-run-log4j2.properties"))
 {
             String content = new String(is.readAllBytes(), 
StandardCharsets.UTF_8)
                     .replace("{{logFile}}", fileName)
-                    .replace("{{level}}", loggingOptions.loggingLevel != null 
? loggingOptions.loggingLevel : "info");
-            Path target = 
runDirPath.resolve("src/main/resources/log4j2.properties");
+                    .replace("{{level}}", loggingLevel != null ? loggingLevel 
: "info");
             Files.createDirectories(target.getParent());
             Files.writeString(target, content);
         }
@@ -1937,6 +2013,11 @@ public class Run extends CamelCommand {
             w.write(fos, model);
         }
 
+        // log to console and to file in ~/.camel (so camel log and the TUI 
can tail the logs)
+        String appName = resolveExistingProjectAppName(projectDir, model);
+        Path logConfig = 
projectDir.resolve("target").resolve(CAMEL_MAIN_RUN_LOG_CONFIG);
+        writeCamelMainRunLogConfig(logConfig, appName, 
loggingOptions.loggingLevel);
+
         // shutdown hook to clean up temp files
         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
             try {
@@ -1955,6 +2036,7 @@ public class Run extends CamelCommand {
                     }
                 }
                 Files.deleteIfExists(tempPom);
+                Files.deleteIfExists(logConfig);
             } catch (Exception e) {
                 // ignore
             }
@@ -1970,27 +2052,11 @@ public class Run extends CamelCommand {
         cmd.add("--quiet");
         cmd.add("--file");
         cmd.add(tempPom.toString());
-        StringBuilder camelJvmArgs = new StringBuilder();
-        if (profile != null && !"prod".equals(profile)) {
-            camelJvmArgs.append("-Dcamel.main.profile=").append(profile);
-        }
-        String camelJfrArg = buildJfrJvmArgs();
-        if (camelJfrArg != null) {
-            if (!camelJvmArgs.isEmpty()) {
-                camelJvmArgs.append(" ");
-            }
-            camelJvmArgs.append(camelJfrArg);
-        }
-        if (jvmArgs != null && !jvmArgs.isBlank()) {
-            if (!camelJvmArgs.isEmpty()) {
-                camelJvmArgs.append(" ");
-            }
-            camelJvmArgs.append(jvmArgs.trim());
-        }
-        if (!camelJvmArgs.isEmpty()) {
-            cmd.add("-Dcamel.jvmArgs=" + camelJvmArgs);
-        }
+        // camel:run runs Camel inside the Maven JVM, so the system properties 
go directly on the Maven command line
+        cmd.addAll(buildExistingCamelMainSystemProperties(appName, logConfig));
         cmd.add("-DskipTests");
+        // camel:run does not compile the project itself
+        cmd.add("compile");
         cmd.add("camel:run");
 
         printer().println("Running Camel Main project: " + projectDir);
@@ -1999,12 +2065,32 @@ public class Run extends CamelCommand {
         pb.command(cmd);
         pb.directory(projectDir.toFile());
         pb.inheritIO();
+        // JVM options (--jvm-args and --jfr) must be set on the Maven JVM as 
Camel runs inside it
+        String mavenOpts = mergeJvmArgs(jvmArgs, buildJfrJvmArgs());
+        if (mavenOpts != null && !mavenOpts.isBlank()) {
+            pb.environment().merge("MAVEN_OPTS", mavenOpts.trim(), (existing, 
extra) -> existing + " " + extra);
+        }
         Process p = pb.start();
         processRef.set(p);
         this.spawnPid = p.pid();
         return p.waitFor();
     }
 
+    /**
+     * The {@code -D} system properties an existing Camel Main project is run 
with (Camel runs inside the Maven JVM so
+     * they go on the Maven command line): the log4j2 configuration, the 
profile, the name, the port and properties.
+     */
+    List<String> buildExistingCamelMainSystemProperties(String appName, Path 
logConfig) {
+        List<String> args = new ArrayList<>();
+        args.add("-Dlog4j2.configurationFile=" + logConfig.toAbsolutePath());
+        if (profile != null && !"prod".equals(profile)) {
+            args.add("-Dcamel.main.profile=" + profile);
+        }
+        args.add("-Dcamel.main.name=" + appName);
+        args.addAll(buildExistingProjectSystemProperties("camel.server.port"));
+        return args;
+    }
+
     private int runExistingQuarkusProject(AtomicReference<Process> processRef) 
throws Exception {
         Path pomPath = Path.of(files.get(0)).toAbsolutePath();
         Path projectDir = pomPath.getParent();
@@ -2070,31 +2156,16 @@ public class Run extends CamelCommand {
         Path mvnwPath = projectDir.resolve(mvnw);
         String mvnCmd = Files.isExecutable(mvnwPath) ? mvnwPath.toString() : 
"mvn";
 
+        // log to console and to file in ~/.camel (so camel log and the TUI 
can tail the logs)
+        String appName = resolveExistingProjectAppName(projectDir, model);
+        Files.deleteIfExists(CommandLineHelper.getCamelDir().resolve(appName + 
".log"));
+
         List<String> cmd = new ArrayList<>();
         cmd.add(mvnCmd);
         cmd.add("--quiet");
         cmd.add("--file");
         cmd.add(tempPom.toString());
-        StringBuilder quarkusJvmArgs = new StringBuilder();
-        if (profile != null && !"prod".equals(profile)) {
-            quarkusJvmArgs.append("-Dcamel.main.profile=").append(profile);
-        }
-        String quarkusJfrArg = buildJfrJvmArgs();
-        if (quarkusJfrArg != null) {
-            if (!quarkusJvmArgs.isEmpty()) {
-                quarkusJvmArgs.append(" ");
-            }
-            quarkusJvmArgs.append(quarkusJfrArg);
-        }
-        if (jvmArgs != null && !jvmArgs.isBlank()) {
-            if (!quarkusJvmArgs.isEmpty()) {
-                quarkusJvmArgs.append(" ");
-            }
-            quarkusJvmArgs.append(jvmArgs.trim());
-        }
-        if (!quarkusJvmArgs.isEmpty()) {
-            cmd.add("-Djvm.args=" + quarkusJvmArgs);
-        }
+        cmd.add("-Djvm.args=" + String.join(" ", 
buildExistingQuarkusJvmArgs(appName)));
         cmd.add("-DskipTests");
         cmd.add("package");
         cmd.add("quarkus:" + (dev ? "dev" : "run"));
@@ -2111,6 +2182,27 @@ public class Run extends CamelCommand {
         return p.waitFor();
     }
 
+    /**
+     * The JVM arguments ({@code jvm.args}) an existing Quarkus project is run 
with: logging to file, the profile, the
+     * name, the port and properties, the flight recording and {@code 
--jvm-args}.
+     */
+    List<String> buildExistingQuarkusJvmArgs(String appName) {
+        List<String> args = new 
ArrayList<>(buildQuarkusLogFileJvmArgs(appName));
+        if (profile != null && !"prod".equals(profile)) {
+            args.add("-Dcamel.main.profile=" + profile);
+        }
+        args.add("-Dcamel.main.name=" + appName);
+        args.addAll(buildExistingProjectSystemProperties("quarkus.http.port"));
+        String jfrArg = buildJfrJvmArgs();
+        if (jfrArg != null) {
+            args.add(jfrArg);
+        }
+        if (jvmArgs != null && !jvmArgs.isBlank()) {
+            args.add(jvmArgs.trim());
+        }
+        return args;
+    }
+
     protected int runSpringBoot() throws Exception {
         if (background) {
             printer().printErr("Run Camel Spring Boot with --background is not 
supported");
@@ -2251,6 +2343,27 @@ public class Run extends CamelCommand {
         return p.waitFor();
     }
 
+    /**
+     * The JVM arguments ({@code spring-boot.run.jvmArguments}) an existing 
Spring Boot project is run with: logging to
+     * file, the profile, the port and properties, the flight recording and 
{@code --jvm-args}.
+     */
+    List<String> buildExistingSpringBootJvmArgs() {
+        List<String> args = new ArrayList<>();
+        args.add("-Dlogging.config=classpath:logback-camel-jbang.xml");
+        if (profile != null && !"prod".equals(profile)) {
+            args.add("-Dcamel.main.profile=" + profile);
+        }
+        args.addAll(buildExistingProjectSystemProperties("server.port"));
+        String jfrArg = buildJfrJvmArgs();
+        if (jfrArg != null) {
+            args.add(jfrArg);
+        }
+        if (jvmArgs != null && !jvmArgs.isBlank()) {
+            args.add(jvmArgs.trim());
+        }
+        return args;
+    }
+
     private int runExistingSpringBootProject(AtomicReference<Process> 
processRef) throws Exception {
         Path pomPath = Path.of(files.get(0)).toAbsolutePath();
         Path projectDir = pomPath.getParent();
@@ -2330,18 +2443,7 @@ public class Run extends CamelCommand {
         cmd.add("--quiet");
         cmd.add("--file");
         cmd.add(tempPom.toString());
-        StringBuilder sbJvmArgs = new 
StringBuilder("-Dlogging.config=classpath:logback-camel-jbang.xml");
-        if (profile != null && !"prod".equals(profile)) {
-            sbJvmArgs.append(" -Dcamel.main.profile=").append(profile);
-        }
-        String sbJfrArg = buildJfrJvmArgs();
-        if (sbJfrArg != null) {
-            sbJvmArgs.append(" ").append(sbJfrArg);
-        }
-        if (jvmArgs != null && !jvmArgs.isBlank()) {
-            sbJvmArgs.append(" ").append(jvmArgs.trim());
-        }
-        cmd.add("-Dspring-boot.run.jvmArguments=" + sbJvmArgs);
+        cmd.add("-Dspring-boot.run.jvmArguments=" + String.join(" ", 
buildExistingSpringBootJvmArgs()));
         cmd.add("-DskipTests");
         cmd.add("package");
         cmd.add("spring-boot:run");
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExistingProjectTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExistingProjectTest.java
new file mode 100644
index 000000000000..d40c744043f2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunExistingProjectTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.maven.model.Model;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.api.parallel.Isolated;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies how {@code camel run pom.xml} runs an existing Maven project: the 
application logs to a file in
+ * {@code ~/.camel} that {@code camel log} and the TUI can read, and the 
options are passed to the right JVM for each
+ * runtime.
+ */
+@Isolated
+class RunExistingProjectTest extends CamelCommandBaseTestSupport {
+
+    @TempDir
+    Path home;
+
+    private String originalHome;
+
+    @BeforeEach
+    void useTempHome() {
+        originalHome = CommandLineHelper.getHomeDir().toString();
+        CommandLineHelper.useHomeDir(home.toString());
+    }
+
+    @AfterEach
+    void restoreHome() {
+        CommandLineHelper.useHomeDir(originalHome);
+    }
+
+    private static Run run(String... args) {
+        Run command = new Run(new CamelJBangMain());
+        CommandLine.populateCommand(command, args);
+        return command;
+    }
+
+    private static Model model(String artifactId) {
+        Model model = new Model();
+        model.setArtifactId(artifactId);
+        return model;
+    }
+
+    @Test
+    void appNameIsCamelMainNameFromApplicationProperties(@TempDir Path 
project) throws Exception {
+        Path props = 
project.resolve("src/main/resources/application.properties");
+        Files.createDirectories(props.getParent());
+        Files.writeString(props, "camel.main.name = my-app\n");
+
+        assertThat(Run.resolveExistingProjectAppName(project, 
model("my-artifact"))).isEqualTo("my-app");
+    }
+
+    @Test
+    void appNameFallsBackToArtifactIdThenDirectoryName(@TempDir Path project) {
+        assertThat(Run.resolveExistingProjectAppName(project, 
model("my-artifact"))).isEqualTo("my-artifact");
+        assertThat(Run.resolveExistingProjectAppName(project, new Model()))
+                .isEqualTo(project.getFileName().toString());
+        assertThat(Run.resolveExistingProjectAppName(project, 
null)).isEqualTo(project.getFileName().toString());
+    }
+
+    @Test
+    void 
camelMainLogsToFileViaLog4j2ConfigurationOnTheMavenCommandLine(@TempDir Path 
project) throws Exception {
+        Path logConfig = 
project.resolve("target").resolve(Run.CAMEL_MAIN_RUN_LOG_CONFIG);
+        Path logFile = CommandLineHelper.getCamelDir().resolve("my-app.log");
+        Files.createDirectories(logFile.getParent());
+        Files.writeString(logFile, "stale output of a previous run");
+
+        Run.writeCamelMainRunLogConfig(logConfig, "my-app", "debug");
+
+        // the previous log is removed so the readers do not show stale lines
+        assertThat(logFile).doesNotExist();
+        String content = Files.readString(logConfig);
+        assertThat(content).contains("appender.file.fileName = " + 
logFile.toAbsolutePath().toString().replace("\\", "/"));
+        assertThat(content).contains("rootLogger.level = debug");
+        assertThat(content).doesNotContain("{{");
+
+        List<String> args = run("--profile=dev", "--port=9090", 
"--max-seconds=30", "--max-messages=5",
+                "--max-idle-seconds=10", "--prop=foo=bar", "--prop=-Dbar=baz", 
"pom.xml")
+                .buildExistingCamelMainSystemProperties("my-app", logConfig);
+        assertThat(args).containsExactly(
+                "-Dlog4j2.configurationFile=" + logConfig.toAbsolutePath(),
+                "-Dcamel.main.profile=dev",
+                "-Dcamel.main.name=my-app",
+                "-Dcamel.server.port=9090",
+                "-Dcamel.main.durationMaxSeconds=30",
+                "-Dcamel.main.durationMaxMessages=5",
+                "-Dcamel.main.durationMaxIdleSeconds=10",
+                "-Dfoo=bar",
+                "-Dbar=baz");
+    }
+
+    @Test
+    void camelMainDefaultsToInfoLevelAndNoProfileOrPort(@TempDir Path project) 
throws Exception {
+        Path logConfig = 
project.resolve("target").resolve(Run.CAMEL_MAIN_RUN_LOG_CONFIG);
+        Run.writeCamelMainRunLogConfig(logConfig, "my-app", null);
+        assertThat(Files.readString(logConfig)).contains("rootLogger.level = 
info");
+
+        // the default profile (dev) is passed, prod is not as it is the 
default of a Maven project
+        List<String> args = 
run("pom.xml").buildExistingCamelMainSystemProperties("my-app", logConfig);
+        assertThat(args).containsExactly(
+                "-Dlog4j2.configurationFile=" + logConfig.toAbsolutePath(),
+                "-Dcamel.main.profile=dev",
+                "-Dcamel.main.name=my-app");
+
+        args = run("--profile=prod", 
"pom.xml").buildExistingCamelMainSystemProperties("my-app", logConfig);
+        assertThat(args).containsExactly(
+                "-Dlog4j2.configurationFile=" + logConfig.toAbsolutePath(),
+                "-Dcamel.main.name=my-app");
+    }
+
+    @Test
+    void quarkusLogsToFileViaJvmArgs() {
+        String logFile = 
CommandLineHelper.getCamelDir().resolve("my-app.log").toAbsolutePath().toString()
+                .replace("\\", "/");
+
+        List<String> args = run("--profile=dev", "--port=9090", 
"--prop=foo=bar", "--jvm-args=-Xmx512m", "pom.xml")
+                .buildExistingQuarkusJvmArgs("my-app");
+
+        assertThat(args).containsExactly(
+                "-Dquarkus.log.file.enabled=true",
+                "-Dquarkus.log.file.path=" + logFile,
+                "-Dquarkus.log.file.format=\"" + Run.QUARKUS_LOG_FILE_FORMAT + 
"\"",
+                "-Dcamel.main.profile=dev",
+                "-Dcamel.main.name=my-app",
+                "-Dquarkus.http.port=9090",
+                "-Dfoo=bar",
+                "-Xmx512m");
+    }
+
+    @Test
+    void springBootLogsToFileViaLogbackConfiguration() {
+        List<String> args = run("--profile=dev", "--port=9090", 
"--prop=foo=bar", "--jvm-args=-Xmx512m", "pom.xml")
+                .buildExistingSpringBootJvmArgs();
+
+        assertThat(args).containsExactly(
+                "-Dlogging.config=classpath:logback-camel-jbang.xml",
+                "-Dcamel.main.profile=dev",
+                "-Dserver.port=9090",
+                "-Dfoo=bar",
+                "-Xmx512m");
+    }
+
+    @Test
+    void jfrIsPassedAsJvmArgument() {
+        List<String> args = run("--jfr", 
"pom.xml").buildExistingQuarkusJvmArgs("my-app");
+        assertThat(args).anyMatch(a -> 
a.startsWith("-XX:StartFlightRecording"));
+
+        args = run("--jfr", "pom.xml").buildExistingSpringBootJvmArgs();
+        assertThat(args).anyMatch(a -> 
a.startsWith("-XX:StartFlightRecording"));
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index 9454f5319860..dcaffdadce42 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -2385,7 +2385,6 @@ public class CamelMonitor extends CamelCommand {
             }
         } else {
             String pidStr = ctx.selectedPid;
-            actionsPopup.getLaunchManager().cleanupTempPom(pid);
             ProcessHandle.of(pid).ifPresent(ph -> {
                 if (forceKill) {
                     ph.destroyForcibly();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LaunchManager.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LaunchManager.java
index 96566fa3f099..b3566400313a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LaunchManager.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LaunchManager.java
@@ -16,15 +16,14 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
@@ -41,7 +40,6 @@ class LaunchManager {
 
     private final Supplier<List<InfraInfo>> infraServices;
     private final List<PendingLaunch> pendingLaunches = new ArrayList<>();
-    private final Map<Long, Path> activeTempPoms = new HashMap<>();
     private DeferredLaunch deferredLaunch;
     private volatile String pendingAutoSelect;
     private BiConsumer<String, Boolean> notificationCallback;
@@ -203,126 +201,30 @@ class LaunchManager {
         return false;
     }
 
+    /**
+     * Runs an existing Maven project via {@code camel run pom.xml}, which 
detects the runtime, injects the CLI
+     * connector, and logs to a file in {@code ~/.camel} that the Log tab 
reads (the same for all runtimes).
+     */
     void launchMavenProject(String dir, String projectType, String 
displayName, List<String> extraArgs) {
         try {
-            List<String> cmd = new ArrayList<>();
-            cmd.add(resolveMvnCommand(dir));
-            switch (projectType) {
-                case "spring-boot" -> cmd.add("spring-boot:run");
-                case "quarkus" -> cmd.add("quarkus:dev");
-                default -> cmd.add("camel:run");
-            }
-            // Translate Camel JBang args to Maven-compatible args
-            cmd.addAll(translateArgsForMaven(extraArgs, projectType));
-            // Inject camel-cli-connector if not already in the project
-            Path tempPom = null;
-            if ("spring-boot".equals(projectType)) {
-                tempPom = injectCliConnectorIfMissing(dir, cmd);
-            }
+            List<String> cmd = new 
ArrayList<>(LauncherHelper.getCamelCommand());
+            cmd.add("run");
+            cmd.add(Path.of(dir, "pom.xml").toString());
+            cmd.addAll(extraArgs);
             Path outputFile = createSecureTempFile("camel-maven-", ".log");
             outputFile.toFile().deleteOnExit();
             ProcessBuilder pb = new ProcessBuilder(cmd);
-            pb.directory(new java.io.File(dir));
+            pb.directory(new File(dir));
             pb.redirectErrorStream(true);
             pb.redirectOutput(outputFile.toFile());
             Process process = pb.start();
-            pendingLaunches.add(new PendingLaunch(displayName, process, 
outputFile, System.currentTimeMillis(), tempPom));
-            if (pendingAutoSelect == null) {
-                pendingAutoSelect = displayName;
-            }
-            notify("Starting: " + displayName + " (mvn " + cmd.get(1) + ")", 
false);
+            addPendingLaunch(displayName, process, outputFile);
+            notify("Starting: " + displayName + " (" + projectType + ")", 
false);
         } catch (Exception e) {
             notify("Failed to start Maven project: " + e.getMessage(), true);
         }
     }
 
-    private Path injectCliConnectorIfMissing(String dir, List<String> cmd) {
-        try {
-            Path pomFile = Path.of(dir, "pom.xml");
-            if (!Files.isRegularFile(pomFile)) {
-                return null;
-            }
-            String pomContent = Files.readString(pomFile);
-            if (pomContent.contains("camel-cli-connector")) {
-                return null;
-            }
-            // Add cli-connector-starter dependency (version managed by BOM)
-            String dep = "\n        <dependency>\n"
-                         + "            
<groupId>org.apache.camel.springboot</groupId>\n"
-                         + "            
<artifactId>camel-cli-connector-starter</artifactId>\n"
-                         + "        </dependency>";
-            // Find the project-level </dependencies> (not inside 
dependencyManagement or plugins)
-            int insertIdx = findProjectDependenciesEnd(pomContent);
-            if (insertIdx < 0) {
-                return null;
-            }
-            String modified = pomContent.substring(0, insertIdx) + dep + "\n   
 " + pomContent.substring(insertIdx);
-            // Write temp pom in the project dir so Maven can find sources
-            Path tempPom = Path.of(dir, ".camel-tui-pom.xml");
-            Files.writeString(tempPom, modified);
-            cmd.add("-f");
-            cmd.add(tempPom.getFileName().toString());
-            return tempPom;
-        } catch (Exception e) {
-            // best effort — don't fail the launch
-            return null;
-        }
-    }
-
-    private static int findProjectDependenciesEnd(String pom) {
-        // Find <dependencies> that is a direct child of <project>,
-        // not nested inside <dependencyManagement>, <plugin>, or <profile>
-        int dmStart = pom.indexOf("<dependencyManagement>");
-        int dmEnd = dmStart >= 0 ? pom.indexOf("</dependencyManagement>", 
dmStart) : -1;
-        int buildStart = pom.indexOf("<build>");
-
-        int searchFrom = 0;
-        while (true) {
-            int depStart = pom.indexOf("<dependencies>", searchFrom);
-            if (depStart < 0) {
-                return -1;
-            }
-            // Skip if inside <dependencyManagement>
-            if (dmStart >= 0 && depStart > dmStart && (dmEnd < 0 || depStart < 
dmEnd)) {
-                searchFrom = dmEnd > 0 ? dmEnd : depStart + 14;
-                continue;
-            }
-            // Skip if inside <build> (plugins can have dependencies)
-            if (buildStart >= 0 && depStart > buildStart) {
-                searchFrom = depStart + 14;
-                continue;
-            }
-            int depEnd = pom.indexOf("</dependencies>", depStart);
-            return depEnd >= 0 ? depEnd : -1;
-        }
-    }
-
-    private static Path writeSpringBootLogbackConfig() {
-        try {
-            Path camelDir = Path.of(System.getProperty("user.home"), ".camel");
-            Files.createDirectories(camelDir);
-            Path logbackFile = 
camelDir.resolve(".tui-logback-spring-boot.xml");
-            String config = """
-                    <?xml version="1.0" encoding="UTF-8"?>
-                    <configuration>
-                        <include 
resource="org/springframework/boot/logging/logback/defaults.xml"/>
-                        <include 
resource="org/springframework/boot/logging/logback/console-appender.xml"/>
-                        <include 
resource="org/springframework/boot/logging/logback/file-appender.xml"/>
-                        <property name="LOG_FILE" 
value="${user.home}${file.separator}.camel${file.separator}${PID}.log"/>
-                        <property name="FILE_LOG_PATTERN" 
value="${CONSOLE_LOG_PATTERN}"/>
-                        <root level="INFO">
-                            <appender-ref ref="CONSOLE"/>
-                            <appender-ref ref="FILE"/>
-                        </root>
-                    </configuration>
-                    """;
-            Files.writeString(logbackFile, config);
-            return logbackFile;
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
     void launchCamelRun(String sourceDir, String displayName, List<String> 
extraArgs) {
         try {
             List<String> cmd = new 
ArrayList<>(LauncherHelper.getCamelCommand());
@@ -343,67 +245,6 @@ class LaunchManager {
         }
     }
 
-    private static String resolveMvnCommand(String dir) {
-        String wrapper = System.getProperty("os.name", 
"").toLowerCase().contains("win") ? "mvnw.cmd" : "./mvnw";
-        Path wrapperPath = Path.of(dir).resolve(wrapper.startsWith("./") ? 
wrapper.substring(2) : wrapper);
-        if (Files.isRegularFile(wrapperPath)) {
-            return wrapperPath.toString();
-        }
-        return "mvn";
-    }
-
-    static List<String> translateArgsForMaven(List<String> extraArgs, String 
projectType) {
-        List<String> mvnArgs = new ArrayList<>();
-        StringBuilder jvmArgs = new StringBuilder();
-        if ("spring-boot".equals(projectType)) {
-            Path logbackFile = writeSpringBootLogbackConfig();
-            if (logbackFile != null) {
-                jvmArgs.append("-Dlogging.config=file:").append(logbackFile);
-            }
-        }
-        for (String arg : extraArgs) {
-            if (arg.startsWith("--prop=")) {
-                String kv = arg.substring("--prop=".length());
-                mvnArgs.add("-D" + kv);
-            } else if (arg.startsWith("--port=")) {
-                String port = arg.substring("--port=".length());
-                if ("spring-boot".equals(projectType)) {
-                    mvnArgs.add("-Dserver.port=" + port);
-                } else if ("quarkus".equals(projectType)) {
-                    mvnArgs.add("-Dquarkus.http.port=" + port);
-                }
-            } else if (arg.startsWith("--profile=")) {
-                String profile = arg.substring("--profile=".length());
-                if (!"prod".equals(profile)) {
-                    if (!jvmArgs.isEmpty()) {
-                        jvmArgs.append(" ");
-                    }
-                    jvmArgs.append("-Dcamel.main.profile=").append(profile);
-                }
-            } else if (arg.startsWith("--jvm-args=")) {
-                String extra = arg.substring("--jvm-args=".length()).trim();
-                if (!extra.isEmpty()) {
-                    if (!jvmArgs.isEmpty()) {
-                        jvmArgs.append(" ");
-                    }
-                    jvmArgs.append(extra);
-                }
-            }
-            // other Camel JBang flags (--name, --runtime, --dev, --observe, 
etc.)
-            // are not applicable to Maven and are silently dropped
-        }
-        if (!jvmArgs.isEmpty()) {
-            if ("spring-boot".equals(projectType)) {
-                mvnArgs.add("-Dspring-boot.run.jvmArguments=" + jvmArgs);
-            } else if ("quarkus".equals(projectType)) {
-                mvnArgs.add("-Djvm.args=" + jvmArgs);
-            } else {
-                mvnArgs.add("-Dcamel.jvmArgs=" + jvmArgs);
-            }
-        }
-        return mvnArgs;
-    }
-
     private void checkDeferredLaunch(long now) {
         if (deferredLaunch != null) {
             Set<String> runningAliases = infraServices.get().stream()
@@ -434,50 +275,21 @@ class LaunchManager {
                         failureLogCallback.accept(pl.name(), pl.outputFile());
                     }
                 }
-                cleanupTempPom(pl);
                 it.remove();
             } else if (now - pl.startTime() > 8000) {
                 notify("Started: " + pl.name(), false);
-                // keep temp pom reference for cleanup when process stops
-                if (pl.tempPom() != null) {
-                    activeTempPoms.put(pl.process().pid(), pl.tempPom());
-                }
                 it.remove();
             }
         }
     }
 
-    void cleanupTempPom(long pid) {
-        Path tempPom = activeTempPoms.remove(pid);
-        if (tempPom != null) {
-            try {
-                Files.deleteIfExists(tempPom);
-            } catch (Exception e) {
-                // best effort
-            }
-        }
-    }
-
-    private static void cleanupTempPom(PendingLaunch pl) {
-        if (pl.tempPom() != null) {
-            try {
-                Files.deleteIfExists(pl.tempPom());
-            } catch (Exception e) {
-                // best effort
-            }
-        }
-    }
-
     private void notify(String msg, boolean error) {
         if (notificationCallback != null) {
             notificationCallback.accept(msg, error);
         }
     }
 
-    private record PendingLaunch(String name, Process process, Path 
outputFile, long startTime, Path tempPom) {
-        PendingLaunch(String name, Process process, Path outputFile, long 
startTime) {
-            this(name, process, outputFile, startTime, null);
-        }
+    private record PendingLaunch(String name, Process process, Path 
outputFile, long startTime) {
     }
 
     private record DeferredLaunch(

Reply via email to