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 47406dddffe6 CAMEL-24372: Auto-inject camel-cli-connector for Spring 
Boot projects
47406dddffe6 is described below

commit 47406dddffe6c87a34aa11ef9ea821c26e8acda4
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Aug 10 19:20:33 2026 +0200

    CAMEL-24372: Auto-inject camel-cli-connector for Spring Boot projects
    
    When launching a Spring Boot project via F10, inject the
    camel-cli-connector-starter dependency into a temp pom copy if not
    already present, so the TUI can communicate with the running app.
    Also enable restart for Spring Boot and Quarkus projects.
    
    CAMEL-24372: Restart Spring Boot/Quarkus via Maven parent re-launch
    
    For Spring Boot and Quarkus restarts, find the Maven parent process
    and re-launch it instead of the raw Java child process. This ensures
    source changes are recompiled before the app restarts.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 76 ++++++++++++++++++++--
 .../dsl/jbang/core/commands/tui/LaunchManager.java | 64 ++++++++++++++++++
 2 files changed, 134 insertions(+), 6 deletions(-)

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 fe75b998ffd4..a4b751233121 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
@@ -1904,16 +1904,80 @@ public class CamelMonitor extends CamelCommand {
         }
 
         String platform = info.platform;
-        boolean isSpringBoot = "Spring Boot".equals(platform);
-        boolean isQuarkus = "Quarkus".equals(platform);
+        boolean isMavenManaged = "Spring Boot".equals(platform) || 
"Quarkus".equals(platform);
+        if (isMavenManaged) {
+            restartMavenProcess(ph, info);
+        } else {
+            restartCamelMainProcess(ph, info);
+        }
+    }
 
