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

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

commit 526eda2e0e5a16d252ff1eabcae8d0d38796a997
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 7 14:27:33 2026 +0200

    CAMEL-25417: camel-jbang - An opened folder run with F10 is linked to its 
app by the launched process
    
    An opened project was linked to its running app by directory only. A
    folder of route files runs from the working directory of the monitor, or
    from a copy in .camel-jbang-run when a runtime is chosen, so its app does
    not report the project folder, and the project stayed listed as Starting
    next to it. The monitor now keeps the process F10 launched, and the app
    whose pid is that process or a descendant of it stands in for the project.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
---
 .../dsl/jbang/core/commands/tui/ActionsPopup.java  |  9 ++++----
 .../core/commands/tui/DataRefreshService.java      | 24 ++++++++++++++++++++
 .../jbang/core/commands/tui/IntegrationInfo.java   |  2 ++
 .../dsl/jbang/core/commands/tui/LaunchManager.java | 15 +++++++++++--
 .../commands/tui/OpenedProjectLifecycleTest.java   | 26 ++++++++++++++++++++++
 5 files changed, 69 insertions(+), 7 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
index 000c2c44fa3c..da1cd007a013 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
@@ -1326,11 +1326,10 @@ class ActionsPopup {
         // the project stays listed (Starting) until its app shows up and 
stands in for it
         phantom.startingSince = System.currentTimeMillis();
 
-        if (phantom.projectType != null) {
-            launchManager.launchMavenProject(phantom.sourceDir, 
phantom.projectType, displayName, extraArgs);
-        } else {
-            launchManager.launchCamelRun(phantom.sourceDir, displayName, 
extraArgs);
-        }
+        // the app is found by the process it runs in: a folder runs from 
elsewhere, and reports another directory
+        phantom.launchedProcess = phantom.projectType != null
+                ? launchManager.launchMavenProject(phantom.sourceDir, 
phantom.projectType, displayName, extraArgs)
+                : launchManager.launchCamelRun(phantom.sourceDir, displayName, 
extraArgs);
     }
 
     // ---- Name Input ----
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java
index 789b2f7ad14b..426a9acbc3d1 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java
@@ -409,6 +409,9 @@ class DataRefreshService {
                 .collect(Collectors.toMap(i -> i.directory, i -> i, (a, b) -> 
a.vanishing ? b : a));
         for (IntegrationInfo phantom : phantoms) {
             IntegrationInfo live = phantom.sourceDir != null ? 
liveDirs.get(phantom.sourceDir) : null;
+            if (live == null) {
+                live = launchedApp(phantom, infos);
+            }
             if (live != null) {
                 if (phantom.pid.equals(ctx.selectedPid) && !live.vanishing) {
                     ctx.selectedPid = live.pid;
@@ -428,6 +431,27 @@ class DataRefreshService {
         }
     }
 
+    /**
+     * The app F10 launched for the project: the launched process or a 
descendant of it. A folder of route files runs
+     * from the working directory of the monitor, or from a copy in 
.camel-jbang-run when a runtime is chosen, so its
+     * app does not report the project folder as its directory (CAMEL-25417).
+     */
+    static IntegrationInfo launchedApp(IntegrationInfo phantom, 
List<IntegrationInfo> infos) {
+        ProcessHandle launched = phantom.launchedProcess;
+        if (launched == null) {
+            return null;
+        }
+        java.util.Set<String> pids = new java.util.HashSet<>();
+        pids.add(Long.toString(launched.pid()));
+        launched.descendants().forEach(p -> pids.add(Long.toString(p.pid())));
+        for (IntegrationInfo info : infos) {
+            if (!info.phantom && info.pid != null && pids.contains(info.pid)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
     private void mergePhantoms(List<IntegrationInfo> infos) {
         mergePhantoms(infos, ctx.phantomIntegrations, ctx);
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
index e2bdef4aec27..6d8b42ff28ee 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
@@ -39,6 +39,8 @@ class IntegrationInfo {
     String openedAs;
     // an opened project: when it was asked to run, until its app shows up (or 
the launch fails)
     long startingSince;
+    // the process F10 launched for an opened project: its app is that process 
or a descendant of it (CAMEL-25417)
+    ProcessHandle launchedProcess;
     String projectType;
     String sourceDir;
     String ready;
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 d9a53829aa90..574c0c4c0e4f 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
@@ -284,8 +284,10 @@ class LaunchManager {
     /**
      * 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).
+     *
+     * @return the launched process, or null when it did not start
      */
-    void launchMavenProject(String dir, String projectType, String 
displayName, List<String> extraArgs) {
+    ProcessHandle launchMavenProject(String dir, String projectType, String 
displayName, List<String> extraArgs) {
         try {
             List<String> cmd = new 
ArrayList<>(LauncherHelper.getCamelCommand());
             cmd.add("run");
@@ -300,12 +302,19 @@ class LaunchManager {
             Process process = pb.start();
             addPendingLaunch(displayName, process, outputFile);
             notify("Starting: " + displayName + " (" + projectType + ")", 
false);
+            return process.toHandle();
         } catch (Exception e) {
             notify("Failed to start Maven project: " + e.getMessage(), true);
+            return null;
         }
     }
 
-    void launchCamelRun(String sourceDir, String displayName, List<String> 
extraArgs) {
+    /**
+     * Runs a folder of route files with camel run --source-dir.
+     *
+     * @return the launched process, or null when it did not start
+     */
+    ProcessHandle launchCamelRun(String sourceDir, String displayName, 
List<String> extraArgs) {
         try {
             List<String> cmd = new 
ArrayList<>(LauncherHelper.getCamelCommand());
             cmd.add("run");
@@ -320,8 +329,10 @@ class LaunchManager {
             Process process = pb.start();
             addPendingLaunch(displayName, process, outputFile);
             notify("Starting: " + displayName, false);
+            return process.toHandle();
         } catch (Exception e) {
             notify("Failed to start: " + sourceDir + " - " + e.getMessage(), 
true);
+            return null;
         }
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OpenedProjectLifecycleTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OpenedProjectLifecycleTest.java
index 763abd87f5c8..90c51c49ff98 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OpenedProjectLifecycleTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OpenedProjectLifecycleTest.java
@@ -66,6 +66,32 @@ class OpenedProjectLifecycleTest {
         assertEquals(project.pid, ctx.selectedPid);
     }
 
+    @Test
+    void aFolderRunWithF10IsFoundByItsProcess() {
+        // CAMEL-25417: a folder of route files runs from the working 
directory of the monitor (or from a copy in
+        // .camel-jbang-run), so its app does not report the project folder: 
it is found by the launched process
+        IntegrationInfo project = new IntegrationInfo();
+        project.name = "custom-kamelet";
+        project.sourceDir = "/work/custom-kamelet";
+        project.startingSince = System.currentTimeMillis();
+        project.launchedProcess = ProcessHandle.current();
+        ctx.addPhantom(project);
+        ctx.selectedPid = project.pid;
+
+        IntegrationInfo app = 
running(Long.toString(ProcessHandle.current().pid()), "custom-kamelet",
+                "/home/me/.camel-jbang-run/1791375674390");
+        List<IntegrationInfo> infos = merge(app);
+        assertFalse(infos.contains(project));
+        assertEquals(app.pid, ctx.selectedPid);
+        assertEquals("custom-kamelet", app.openedAs);
+        assertEquals(0, project.startingSince);
+
+        // another app with the same name, not launched for the project, does 
not stand in for it
+        IntegrationInfo other = running("4242", "custom-kamelet", 
"/elsewhere");
+        project.launchedProcess = null;
+        assertTrue(merge(other).contains(project));
+    }
+
     @Test
     void aProjectIsStartingUntilItsAppShowsUp() {
         IntegrationInfo project = new IntegrationInfo();

Reply via email to