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();