-        // TODO: restart for Spring Boot and Quarkus is not yet reliable
-        if (isSpringBoot || isQuarkus) {
-            setNotification("Restart not supported for " + platform, true);
+    private void restartMavenProcess(ProcessHandle ph, IntegrationInfo info) {
+        // Find the Maven parent process (mvn spring-boot:run / quarkus:dev)
+        ProcessHandle mavenPh = ph.parent().orElse(null);
+        if (mavenPh == null) {
+            setNotification("Cannot restart: Maven parent process not found", 
true);
             return;
         }
 
-        restartCamelMainProcess(ph, info);
+        // Capture Maven parent command line before stopping
+        Optional<String> cmdOpt = mavenPh.info().command();
+        Optional<String[]> argsOpt = mavenPh.info().arguments();
+        Optional<String> cmdLineOpt = mavenPh.info().commandLine();
+
+        String name = info.name;
+        String directory = info.directory;
+
+        ctx.lastSelectedName = name;
+        dataService.forceFullScan();
+
+        // Kill the Maven parent (which also kills the child Java process)
+        mavenPh.destroy();
+        setNotification("Restarting: " + name, false);
+
+        if (runner != null) {
+            ctx.backgroundExecutor.execute(() -> {
+                try {
+                    CompletableFuture<ProcessHandle> exitFuture = 
mavenPh.onExit().toCompletableFuture();
+                    try {
+                        exitFuture.get(15, TimeUnit.SECONDS);
+                    } catch (Exception e) {
+                        mavenPh.destroyForcibly();
+                        Thread.sleep(500);
+                    }
+
+                    List<String> cmd = new ArrayList<>();
+                    if (cmdOpt.isPresent() && argsOpt.isPresent() && 
argsOpt.get().length > 0) {
+                        cmd.add(cmdOpt.get());
+                        Collections.addAll(cmd, argsOpt.get());
+                    } else {
+                        cmdLineOpt.ifPresent(s -> 
cmd.addAll(parseCommandLine(s)));
+                    }
+
+                    if (cmd.isEmpty()) {
+                        runner.runOnRenderThread(
+                                () -> setNotification("Cannot restart: Maven 
command line not available", true));
+                        return;
+                    }
+
+                    ProcessBuilder pb = new ProcessBuilder(cmd);
+                    if (directory != null) {
+                        pb.directory(new File(directory));
+                    }
+                    pb.redirectErrorStream(true);
+                    Path outputFile = 
LaunchManager.createSecureTempFile("camel-restart-", ".log");
+                    outputFile.toFile().deleteOnExit();
+                    pb.redirectOutput(outputFile.toFile());
+                    Process process = pb.start();
+                    actionsPopup.getLaunchManager().addPendingLaunch(name, 
process, outputFile);
+
+                    runner.runOnRenderThread(() -> setNotification("Restarted: 
" + name + " (recompiling)", false));
+                } catch (Exception e) {
+                    runner.runOnRenderThread(
+                            () -> setNotification("Restart failed: " + 
e.getMessage(), true));
+                }
+            });
+        }
     }
 
     private void restartCamelMainProcess(ProcessHandle ph, IntegrationInfo 
info) {
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 890e8043ad50..6ac7d6e5e2c5 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
@@ -203,6 +203,10 @@ class LaunchManager {
             }
             // Translate Camel JBang args to Maven-compatible args
             cmd.addAll(translateArgsForMaven(extraArgs, projectType));
+            // Inject camel-cli-connector if not already in the project
+            if ("spring-boot".equals(projectType)) {
+                injectCliConnectorIfMissing(dir, cmd);
+            }
             Path outputFile = createSecureTempFile("camel-maven-", ".log");
             outputFile.toFile().deleteOnExit();
             ProcessBuilder pb = new ProcessBuilder(cmd);
@@ -217,6 +221,66 @@ class LaunchManager {
         }
     }
 
+    private void injectCliConnectorIfMissing(String dir, List<String> cmd) {
+        try {
+            Path pomFile = Path.of(dir, "pom.xml");
+            if (!Files.isRegularFile(pomFile)) {
+                return;
+            }
+            String pomContent = Files.readString(pomFile);
+            if (pomContent.contains("camel-cli-connector")) {
+                return;
+            }
+            // Add cli-connector-starter dependency (version managed by BOM)
+            String dep = "\n        <dependency>\n"
+                         + "            
<groupId>org.apache.camel.springboot</groupId>\n"
+                         + "            
<artifactId>camel-cli-connector-starter</artifactId>\n"
+                         + "        </dependency>";
+            // Find the project-level </dependencies> (not inside 
dependencyManagement or plugins)
+            int insertIdx = findProjectDependenciesEnd(pomContent);
+            if (insertIdx < 0) {
+                return;
+            }
+            String modified = pomContent.substring(0, insertIdx) + dep + "\n   
 " + pomContent.substring(insertIdx);
+            // Write temp pom in the project dir so Maven can find sources
+            Path tempPom = Path.of(dir, ".camel-tui-pom.xml");
+            tempPom.toFile().deleteOnExit();
+            Files.writeString(tempPom, modified);
+            cmd.add("-f");
+            cmd.add(tempPom.getFileName().toString());
+        } catch (Exception e) {
+            // best effort — don't fail the launch
+        }
+    }
+
+    private static int findProjectDependenciesEnd(String pom) {
+        // Find <dependencies> that is a direct child of <project>,
+        // not nested inside <dependencyManagement>, <plugin>, or <profile>
+        int dmStart = pom.indexOf("<dependencyManagement>");
+        int dmEnd = dmStart >= 0 ? pom.indexOf("</dependencyManagement>", 
dmStart) : -1;
+        int buildStart = pom.indexOf("<build>");
+
+        int searchFrom = 0;
+        while (true) {
+            int depStart = pom.indexOf("<dependencies>", searchFrom);
+            if (depStart < 0) {
+                return -1;
+            }
+            // Skip if inside <dependencyManagement>
+            if (dmStart >= 0 && depStart > dmStart && (dmEnd < 0 || depStart < 
dmEnd)) {
+                searchFrom = dmEnd > 0 ? dmEnd : depStart + 14;
+                continue;
+            }
+            // Skip if inside <build> (plugins can have dependencies)
+            if (buildStart >= 0 && depStart > buildStart) {
+                searchFrom = depStart + 14;
+                continue;
+            }
+            int depEnd = pom.indexOf("</dependencies>", depStart);
+            return depEnd >= 0 ? depEnd : -1;
+        }
+    }
+
     void launchCamelRun(String sourceDir, String displayName, List<String> 
extraArgs) {
         try {
             List<String> cmd = new 
ArrayList<>(LauncherHelper.getCamelCommand());

Reply via email to