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 ef288e71825e camel-java-io - Java DSL dump includes sourceLineNumber 
comments for TUI node selection
ef288e71825e is described below

commit ef288e71825e340b1feaf7fd5e3f31d4f4f81e63
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jul 24 15:02:38 2026 +0200

    camel-java-io - Java DSL dump includes sourceLineNumber comments for TUI 
node selection
    
    camel-jbang - Fix TUI action file IPC race condition and use background 
thread pool
    
    Use a dedicated cached thread pool (backgroundExecutor) instead of the
    single-threaded TUI scheduler for background tasks, preventing animation
    freezes during data loading. Serialize action file IPC with per-PID locks
    to prevent concurrent background threads from overwriting each other's
    action/output files. Centralize IPC in MonitorContext.executeAction() and
    fireAction() methods used by all 18+ tab/popup classes.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/spi/ModelToJavaDumper.java    | 18 ++++++
 .../camel/impl/console/RouteDumpDevConsole.java    | 13 ++++-
 .../org/apache/camel/java/LwModelToJavaDumper.java | 29 +++++++++-
 .../camel/java/out/JavaDslModelWriterSupport.java  | 34 ++++++++++-
 .../api/management/mbean/ManagedRouteMBean.java    |  3 +
 .../camel/management/mbean/ManagedRoute.java       |  8 ++-
 .../dsl/jbang/core/commands/tui/ActionsPopup.java  |  8 +--
 .../dsl/jbang/core/commands/tui/BeansTab.java      | 13 +----
 .../dsl/jbang/core/commands/tui/BrowseTab.java     | 24 ++------
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 14 ++---
 .../dsl/jbang/core/commands/tui/CatalogTab.java    | 10 +---
 .../dsl/jbang/core/commands/tui/ClasspathTab.java  | 13 +----
 .../dsl/jbang/core/commands/tui/CveAuditTab.java   |  2 +-
 .../core/commands/tui/DataRefreshService.java      | 13 +----
 .../jbang/core/commands/tui/DiagramSupport.java    | 27 +--------
 .../dsl/jbang/core/commands/tui/DiagramTab.java    | 17 +-----
 .../jbang/core/commands/tui/DocViewerPopup.java    |  7 +--
 .../dsl/jbang/core/commands/tui/ErrorsTab.java     |  2 +-
 .../jbang/core/commands/tui/HeapHistogramTab.java  | 22 +-------
 .../dsl/jbang/core/commands/tui/HistoryTab.java    |  2 +-
 .../camel/dsl/jbang/core/commands/tui/HttpTab.java | 14 +----
 .../camel/dsl/jbang/core/commands/tui/LogTab.java  |  3 +-
 .../core/commands/tui/MavenDependenciesTab.java    |  4 +-
 .../dsl/jbang/core/commands/tui/McpFacade.java     | 22 +-------
 .../dsl/jbang/core/commands/tui/MemoryLeakTab.java | 65 +++-------------------
 .../dsl/jbang/core/commands/tui/MemoryTab.java     |  5 +-
 .../dsl/jbang/core/commands/tui/MetricsTab.java    |  2 +-
 .../jbang/core/commands/tui/MonitorContext.java    | 35 ++++++++++++
 .../dsl/jbang/core/commands/tui/RoutesTab.java     | 20 ++-----
 .../jbang/core/commands/tui/SendMessagePopup.java  | 19 ++-----
 .../dsl/jbang/core/commands/tui/SourceViewer.java  | 25 ++-------
 .../dsl/jbang/core/commands/tui/SqlQueryTab.java   | 24 ++------
 .../dsl/jbang/core/commands/tui/StartupTab.java    | 13 +----
 .../dsl/jbang/core/commands/tui/ThreadsTab.java    | 13 +----
 .../dsl/jbang/core/commands/tui/TuiHelper.java     |  8 +--
 35 files changed, 208 insertions(+), 343 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToJavaDumper.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToJavaDumper.java
index 3d76721f7217..7622bdb7de05 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToJavaDumper.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToJavaDumper.java
@@ -68,4 +68,22 @@ public interface ModelToJavaDumper {
             boolean resolvePlaceholders, boolean generatedIds)
             throws Exception;
 
+    /**
+     * Dumps the definition as Java DSL
+     *
+     * @param  context             the CamelContext
+     * @param  definition          the definition, such as a {@link NamedNode}
+     * @param  resolvePlaceholders whether to resolve property placeholders in 
the dumped Java DSL
+     * @param  generatedIds        whether to include auto generated IDs
+     * @param  sourceLocation      whether to include source location 
information as comments
+     * @return                     the output in Java DSL (is formatted)
+     * @throws Exception           is throw if error marshalling to Java DSL
+     */
+    default String dumpModelAsJava(
+            CamelContext context, NamedNode definition,
+            boolean resolvePlaceholders, boolean generatedIds, boolean 
sourceLocation)
+            throws Exception {
+        return dumpModelAsJava(context, definition, resolvePlaceholders, 
generatedIds);
+    }
+
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
index 3d4447d9b4cf..fb42ea5e9ea7 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
@@ -132,7 +132,7 @@ public class RouteDumpDevConsole extends AbstractDevConsole 
{
                     dump = mrb.dumpRouteAsYaml(true, uriAsParameters, false, 
true);
                 } else if ("java".equals(format)) {
                     jo.put("format", "java");
-                    dump = mrb.dumpRouteAsJava(true, false);
+                    dump = mrb.dumpRouteAsJava(true, false, true);
                 }
                 if (dump != null) {
                     JsonArray code;
@@ -261,6 +261,17 @@ public class RouteDumpDevConsole extends 
AbstractDevConsole {
             // ignore
         }
 
+        // merge trailing ; with previous code line (sourceLineNumber comments 
may have separated them)
+        if (code.size() > 1) {
+            JsonObject last = (JsonObject) code.get(code.size() - 1);
+            String lastCode = Jsoner.unescape(last.getString("code")).trim();
+            if (";".equals(lastCode)) {
+                code.remove(code.size() - 1);
+                JsonObject prev = (JsonObject) code.get(code.size() - 1);
+                prev.put("code", prev.getString("code") + ";");
+            }
+        }
+
         return code.isEmpty() ? null : code;
     }
 
diff --git 
a/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
 
b/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
index e2e4131f829d..2ca544b1c848 100644
--- 
a/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
+++ 
b/core/camel-java-io/src/main/java/org/apache/camel/java/LwModelToJavaDumper.java
@@ -62,6 +62,14 @@ public class LwModelToJavaDumper implements 
ModelToJavaDumper {
             CamelContext context, NamedNode definition,
             boolean resolvePlaceholders, boolean generatedIds)
             throws Exception {
+        return dumpModelAsJava(context, definition, resolvePlaceholders, 
generatedIds, false);
+    }
+
+    @Override
+    public String dumpModelAsJava(
+            CamelContext context, NamedNode definition,
+            boolean resolvePlaceholders, boolean generatedIds, boolean 
sourceLocation)
+            throws Exception {
 
         Properties properties = new Properties();
         List<Runnable> restorers = new ArrayList<>();
@@ -79,7 +87,9 @@ public class LwModelToJavaDumper implements ModelToJavaDumper 
{
             restorers.add(resolveEndpointDslUris(route));
         }
 
-        org.apache.camel.java.out.JavaDslModelWriter writer = new 
org.apache.camel.java.out.JavaDslModelWriter();
+        org.apache.camel.java.out.JavaDslModelWriter writer = 
createWriter(sourceLocation);
+        writer.setGeneratedIds(generatedIds);
+        writer.setSourceLocation(sourceLocation);
 
         StringBuilder sb = new StringBuilder();
         try {
@@ -148,6 +158,23 @@ public class LwModelToJavaDumper implements 
ModelToJavaDumper {
         return result;
     }
 
+    private static org.apache.camel.java.out.JavaDslModelWriter 
createWriter(boolean sourceLocation) {
+        if (!sourceLocation) {
+            return new org.apache.camel.java.out.JavaDslModelWriter();
+        }
+        return new org.apache.camel.java.out.JavaDslModelWriter() {
+            @Override
+            protected void doWriteOptionalIdentifiedDefinitionAttributes(
+                    StringBuilder sb, 
org.apache.camel.model.OptionalIdentifiedDefinition<?> def) {
+                int line = (def instanceof RouteDefinition rd ? rd.getInput() 
: def).getLineNumber();
+                if (line != -1) {
+                    sb.append("\n// sourceLineNumber: ").append(line);
+                }
+                super.doWriteOptionalIdentifiedDefinitionAttributes(sb, def);
+            }
+        };
+    }
+
     private static void collectTemplateProperties(RouteTemplateDefinition 
template, Properties properties) {
         if (Boolean.TRUE.equals(template.getRoute().isTemplate())) {
             java.util.Map<String, Object> parameters = 
template.getRoute().getTemplateParameters();
diff --git 
a/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
 
b/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
index 57b6a51df69a..11ab52312bec 100644
--- 
a/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
+++ 
b/core/camel-java-io/src/main/java/org/apache/camel/java/out/JavaDslModelWriterSupport.java
@@ -152,10 +152,25 @@ public abstract class JavaDslModelWriterSupport {
     private boolean inDataFormatBuilder;
     private boolean inSubBuilder;
     private boolean inRestParam;
+    private boolean generatedIds;
+    private boolean sourceLocation;
+    private boolean currentNodeCustomId;
 
     protected int indentLevel = 1;
     protected final Set<String> handledAttributes = new HashSet<>();
 
+    public void setGeneratedIds(boolean generatedIds) {
+        this.generatedIds = generatedIds;
+    }
+
+    public void setSourceLocation(boolean sourceLocation) {
+        this.sourceLocation = sourceLocation;
+    }
+
+    public boolean isSourceLocation() {
+        return sourceLocation;
+    }
+
     protected void resetState() {
         indentLevel = 1;
         handledAttributes.clear();
@@ -186,13 +201,19 @@ public abstract class JavaDslModelWriterSupport {
 
         if (def.getInput() != null) {
             
sb.append("from(").append(quote(def.getInput().getUri())).append(")");
+            if (sourceLocation) {
+                int line = def.getInput().getLineNumber();
+                if (line != -1) {
+                    sb.append(NL).append("// sourceLineNumber: ").append(line);
+                }
+            }
         }
         handledAttributes.add("input");
         handledAttributes.add("uri");
-        if (def.getId() != null) {
+        if (def.getId() != null && (generatedIds || 
Boolean.TRUE.equals(def.getCustomId()))) {
             
sb.append(NL).append(indent()).append(".routeId(").append(quote(def.getId())).append(")");
-            handledAttributes.add("id");
         }
+        handledAttributes.add("id");
         handledAttributes.add("customId");
         if (def.getRouteProperties() != null) {
             for (PropertyDefinition prop : def.getRouteProperties()) {
@@ -206,6 +227,9 @@ public abstract class JavaDslModelWriterSupport {
         indentLevel--;
         doWriteRouteDefinition(sb, def);
         indentLevel++;
+        if (sourceLocation) {
+            sb.append(NL);
+        }
         sb.append(";");
     }
 
@@ -482,6 +506,12 @@ public abstract class JavaDslModelWriterSupport {
 
     protected void doWriteAttribute(StringBuilder sb, String key, String 
value, String defaultValue) {
         if (value == null || handledAttributes.contains(key) || 
SKIP_ATTRIBUTES.contains(key)) {
+            if ("customId".equals(key)) {
+                currentNodeCustomId = "true".equals(value);
+            }
+            return;
+        }
+        if ("id".equals(key) && !generatedIds && !currentNodeCustomId) {
             return;
         }
         if (defaultValue != null && defaultValue.equals(value)) {
diff --git 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
index 90473b64a932..aced2e336c66 100644
--- 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
+++ 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
@@ -161,6 +161,9 @@ public interface ManagedRouteMBean extends 
ManagedPerformanceCounterMBean {
     @ManagedOperation(description = "Dumps the route as Java DSL")
     String dumpRouteAsJava(boolean resolvePlaceholders, boolean generatedIds) 
throws Exception;
 
+    @ManagedOperation(description = "Dumps the route as Java DSL")
+    String dumpRouteAsJava(boolean resolvePlaceholders, boolean generatedIds, 
boolean sourceLocation) throws Exception;
+
     @ManagedOperation(description = "Dumps the route stats as XML")
     String dumpRouteStatsAsXml(boolean fullStats, boolean includeProcessors) 
throws Exception;
 
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
index e64d74cd4882..49775a6e3976 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
@@ -496,11 +496,17 @@ public class ManagedRoute extends 
ManagedPerformanceCounter implements ManagedRo
 
     @Override
     public String dumpRouteAsJava(boolean resolvePlaceholders, boolean 
generatedIds) throws Exception {
+        return dumpRouteAsJava(resolvePlaceholders, generatedIds, false);
+    }
+
+    @Override
+    public String dumpRouteAsJava(boolean resolvePlaceholders, boolean 
generatedIds, boolean sourceLocation)
+            throws Exception {
         String id = route.getId();
         RouteDefinition def = 
context.getCamelContextExtension().getContextPlugin(Model.class).getRouteDefinition(id);
         if (def != null) {
             return 
PluginHelper.getModelToJavaDumper(context).dumpModelAsJava(context, def, 
resolvePlaceholders,
-                    generatedIds);
+                    generatedIds, sourceLocation);
         }
 
         return null;
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 06005dc5b830..dc4a1e8d4c79 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
@@ -20,7 +20,6 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.BiConsumer;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
@@ -130,7 +129,6 @@ class ActionsPopup {
     private final SendMessagePopup sendMessagePopup = new SendMessagePopup();
     private final StopAllPopup stopAllPopup;
     private final CaptionOverlay captionOverlay;
-    private ScheduledExecutorService scheduler;
 
     private final LaunchManager launchManager;
     private BiConsumer<String, Boolean> notificationCallback;
@@ -176,10 +174,6 @@ class ActionsPopup {
         return launchManager;
     }
 
-    void setScheduler(ScheduledExecutorService scheduler) {
-        this.scheduler = scheduler;
-    }
-
     void setPreSelectedRouteId(String routeId) {
         this.preSelectedRouteId = routeId;
     }
@@ -487,7 +481,7 @@ class ActionsPopup {
     boolean handleKeyEvent(KeyEvent ke) {
         if (sendMessagePopup.isVisible()) {
             if (ke.isKey(KeyCode.F5) && !sendFileBrowser.isVisible()) {
-                sendMessagePopup.doSend(ctx, scheduler);
+                sendMessagePopup.doSend(ctx, ctx.backgroundExecutor);
             } else {
                 sendMessagePopup.handleKeyEvent(ke);
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BeansTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BeansTab.java
index 39248fcdfd19..6ff0dde6a8c4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BeansTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BeansTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -41,7 +40,6 @@ import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -390,7 +388,7 @@ class BeansTab extends AbstractTableTab {
             return;
         }
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadBeansInBackground(pid);
             } finally {
@@ -400,20 +398,13 @@ class BeansTab extends AbstractTableTab {
     }
 
     private void loadBeansInBackground(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "bean");
         root.put("properties", true);
         root.put("nulls", true);
         root.put("internal", true);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BrowseTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BrowseTab.java
index cd1d621c246c..52ba8db3713f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BrowseTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/BrowseTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -49,7 +48,6 @@ import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import dev.tamboui.widgets.table.TableState;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -534,7 +532,7 @@ class BrowseTab extends AbstractTab {
             return;
         }
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadEndpointsInBackground(pid);
             } finally {
@@ -544,20 +542,13 @@ class BrowseTab extends AbstractTab {
     }
 
     private void loadEndpointsInBackground(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "browse");
         root.put("filter", "*");
         root.put("limit", 100);
         root.put("dump", false);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
@@ -595,7 +586,7 @@ class BrowseTab extends AbstractTab {
             return;
         }
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadMessagesInBackground(pid, endpointUri);
             } finally {
@@ -605,9 +596,6 @@ class BrowseTab extends AbstractTab {
     }
 
     private void loadMessagesInBackground(String pid, String endpointUri) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "browse");
         root.put("filter", endpointUri);
@@ -615,11 +603,7 @@ class BrowseTab extends AbstractTab {
         root.put("dump", true);
         root.put("includeBody", true);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
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 7dc8be3d826c..7d4477a05f3a 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
@@ -500,7 +500,6 @@ public class CamelMonitor extends CamelCommand {
             this.runner = tui;
             aiPanel.setExitCallbackForTestingOrRuntime(tui::quit);
             ctx.runner = tui;
-            actionsPopup.setScheduler(tui.scheduler());
             actionsPopup.setResetScreenAction(() -> tui.terminal().clear());
             actionsPopup.setThemeRefreshAction(() -> tui.terminal().clear());
             actionsPopup.setClearScreenAction(() -> tui.terminal().clear());
@@ -521,6 +520,7 @@ public class CamelMonitor extends CamelCommand {
         } finally {
             shellPanel.destroy();
             aiPanel.destroy();
+            ctx.backgroundExecutor.shutdownNow();
             if (mcpServer != null) {
                 mcpServer.stop();
             }
@@ -1759,9 +1759,11 @@ public class CamelMonitor extends CamelCommand {
         ph.destroy();
         setNotification("Restarting: " + name, false);
 
-        // re-launch in background after process terminates
+        // re-launch in background after process terminates (use the 
background executor
+        // instead of runner.scheduler() — the scheduler is single-threaded 
and generates
+        // tick events; blocking it would freeze the notification wave 
animation)
         if (runner != null) {
-            runner.scheduler().execute(() -> {
+            ctx.backgroundExecutor.execute(() -> {
                 try {
                     // wait for termination (max 10 seconds, then force kill)
                     CompletableFuture<ProcessHandle> exitFuture = 
ph.onExit().toCompletableFuture();
@@ -1851,8 +1853,7 @@ public class CamelMonitor extends CamelCommand {
         String pid = info.pid;
         JsonObject root = new JsonObject();
         root.put("action", "reset-stats");
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
+        ctx.fireAction(pid, root);
         dataService.metrics().resetStats(pid);
 
         info.activity = List.of();
@@ -1898,8 +1899,7 @@ public class CamelMonitor extends CamelCommand {
         root.put("action", "route");
         root.put("id", routeId);
         root.put("command", command);
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
+        ctx.fireAction(pid, root);
     }
 
     private void renderFooter(Frame frame, Rect area) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
index 6c34acfb0ed6..9b412effeec8 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -44,7 +43,6 @@ import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.tooling.model.EipModel;
 import org.apache.camel.util.json.JsonArray;
@@ -462,7 +460,7 @@ class CatalogTab extends AbstractTableTab {
         }
 
         boolean full = fullCatalog;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 Set<String> appArtifacts = null;
                 if (!full) {
@@ -554,18 +552,14 @@ class CatalogTab extends AbstractTableTab {
                 eipNames = new HashSet<>(catalog.findModelNames());
             } else {
                 eipNames = new HashSet<>();
-                Path outputFile = ctx.getOutputFile(pid);
-                PathUtils.deleteFile(outputFile);
 
                 JsonObject root = new JsonObject();
                 root.put("action", "route-structure");
                 root.put("filter", "*");
                 root.put("brief", true);
                 root.put("metric", false);
-                PathUtils.writeTextSafely(root.toJson(), 
ctx.getActionFile(pid));
 
-                JsonObject jo = TuiHelper.pollJsonResponse(outputFile, 5000);
-                PathUtils.deleteFile(outputFile);
+                JsonObject jo = ctx.executeAction(pid, root, 5000);
                 if (jo == null) {
                     return;
                 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
index 1df66be3e012..43bee09c26db 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -43,7 +42,6 @@ import dev.tamboui.widgets.list.ScrollMode;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.scrollbar.Scrollbar;
 import dev.tamboui.widgets.scrollbar.ScrollbarState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -302,19 +300,12 @@ class ClasspathTab extends AbstractTab {
         }
 
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
-                Path outputFile = ctx.getOutputFile(pid);
-                PathUtils.deleteFile(outputFile);
-
                 JsonObject action = new JsonObject();
                 action.put("action", "jvm");
 
-                Path actionFile = ctx.getActionFile(pid);
-                PathUtils.writeTextSafely(action.toJson(), actionFile);
-
-                JsonObject response = pollJsonResponse(outputFile, 5000);
-                PathUtils.deleteFile(outputFile);
+                JsonObject response = ctx.executeAction(pid, action, 5000);
 
                 if (response == null) {
                     applyResult(Collections.emptyList(), "No response from 
integration");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java
index 201bbaac1b4d..9e4bc931196e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java
@@ -384,7 +384,7 @@ class CveAuditTab extends AbstractTableTab {
             return;
         }
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 DependencyLoader.LoadResult loadResult = 
DependencyLoader.loadDependencies(info);
                 if (loadResult.error() != null && 
loadResult.entries().isEmpty()) {
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 b4396dbec667..bb635f6b27ad 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
@@ -35,7 +35,6 @@ import java.util.stream.Collectors;
 
 import dev.tamboui.tui.TuiRunner;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
@@ -186,7 +185,7 @@ class DataRefreshService {
             return;
         }
         lastRefresh = System.currentTimeMillis();
-        runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 refreshSync(logRefresher, conditionalRefresher);
             } finally {
@@ -564,19 +563,12 @@ class DataRefreshService {
             return;
         }
         try {
-            // Send action to request span data
-            Path outputFile = ctx.getOutputFile(pid);
-            PathUtils.deleteFile(outputFile);
-
             JsonObject action = new JsonObject();
             action.put("action", "span");
             action.put("dump", "true");
             action.put("limit", "500");
-            Path actionFile = ctx.getActionFile(pid);
-            PathUtils.writeTextSafely(action.toJson(), actionFile);
 
-            // Poll for response
-            JsonObject response = TuiHelper.pollJsonResponse(outputFile, 3000);
+            JsonObject response = ctx.executeAction(pid, action, 3000);
             if (response != null) {
                 Boolean enabled = response.getBoolean("enabled");
                 if (enabled != null && enabled) {
@@ -590,7 +582,6 @@ class DataRefreshService {
                         otelSpans.set(entries);
                     }
                 }
-                PathUtils.deleteFile(outputFile);
             }
         } catch (Exception e) {
             // ignore
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
index fa384ed5fd64..1bfb200729fc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -59,7 +58,6 @@ import 
org.apache.camel.diagram.TopologyLayoutEngine.TopologyLayoutEdge;
 import org.apache.camel.diagram.TopologyLayoutEngine.TopologyLayoutNode;
 import org.apache.camel.diagram.TopologyLayoutEngine.TopologyLayoutResult;
 import org.apache.camel.diagram.TopologyLayoutEngine.TopologyNodeInfo;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -547,7 +545,7 @@ class DiagramSupport {
         }
         preloading = true;
         if (ctx.runner != null) {
-            ctx.runner.scheduler().execute(() -> {
+            ctx.backgroundExecutor.execute(() -> {
                 try {
                     setTopologyMode(true);
                     loadAllDiagramsInBackground(ctx, pid, false, 0);
@@ -2029,42 +2027,23 @@ class DiagramSupport {
     }
 
     private JsonObject requestRouteTopology(MonitorContext ctx, String pid, 
boolean external, boolean routes) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "route-topology");
         root.put("metric", "true");
-        // Always request external endpoints so route diagrams can show dashed 
borders
         root.put("external", "true");
         if (routes) {
             root.put("routes", "true");
         }
-
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
-        return jo;
+        return ctx.executeAction(pid, root, 5000);
     }
 
     private JsonObject requestRouteStructure(MonitorContext ctx, String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "route-structure");
         root.put("filter", "*");
         root.put("brief", false);
         root.put("metric", true);
-
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
-        return jo;
+        return ctx.executeAction(pid, root, 5000);
     }
 
     static void addParentNodes(List<RouteDiagramLayoutEngine.NodeInfo> nodes, 
Set<String> nodeIds) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
index eb1220176bf6..b2c69a82ce18 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
@@ -17,7 +17,6 @@
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.net.URISyntaxException;
-import java.nio.file.Path;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
@@ -49,7 +48,6 @@ import dev.tamboui.widgets.block.Borders;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.EipModel;
@@ -901,7 +899,7 @@ class DiagramTab extends AbstractTab {
             diagram.setLoadingPlaceholder();
         }
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 diagram.setTopologyMode(topologyMode);
                 diagram.loadAllDiagramsInBackground(ctx, pid, showMetrics, 
external);
@@ -1206,7 +1204,7 @@ class DiagramTab extends AbstractTab {
             detailLoading = true;
             String rid = drillDownRouteId;
             if (ctx.runner != null) {
-                ctx.runner.scheduler().execute(() -> {
+                ctx.backgroundExecutor.execute(() -> {
                     JsonObject result = requestRouteProcessorDetail(rid);
                     cachedRouteDetail = result;
                     cachedRouteDetailId = rid;
@@ -1413,19 +1411,10 @@ class DiagramTab extends AbstractTab {
             return null;
         }
         try {
-            Path outputFile = ctx.getOutputFile(ctx.selectedPid);
-            PathUtils.deleteFile(outputFile);
-
             JsonObject root = new JsonObject();
             root.put("action", "processor-detail");
             root.put("routeId", routeId);
-
-            Path actionFile = ctx.getActionFile(ctx.selectedPid);
-            PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-            JsonObject jo = pollJsonResponse(outputFile, 5000);
-            PathUtils.deleteFile(outputFile);
-            return jo;
+            return ctx.executeAction(ctx.selectedPid, root, 5000);
         } catch (Exception e) {
             return null;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocViewerPopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocViewerPopup.java
index 5d339be97f73..c1b472d2c9c4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocViewerPopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DocViewerPopup.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
@@ -48,7 +47,6 @@ import dev.tamboui.widgets.list.ScrollMode;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.scrollbar.Scrollbar;
 import dev.tamboui.widgets.scrollbar.ScrollbarState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 
 import static org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.hint;
 import static org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.hintLast;
@@ -334,12 +332,9 @@ class DocViewerPopup {
         docLines = null;
         showPicker = false;
         try {
-            Path outputFile = ctx.getOutputFile(info.pid);
-            Files.deleteIfExists(outputFile);
             org.apache.camel.util.json.JsonObject action = new 
org.apache.camel.util.json.JsonObject();
             action.put("action", "readme");
-            PathUtils.writeTextSafely(action.toJson(), 
ctx.getActionFile(info.pid));
-            org.apache.camel.util.json.JsonObject response = 
TuiHelper.pollJsonResponse(outputFile, 5000);
+            org.apache.camel.util.json.JsonObject response = 
ctx.executeAction(info.pid, action, 5000);
             if (response != null && response.getString("content") != null) {
                 String raw = response.getString("content");
                 String file = response.getStringOrDefault("file", "README");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
index e24414dbca4a..32df7513bfd1 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
@@ -761,7 +761,7 @@ class ErrorsTab extends AbstractTableTab {
 
         diagram.setLoadingPlaceholder();
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 diagram.loadHighlightedNativeDiagramInBackground(ctx, pid, 
messageHistory, true, -1);
             } finally {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
index d5be3b8d700c..469129041b64 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -39,7 +38,6 @@ import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -476,7 +474,7 @@ class HeapHistogramTab extends AbstractTableTab {
             return;
         }
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadHeapHistogramInBackground(pid);
             } finally {
@@ -486,17 +484,10 @@ class HeapHistogramTab extends AbstractTableTab {
     }
 
     private void loadHeapHistogramInBackground(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "heap-histogram");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
@@ -540,17 +531,10 @@ class HeapHistogramTab extends AbstractTableTab {
     }
 
     private List<ClasspathTab.JarEntry> loadClasspathEntries(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject action = new JsonObject();
         action.put("action", "jvm");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(action.toJson(), actionFile);
-
-        JsonObject response = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject response = ctx.executeAction(pid, action, 5000);
 
         if (response == null) {
             return Collections.emptyList();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
index 68a38dcf5d75..fbe5f491ae11 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
@@ -1124,7 +1124,7 @@ class HistoryTab extends AbstractTab {
 
         boolean isFailed = failed;
         int step = initialStep;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 diagram.loadHighlightedNativeDiagramInBackground(ctx, pid, 
messageHistory, isFailed, step);
             } finally {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
index b2117c1e134a..d6327c8d8b1d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HttpTab.java
@@ -56,7 +56,6 @@ import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -708,7 +707,7 @@ class HttpTab extends AbstractTableTab {
         }
         List<FormHelper.HeaderEntry> hdrs = headerSnapshot;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 doProbeRequestInBackground(baseUrl, method, path, sendBody, 
hdrs);
             } finally {
@@ -1429,7 +1428,7 @@ class HttpTab extends AbstractTableTab {
         String specUri = ep.specificationUri;
         String operationId = ep.operationId;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadSpecInBackground(pid, specUri, operationId);
             } finally {
@@ -1439,18 +1438,11 @@ class HttpTab extends AbstractTableTab {
     }
 
     private void loadSpecInBackground(String pid, String specUri, String 
operationId) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "rest-spec");
         root.put("filter", specUri);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             applySpecResult(specUri, List.of("(No response from 
integration)"), 0);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LogTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LogTab.java
index 0f919da3e7ac..c09af0e382c3 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LogTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LogTab.java
@@ -478,8 +478,7 @@ class LogTab extends AbstractTab {
         root.put("command", "set-logging-level");
         root.put("logger-name", "root");
         root.put("logging-level", level);
-        Path actionFile = ctx.getActionFile(pid);
-        
org.apache.camel.dsl.jbang.core.common.PathUtils.writeTextSafely(root.toJson(), 
actionFile);
+        ctx.fireAction(pid, root);
     }
 
     boolean isSearchInputActive() {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MavenDependenciesTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MavenDependenciesTab.java
index b365db6d3f12..27feb4d7e24a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MavenDependenciesTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MavenDependenciesTab.java
@@ -351,7 +351,7 @@ class MavenDependenciesTab extends AbstractTableTab {
             return;
         }
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 DependencyLoader.LoadResult result = 
DependencyLoader.loadDependencies(info);
                 applyResult(result.entries(), result.source(), result.error());
@@ -368,7 +368,7 @@ class MavenDependenciesTab extends AbstractTableTab {
             return;
         }
         transitiveLoading = true;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 List<DependencyLoader.DepEntry> entries = 
DependencyLoader.resolveTransitives(allEntries);
                 applyTransitiveResult(entries);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 90941b7fdedd..1aef9c262ee7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -35,7 +35,6 @@ import dev.tamboui.tui.event.KeyCode;
 import dev.tamboui.tui.event.KeyEvent;
 import dev.tamboui.tui.event.KeyModifiers;
 import dev.tamboui.widgets.tabs.TabsState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -463,19 +462,13 @@ class McpFacade {
             return err;
         }
         try {
-            Path outputFile = ctx.getOutputFile(pid);
-            PathUtils.deleteFile(outputFile);
-
             JsonObject action = new JsonObject();
             action.put("action", "span");
             action.put("dump", "true");
             action.put("limit", String.valueOf(limit));
-            Path actionFile = ctx.getActionFile(pid);
-            PathUtils.writeTextSafely(action.toJson(), actionFile);
 
-            JsonObject response = TuiHelper.pollJsonResponse(outputFile, 3000);
+            JsonObject response = ctx.executeAction(pid, action, 3000);
             if (response != null) {
-                PathUtils.deleteFile(outputFile);
                 Boolean enabled = response.getBoolean("enabled");
                 if (enabled == null || !enabled) {
                     JsonObject err = new JsonObject();
@@ -516,17 +509,11 @@ class McpFacade {
             return err;
         }
         try {
-            Path outputFile = ctx.getOutputFile(pid);
-            PathUtils.deleteFile(outputFile);
-
             JsonObject action = new JsonObject();
             action.put("action", "processor-detail");
             action.put("routeId", routeId != null ? routeId : "*");
-            Path actionFile = ctx.getActionFile(pid);
-            PathUtils.writeTextSafely(action.toJson(), actionFile);
 
-            JsonObject response = TuiHelper.pollJsonResponse(outputFile, 5000);
-            PathUtils.deleteFile(outputFile);
+            JsonObject response = ctx.executeAction(pid, action, 5000);
             return response;
         } catch (Exception e) {
             JsonObject err = new JsonObject();
@@ -720,12 +707,9 @@ class McpFacade {
             return null;
         }
         try {
-            Path outputFile = ctx.getOutputFile(target.pid);
-            Files.deleteIfExists(outputFile);
             JsonObject action = new JsonObject();
             action.put("action", "readme");
-            PathUtils.writeTextSafely(action.toJson(), 
ctx.getActionFile(target.pid));
-            return TuiHelper.pollJsonResponse(outputFile, 5000);
+            return ctx.executeAction(target.pid, action, 5000);
         } catch (Exception e) {
             return null;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java
index 7362a523d83b..4db816e7dde2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -43,7 +42,6 @@ import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import dev.tamboui.widgets.table.TableState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -1504,19 +1502,12 @@ class MemoryLeakTab extends AbstractTab {
     }
 
     private void sendStartCommand(String pid, int dur) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "start");
         root.put("duration", String.valueOf(dur));
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 15000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 15000);
 
         if (jo != null && "recording".equals(jo.getString("status"))) {
             if (ctx.runner != null) {
@@ -1584,19 +1575,12 @@ class MemoryLeakTab extends AbstractTab {
     }
 
     private boolean sendStopAndLoadResults(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "stop");
         root.put("stacktrace", "true");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 30000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 30000);
 
         if (jo != null && "completed".equals(jo.getString("status"))) {
             List<SampleEntry> result = parseSamples(jo);
@@ -1709,35 +1693,21 @@ class MemoryLeakTab extends AbstractTab {
     }
 
     private boolean isRecordingActive(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "status");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         return jo == null || "recording".equals(jo.getString("status"));
     }
 
     private void sendStatusCommand(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "status");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
@@ -1769,19 +1739,12 @@ class MemoryLeakTab extends AbstractTab {
     }
 
     private void loadQueryResults(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "query");
         root.put("stacktrace", "true");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 10000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 10000);
 
         if (jo != null && "completed".equals(jo.getString("status"))) {
             List<SampleEntry> result = parseSamples(jo);
@@ -1815,19 +1778,12 @@ class MemoryLeakTab extends AbstractTab {
 
     private void sendStopAndLoadComparison(String pid) {
         // stop the second recording
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "jfr-memory-leak");
         root.put("command", "stop");
         root.put("stacktrace", "true");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 30000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 30000);
 
         if (jo == null || !"completed".equals(jo.getString("status"))) {
             if (ctx.runner != null) {
@@ -1841,19 +1797,12 @@ class MemoryLeakTab extends AbstractTab {
     }
 
     private void loadComparisonResults(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject cmpRoot = new JsonObject();
         cmpRoot.put("action", "jfr-memory-leak");
         cmpRoot.put("command", "compare");
         cmpRoot.put("stacktrace", "true");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(cmpRoot.toJson(), actionFile);
-
-        JsonObject cmpResult = pollJsonResponse(outputFile, 10000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject cmpResult = ctx.executeAction(pid, cmpRoot, 10000);
 
         if (cmpResult != null && 
"compared".equals(cmpResult.getString("status"))) {
             List<ComparisonEntry> entries = parseComparisons(cmpResult);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
index e683310ecfa4..f100067731c6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -37,7 +36,6 @@ import dev.tamboui.widgets.block.Block;
 import dev.tamboui.widgets.block.BorderType;
 import dev.tamboui.widgets.block.Borders;
 import dev.tamboui.widgets.paragraph.Paragraph;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonObject;
 
@@ -459,8 +457,7 @@ class MemoryTab extends AbstractTab {
         }
         JsonObject root = new JsonObject();
         root.put("action", "gc");
-        Path actionFile = ctx.getActionFile(info.pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
+        ctx.fireAction(info.pid, root);
     }
 
     @Override
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsTab.java
index a2ee8cd1db6e..4ef4cd0da358 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsTab.java
@@ -576,7 +576,7 @@ class MetricsTab extends AbstractTableTab {
         }
         rawTitle = url;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 HttpClient client = HttpClient.newBuilder()
                         .connectTimeout(Duration.ofSeconds(5))
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
index 78a42873521e..2b200b23ddb0 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
@@ -18,12 +18,17 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.nio.file.Path;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 
 import dev.tamboui.tui.TuiRunner;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.dsl.jbang.core.common.PathUtils;
+import org.apache.camel.util.json.JsonObject;
 
 /**
  * Shared state accessible to all {@link MonitorTab} implementations.
@@ -33,6 +38,11 @@ class MonitorContext {
     final AtomicReference<List<IntegrationInfo>> data;
     final AtomicReference<List<InfraInfo>> infraData;
     TuiRunner runner;
+    final ExecutorService backgroundExecutor = Executors.newCachedThreadPool(r 
-> {
+        Thread t = new Thread(r, "camel-tui-bg");
+        t.setDaemon(true);
+        return t;
+    });
 
     volatile String selectedPid;
     volatile String lastSelectedName;
@@ -94,6 +104,31 @@ class MonitorContext {
         return "?";
     }
 
+    private final ConcurrentHashMap<String, Object> actionLocks = new 
ConcurrentHashMap<>();
+
+    void fireAction(String pid, JsonObject request) {
+        Object lock = actionLocks.computeIfAbsent(pid, k -> new Object());
+        synchronized (lock) {
+            Path actionFile = getActionFile(pid);
+            PathUtils.writeTextSafely(request.toJson(), actionFile);
+        }
+    }
+
+    JsonObject executeAction(String pid, JsonObject request, long timeoutMs) {
+        Object lock = actionLocks.computeIfAbsent(pid, k -> new Object());
+        synchronized (lock) {
+            Path outputFile = getOutputFile(pid);
+            PathUtils.deleteFile(outputFile);
+            Path actionFile = getActionFile(pid);
+            PathUtils.writeTextSafely(request.toJson(), actionFile);
+            try {
+                return TuiHelper.pollJsonResponse(outputFile, timeoutMs);
+            } finally {
+                PathUtils.deleteFile(outputFile);
+            }
+        }
+    }
+
     Path getActionFile(String pid) {
         return CommandLineHelper.getCamelDir().resolve(pid + "-action.json");
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
index f05948ea43b4..a284cf16c034 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
@@ -17,7 +17,6 @@
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.net.URISyntaxException;
-import java.nio.file.Path;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
@@ -54,7 +53,6 @@ import dev.tamboui.widgets.table.Table;
 import dev.tamboui.widgets.table.TableState;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.EipModel;
@@ -1604,8 +1602,7 @@ class RoutesTab extends AbstractTab {
         root.put("action", "route");
         root.put("id", routeId);
         root.put("command", command);
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
+        ctx.fireAction(pid, root);
     }
 
     // ---- Async loading ----
@@ -1626,7 +1623,7 @@ class RoutesTab extends AbstractTab {
             diagram.setLoadingPlaceholder();
         }
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 diagram.setTopologyMode(topologyMode);
                 diagram.loadAllDiagramsInBackground(ctx, pid, showMetrics, 
external);
@@ -1816,7 +1813,7 @@ class RoutesTab extends AbstractTab {
             detailLoading = true;
             String rid = drillDownRouteId;
             if (ctx.runner != null) {
-                ctx.runner.scheduler().execute(() -> {
+                ctx.backgroundExecutor.execute(() -> {
                     JsonObject result = requestRouteProcessorDetail(rid);
                     cachedRouteDetail = result;
                     cachedRouteDetailId = rid;
@@ -2005,19 +2002,10 @@ class RoutesTab extends AbstractTab {
             return null;
         }
         try {
-            Path outputFile = ctx.getOutputFile(ctx.selectedPid);
-            PathUtils.deleteFile(outputFile);
-
             JsonObject root = new JsonObject();
             root.put("action", "processor-detail");
             root.put("routeId", routeId);
-
-            Path actionFile = ctx.getActionFile(ctx.selectedPid);
-            PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-            JsonObject jo = TuiHelper.pollJsonResponse(outputFile, 5000);
-            PathUtils.deleteFile(outputFile);
-            return jo;
+            return ctx.executeAction(ctx.selectedPid, root, 5000);
         } catch (Exception e) {
             return null;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SendMessagePopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SendMessagePopup.java
index b08ca2f5d675..3125e4c6cc7e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SendMessagePopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SendMessagePopup.java
@@ -17,12 +17,11 @@
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.io.File;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ExecutorService;
 
 import dev.tamboui.layout.Constraint;
 import dev.tamboui.layout.Layout;
@@ -46,7 +45,6 @@ import dev.tamboui.widgets.input.TextInput;
 import dev.tamboui.widgets.input.TextInputState;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
@@ -484,8 +482,8 @@ class SendMessagePopup {
         }
     }
 
-    void doSend(MonitorContext ctx, ScheduledExecutorService scheduler) {
-        if (!visible || sending || ctx == null || scheduler == null) {
+    void doSend(MonitorContext ctx, ExecutorService executor) {
+        if (!visible || sending || ctx == null || executor == null) {
             return;
         }
         sending = true;
@@ -509,11 +507,8 @@ class SendMessagePopup {
         List<FormHelper.HeaderEntry> hdrs = headers != null ? new 
ArrayList<>(headers) : null;
         String routeId = route.routeId;
 
-        scheduler.execute(() -> {
+        executor.execute(() -> {
             try {
-                Path outputFile = ctx.getOutputFile(targetPid);
-                PathUtils.deleteFile(outputFile);
-
                 JsonObject root = new JsonObject();
                 root.put("action", "send");
                 root.put("endpoint", endpoint);
@@ -539,11 +534,7 @@ class SendMessagePopup {
                     }
                 }
 
-                Path actionFile = ctx.getActionFile(targetPid);
-                PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-                JsonObject response = TuiHelper.pollJsonResponse(outputFile, 
25000);
-                PathUtils.deleteFile(outputFile);
+                JsonObject response = ctx.executeAction(targetPid, root, 
25000);
 
                 if (response == null) {
                     applyResult(routeId, sendBody, hdrs, captureInOut,
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
index 505b3dc39a5f..4985cc301b3f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
@@ -46,15 +46,12 @@ import dev.tamboui.widgets.block.Title;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.scrollbar.Scrollbar;
 import dev.tamboui.widgets.scrollbar.ScrollbarState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.support.LoggerHelper;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
 
-import static 
org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.pollJsonResponse;
-
 /**
  * Reusable source code viewer with syntax highlighting, scrolling, and 
line-number display. Can be used by any tab that
  * needs to show route source code.
@@ -512,7 +509,7 @@ class SourceViewer {
         scrollX = 0;
         visible = true;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadInBackground(ctx, pid, routeId, targetLine);
             } finally {
@@ -561,18 +558,11 @@ class SourceViewer {
     }
 
     private void loadInBackground(MonitorContext ctx, String pid, String 
routeId, int targetLine) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "source");
         root.put("filter", routeId);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             applyResult(ctx, routeId, null, List.of("(No response from 
integration)"), Collections.emptyList(), 0, -1);
@@ -760,7 +750,7 @@ class SourceViewer {
         MonitorContext ctx = currentCtx;
         String pid = currentPid;
         String routeId = currentRouteId;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 if (format.equals(originalFormat)) {
                     loadInBackground(ctx, pid, routeId, 0);
@@ -774,20 +764,13 @@ class SourceViewer {
     }
 
     private void loadFormatInBackground(MonitorContext ctx, String pid, String 
routeId, String format) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "route-dump");
         root.put("filter", routeId);
         root.put("format", format);
         root.put("uriAsParameters", "yaml".equals(format) ? "true" : "false");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             applyFormatResult(ctx, format, List.of("(No response from 
integration)"), Collections.emptyList());
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlQueryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlQueryTab.java
index 1f6f8e273e93..8bea8e086eb7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlQueryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlQueryTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -47,7 +46,6 @@ import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
 import dev.tamboui.widgets.table.TableState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -675,11 +673,8 @@ class SqlQueryTab extends AbstractTab {
         String savedSql = lastSql;
         String savedDs = lastDsName;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
-                Path outputFile = ctx.getOutputFile(ctx.selectedPid);
-                PathUtils.deleteFile(outputFile);
-
                 JsonObject action = new JsonObject();
                 action.put("action", "sql-update-row");
                 action.put("table", tableName);
@@ -689,11 +684,7 @@ class SqlQueryTab extends AbstractTab {
                 action.put("primaryKeyValues", pkJson);
                 action.put("columnValues", colJson);
 
-                Path actionFile = ctx.getActionFile(ctx.selectedPid);
-                PathUtils.writeTextSafely(action.toJson(), actionFile);
-
-                JsonObject jo = pollJsonResponse(outputFile, 15000);
-                PathUtils.deleteFile(outputFile);
+                JsonObject jo = ctx.executeAction(ctx.selectedPid, action, 
15000);
 
                 if (jo == null) {
                     if (ctx.runner != null) {
@@ -973,7 +964,7 @@ class SqlQueryTab extends AbstractTab {
         lastSql = sql;
         lastDsName = dsName;
 
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 executeInBackground(pid, sql, dsName);
             } finally {
@@ -983,9 +974,6 @@ class SqlQueryTab extends AbstractTab {
     }
 
     private void executeInBackground(String pid, String sql, String dsName) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "sql-query");
         root.put("sql", sql);
@@ -995,11 +983,7 @@ class SqlQueryTab extends AbstractTab {
         root.put("maxRows", 100);
         root.put("queryTimeout", 30);
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 35000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 35000);
 
         if (jo == null) {
             if (ctx.runner != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
index 0df04c613750..60d04399d968 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -40,7 +39,6 @@ import dev.tamboui.widgets.block.Borders;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.scrollbar.Scrollbar;
 import dev.tamboui.widgets.scrollbar.ScrollbarState;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -270,19 +268,12 @@ class StartupTab extends AbstractTab {
         }
 
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
-                Path outputFile = ctx.getOutputFile(pid);
-                PathUtils.deleteFile(outputFile);
-
                 JsonObject root = new JsonObject();
                 root.put("action", "startup-recorder");
 
-                Path actionFile = ctx.getActionFile(pid);
-                PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-                JsonObject jo = pollJsonResponse(outputFile, 5000);
-                PathUtils.deleteFile(outputFile);
+                JsonObject jo = ctx.executeAction(pid, root, 5000);
 
                 if (jo == null) {
                     applyResult(Collections.emptyList(), "No response from 
integration");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThreadsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThreadsTab.java
index 68f087f0d01d..a3333a65d253 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThreadsTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThreadsTab.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -40,7 +39,6 @@ import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -331,7 +329,7 @@ class ThreadsTab extends AbstractTableTab {
             return;
         }
         String pid = ctx.selectedPid;
-        ctx.runner.scheduler().execute(() -> {
+        ctx.backgroundExecutor.execute(() -> {
             try {
                 loadThreadsInBackground(pid);
             } finally {
@@ -341,17 +339,10 @@ class ThreadsTab extends AbstractTableTab {
     }
 
     private void loadThreadsInBackground(String pid) {
-        Path outputFile = ctx.getOutputFile(pid);
-        PathUtils.deleteFile(outputFile);
-
         JsonObject root = new JsonObject();
         root.put("action", "thread-dump");
 
-        Path actionFile = ctx.getActionFile(pid);
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
-
-        JsonObject jo = pollJsonResponse(outputFile, 5000);
-        PathUtils.deleteFile(outputFile);
+        JsonObject jo = ctx.executeAction(pid, root, 5000);
 
         if (jo == null) {
             return;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
index 04b6d60337f4..16c5a4c624c4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
@@ -34,7 +34,6 @@ import dev.tamboui.text.CharWidth;
 import dev.tamboui.text.Line;
 import dev.tamboui.text.Span;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
-import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.FileUtil;
@@ -934,13 +933,9 @@ final class TuiHelper {
         notifyCallback.accept("Writing heap dump...", false);
         String pid = info.pid;
         Thread t = new Thread(() -> {
-            Path outputFile = ctx.getOutputFile(pid);
-            PathUtils.deleteFile(outputFile);
             JsonObject root = new JsonObject();
             root.put("action", "heap-dump");
-            Path actionFile = ctx.getActionFile(pid);
-            PathUtils.writeTextSafely(root.toJson(), actionFile);
-            JsonObject jo = pollJsonResponse(outputFile, 60000);
+            JsonObject jo = ctx.executeAction(pid, root, 60000);
             if (jo != null) {
                 String error = jo.getString("error");
                 if (error != null) {
@@ -953,7 +948,6 @@ final class TuiHelper {
             } else {
                 notifyCallback.accept("Heap dump: no response within 60s", 
true);
             }
-            PathUtils.deleteFile(outputFile);
         });
         t.setDaemon(true);
         t.setName("heap-dump-" + pid);

Reply via email to