You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2025/08/12 10:48:48 UTC

(camel) branch main updated: CAMEL-22322: camel-jbang - camel debug for spring boot (#18874)

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 70adba06d72 CAMEL-22322: camel-jbang - camel debug for spring boot (#18874)
70adba06d72 is described below

commit 70adba06d726f6d48e0dfcc04a8cedd241d83760
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Aug 12 12:48:43 2025 +0200

    CAMEL-22322: camel-jbang - camel debug for spring boot (#18874)
---
 .../java/org/apache/camel/spi/BacklogDebugger.java |   9 +-
 .../org/apache/camel/resource-resolver/source      |   2 +
 .../impl/debugger/DefaultBacklogDebugger.java      |   5 +
 .../camel/impl/engine/AbstractCamelContext.java    |  22 +--
 .../impl/engine/DefaultResourceResolvers.java      |  47 +++++
 .../org/apache/camel/util/ResourceLoaderTest.java  |  17 ++
 .../org/apache/camel/main/BaseMainSupport.java     |  57 +------
 .../camel/main/DefaultConfigurationConfigurer.java |  68 ++++++++
 .../apache/camel/main/DefaultRoutesCollector.java  |  12 ++
 .../camel/dsl/jbang/core/commands/Debug.java       | 189 +++++++++++++++++++--
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   6 +-
 11 files changed, 347 insertions(+), 87 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogDebugger.java b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogDebugger.java
index 027052f04cf..a660b43c5e1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogDebugger.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogDebugger.java
@@ -81,15 +81,20 @@ public interface BacklogDebugger extends StatefulService {
     void setLoggingLevel(String level);
 
     /**
-     * To enable the debugger
+     * To enable and start the debugger
      */
     void enableDebugger();
 
     /**
-     * To disable the debugger
+     * To disable and stop the debugger
      */
     void disableDebugger();
 
+    /**
+     * To configure whether the debugger should be enabled
+     */
+    void setEnabled(boolean enabled);
+
     /**
      * Whether the debugger is enabled
      */
diff --git a/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/source b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/source
new file mode 100644
index 00000000000..ad600b90740
--- /dev/null
+++ b/core/camel-base-engine/src/generated/resources/META-INF/services/org/apache/camel/resource-resolver/source
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.engine.DefaultResourceResolvers$SourceResolver
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
index 733f7791e7d..4011f60aa16 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
@@ -206,6 +206,11 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
         clearBreakpoints();
     }
 
+    @Override
+    public void setEnabled(boolean enabled) {
+        this.enabled.set(enabled);
+    }
+
     @Override
     public boolean isEnabled() {
         return enabled.get();
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index 9efb702c5de..dc2f9462082 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -2445,17 +2445,6 @@ public abstract class AbstractCamelContext extends BaseService
 
         forceLazyInitialization();
 
-        // setup cli-connector if not already done
-        if (hasService(CliConnector.class) == null) {
-            CliConnectorFactory ccf = getCamelContextExtension().getContextPlugin(CliConnectorFactory.class);
-            if (ccf != null && ccf.isEnabled()) {
-                CliConnector connector = ccf.createConnector();
-                addService(connector, true);
-                // force start cli connector early as otherwise it will be deferred until context is started
-                // but, we want status available during startup phase
-                ServiceHelper.startService(connector);
-            }
-        }
         // auto-detect camel-debug on classpath (if debugger has not been explicit added)
         boolean debuggerDetected = false;
         if (getDebugger() == null && hasService(BacklogDebugger.class) == null) {
@@ -2480,6 +2469,17 @@ public abstract class AbstractCamelContext extends BaseService
                 addService(backlog, true, true);
             }
         }
+        // setup cli-connector if not already done (after debugger)
+        if (hasService(CliConnector.class) == null) {
+            CliConnectorFactory ccf = getCamelContextExtension().getContextPlugin(CliConnectorFactory.class);
+            if (ccf != null && ccf.isEnabled()) {
+                CliConnector connector = ccf.createConnector();
+                addService(connector, true);
+                // force start cli connector early as otherwise it will be deferred until context is started
+                // but, we want status available during startup phase
+                ServiceHelper.startService(connector);
+            }
+        }
 
         addService(getManagementStrategy(), false);
 
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
index 01dd83eec05..f72e9d0ad93 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultResourceResolvers.java
@@ -287,6 +287,53 @@ public final class DefaultResourceResolvers {
         }
     }
 
+    /**
+     * An implementation of the {@link ResourceResolver} that resolves a {@link Resource} from file system in
+     * src/main/java.
+     */
+    @ResourceResolver(SourceResolver.SCHEME)
+    public static class SourceResolver extends ResourceResolverSupport {
+        public static final String SCHEME = "source";
+
+        public SourceResolver() {
+            super(SCHEME);
+        }
+
+        @Override
+        public Resource createResource(String location, String remaining) {
+            String dir = "src/main/java/";
+            boolean test = remaining.endsWith("?test=true");
+            if (test) {
+                remaining = remaining.substring(0, remaining.length() - 10);
+                dir = "src/test/java/";
+            }
+            String name = remaining.replace('.', '/') + ".java";
+            final File path = new File(getPath(dir + name));
+            return new ResourceSupport(SCHEME, location) {
+                @Override
+                public boolean exists() {
+                    return path.exists();
+                }
+
+                @Override
+                public URI getURI() {
+                    return path.toURI();
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    if (!exists()) {
+                        throw new FileNotFoundException(path + " does not exist");
+                    }
+                    if (path.isDirectory()) {
+                        throw new FileNotFoundException(path + " is a directory");
+                    }
+                    return new FileInputStream(path);
+                }
+            };
+        }
+    }
+
     static final class HttpResource extends ResourceSupport implements ContentTypeAware {
         private String contentType;
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java b/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java
index 293b7474658..559e96c8fce 100644
--- a/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/util/ResourceLoaderTest.java
@@ -26,6 +26,7 @@ import java.util.Base64;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.GZIPOutputStream;
 
+import org.apache.camel.MyFoo;
 import org.apache.camel.TestSupport;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.impl.engine.DefaultResourceLoader;
@@ -339,4 +340,20 @@ public class ResourceLoaderTest extends TestSupport {
             assertEquals(raw, content);
         }
     }
+
+    @Test
+    public void testLoadSource() throws Exception {
+        DefaultCamelContext context = new DefaultCamelContext();
+        Resource resource
+                = PluginHelper.getResourceLoader(context).resolveResource("source:" + MyFoo.class.getName() + "?test=true");
+
+        try (InputStream is = resource.getInputStream()) {
+            assertNotNull(is);
+
+            String content = context.getTypeConverter().convertTo(String.class, is);
+            assertNotNull(content);
+            assertTrue(content.contains("public class MyFoo {"));
+        }
+    }
+
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index f81857115c0..cff1264bd25 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -58,13 +58,10 @@ import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.health.HealthCheck;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckRepository;
-import org.apache.camel.impl.debugger.DebuggerJmxConnectorService;
-import org.apache.camel.impl.debugger.DefaultBacklogDebugger;
 import org.apache.camel.impl.engine.DefaultCompileStrategy;
 import org.apache.camel.impl.engine.DefaultRoutesLoader;
 import org.apache.camel.saga.CamelSagaService;
 import org.apache.camel.spi.AutowiredLifecycleStrategy;
-import org.apache.camel.spi.BacklogDebugger;
 import org.apache.camel.spi.BacklogTracer;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.CamelEvent;
@@ -2100,58 +2097,8 @@ public abstract class BaseMainSupport extends BaseService {
         setPropertiesOnTarget(camelContext, config, properties, "camel.debug.",
                 failIfNotSet, true, autoConfiguredProperties);
 
-        if (!config.isEnabled() && !config.isStandby()) {
-            return;
-        }
-
-        // must enable source location and history
-        // so debugger tooling knows to map breakpoints to source code
-        camelContext.setSourceLocationEnabled(true);
-        camelContext.setMessageHistory(true);
-
-        // enable debugger on camel
-        camelContext.setDebugging(config.isEnabled());
-        camelContext.setDebugStandby(config.isStandby());
-
-        BacklogDebugger debugger = DefaultBacklogDebugger.createDebugger(camelContext);
-        debugger.setStandby(config.isStandby());
-        debugger.setInitialBreakpoints(config.getBreakpoints());
-        debugger.setSingleStepIncludeStartEnd(config.isSingleStepIncludeStartEnd());
-        debugger.setBodyMaxChars(config.getBodyMaxChars());
-        debugger.setBodyIncludeStreams(config.isBodyIncludeStreams());
-        debugger.setBodyIncludeFiles(config.isBodyIncludeFiles());
-        debugger.setIncludeExchangeProperties(config.isIncludeExchangeProperties());
-        debugger.setIncludeExchangeVariables(config.isIncludeExchangeVariables());
-        debugger.setIncludeException(config.isIncludeException());
-        debugger.setLoggingLevel(config.getLoggingLevel().name());
-        debugger.setSuspendMode(config.isWaitForAttach()); // this option is named wait-for-attach
-        debugger.setFallbackTimeout(config.getFallbackTimeout());
-
-        // enable jmx connector if port is set
-        if (config.isJmxConnectorEnabled()) {
-            DebuggerJmxConnectorService connector = new DebuggerJmxConnectorService();
-            connector.setCreateConnector(true);
-            connector.setRegistryPort(config.getJmxConnectorPort());
-            camelContext.addService(connector);
-        }
-
-        // start debugger after context is started
-        camelContext.addLifecycleStrategy(new LifecycleStrategySupport() {
-            @Override
-            public void onContextStarted(CamelContext context) {
-                // only enable debugger if not in standby mode
-                if (!debugger.isStandby()) {
-                    debugger.enableDebugger();
-                }
-            }
-
-            @Override
-            public void onContextStopping(CamelContext context) {
-                debugger.disableDebugger();
-            }
-        });
-
-        camelContext.addService(debugger);
+        // use common logic to configure debugger
+        DefaultConfigurationConfigurer.configureBacklogDebugger(camelContext, config);
     }
 
     private void setTracerProperties(
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
index e9e994d41f7..dd459d3a4af 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
@@ -36,6 +36,8 @@ import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckRepository;
 import org.apache.camel.impl.debugger.BacklogTracer;
+import org.apache.camel.impl.debugger.DebuggerJmxConnectorService;
+import org.apache.camel.impl.debugger.DefaultBacklogDebugger;
 import org.apache.camel.impl.engine.DefaultCompileStrategy;
 import org.apache.camel.impl.engine.PooledExchangeFactory;
 import org.apache.camel.impl.engine.PooledProcessorExchangeFactory;
@@ -91,6 +93,7 @@ import org.apache.camel.spi.VariableRepositoryFactory;
 import org.apache.camel.support.ClassicUuidGenerator;
 import org.apache.camel.support.DefaultContextReloadStrategy;
 import org.apache.camel.support.DefaultUuidGenerator;
+import org.apache.camel.support.LifecycleStrategySupport;
 import org.apache.camel.support.OffUuidGenerator;
 import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.RouteWatcherReloadStrategy;
@@ -664,6 +667,71 @@ public final class DefaultConfigurationConfigurer {
         }
     }
 
+    /**
+     * Configures the {@link BacklogDebugger} with the configuration.
+     *
+     * @param camelContext the camel context
+     * @param config       the configuration
+     */
+    public static void configureBacklogDebugger(CamelContext camelContext, DebuggerConfigurationProperties config)
+            throws Exception {
+        if (!config.isEnabled() && !config.isStandby()) {
+            return;
+        }
+
+        // must enable source location and history
+        // so debugger tooling knows to map breakpoints to source code
+        camelContext.setSourceLocationEnabled(true);
+        camelContext.setMessageHistory(true);
+
+        // enable debugger on camel
+        camelContext.setDebugging(config.isEnabled());
+        camelContext.setDebugStandby(config.isStandby());
+
+        BacklogDebugger debugger = DefaultBacklogDebugger.createDebugger(camelContext);
+        debugger.setEnabled(config.isEnabled());
+        debugger.setStandby(config.isStandby());
+        debugger.setInitialBreakpoints(config.getBreakpoints());
+        debugger.setSingleStepIncludeStartEnd(config.isSingleStepIncludeStartEnd());
+        debugger.setBodyMaxChars(config.getBodyMaxChars());
+        debugger.setBodyIncludeStreams(config.isBodyIncludeStreams());
+        debugger.setBodyIncludeFiles(config.isBodyIncludeFiles());
+        debugger.setIncludeExchangeProperties(config.isIncludeExchangeProperties());
+        debugger.setIncludeExchangeVariables(config.isIncludeExchangeVariables());
+        debugger.setIncludeException(config.isIncludeException());
+        debugger.setLoggingLevel(config.getLoggingLevel().name());
+        debugger.setSuspendMode(config.isWaitForAttach()); // this option is named wait-for-attach
+        debugger.setFallbackTimeout(config.getFallbackTimeout());
+
+        // enable jmx connector if port is set
+        if (config.isJmxConnectorEnabled()) {
+            DebuggerJmxConnectorService connector = new DebuggerJmxConnectorService();
+            connector.setCreateConnector(true);
+            connector.setRegistryPort(config.getJmxConnectorPort());
+            camelContext.addService(connector);
+        }
+
+        // start debugger after context is started
+        camelContext.addLifecycleStrategy(new LifecycleStrategySupport() {
+            @Override
+            public void onContextStarted(CamelContext context) {
+                // only enable debugger if not in standby mode
+                if (debugger.isEnabled() && !debugger.isStandby()) {
+                    debugger.enableDebugger();
+                }
+            }
+
+            @Override
+            public void onContextStopping(CamelContext context) {
+                if (debugger.isEnabled()) {
+                    debugger.disableDebugger();
+                }
+            }
+        });
+
+        camelContext.addService(debugger);
+    }
+
     /**
      * Configures security vaults refresh such as AWS, Azure, Google.
      */
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java
index 2a93cddcc03..a490f9f913e 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java
@@ -29,8 +29,10 @@ import org.apache.camel.builder.LambdaRouteBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceAware;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.util.AntPathMatcher;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StopWatch;
@@ -143,6 +145,16 @@ public class DefaultRoutesCollector implements RoutesCollector {
             }
         }
 
+        // the route may have source code available so attempt to load as resource
+        for (RoutesBuilder route : routes) {
+            if (route instanceof ResourceAware ra && ra.getResource() == null) {
+                Resource r = ResourceHelper.resolveResource(camelContext, "source:" + route.getClass().getName());
+                if (r != null && r.exists()) {
+                    ra.setResource(r);
+                }
+            }
+        }
+
         return routes;
     }
 
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
index e53ee102704..dc07f0d36cc 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
@@ -18,18 +18,25 @@ package org.apache.camel.dsl.jbang.core.commands;
 
 import java.io.BufferedReader;
 import java.io.Console;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.Reader;
 import java.io.StringWriter;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.camel.dsl.jbang.core.commands.action.MessageTableHelper;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
@@ -46,11 +53,22 @@ import org.apache.camel.util.concurrent.ThreadHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
+import org.apache.maven.model.Activation;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.PluginExecution;
+import org.apache.maven.model.Profile;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.fusesource.jansi.Ansi;
 import org.fusesource.jansi.AnsiConsole;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
+import static org.apache.camel.dsl.jbang.core.common.CommandLineHelper.CAMEL_JBANG_WORK_DIR;
 import static org.apache.camel.util.IOHelper.buffered;
 
 @Command(name = "debug", description = "Debug local Camel integration", sortOptions = false, showDefaultValues = true)
@@ -172,8 +190,14 @@ public class Debug extends Run {
                     printer().println(line);
                 }
                 // and any error
-                String text = IOHelper.loadText(spawnError);
-                printer().println(text);
+                if (spawnError != null) {
+                    try {
+                        String text = IOHelper.loadText(spawnError);
+                        printer().println(text);
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                }
                 return -1;
             }
         } while (exit == 0 && !quit.get());
@@ -183,23 +207,25 @@ public class Debug extends Run {
 
     private void doReadLog(AtomicBoolean quit) {
         do {
-            InputStreamReader isr = new InputStreamReader(spawnOutput);
-            try {
-                BufferedReader reader = buffered(isr);
-                while (true) {
-                    String line = reader.readLine();
-                    if (line != null) {
-                        while (logBuffer.size() >= 100) {
-                            logBuffer.remove(0);
+            if (spawnOutput != null) {
+                InputStreamReader isr = new InputStreamReader(spawnOutput);
+                try {
+                    BufferedReader reader = buffered(isr);
+                    while (true) {
+                        String line = reader.readLine();
+                        if (line != null) {
+                            while (logBuffer.size() >= 100) {
+                                logBuffer.remove(0);
+                            }
+                            logBuffer.add(line);
+                            logUpdated.set(true);
+                        } else {
+                            break;
                         }
-                        logBuffer.add(line);
-                        logUpdated.set(true);
-                    } else {
-                        break;
                     }
+                } catch (Exception e) {
+                    // ignore
                 }
-            } catch (Exception e) {
-                // ignore
             }
         } while (!quit.get());
     }
@@ -237,6 +263,137 @@ public class Debug extends Run {
 
     @Override
     protected int runDebug(KameletMain main) throws Exception {
+        // is this a maven project
+        File pom = new File("pom.xml");
+        if (pom.isFile() && pom.exists()) {
+            // is this a spring-boot project
+            try (InputStream is = new FileInputStream(pom)) {
+                String text = IOHelper.loadText(is);
+                if (text.contains("camel-spring-boot-bom")) {
+                    return doRunDebugSpringBoot(main);
+                }
+            }
+        }
+        return doRunDebug(main);
+    }
+
+    private int doRunDebugSpringBoot(KameletMain main) throws Exception {
+        Path pom = Paths.get("pom.xml");
+        MavenXpp3Reader mavenReader = new MavenXpp3Reader();
+        try (Reader reader = Files.newBufferedReader(pom)) {
+            Model model = mavenReader.read(reader);
+
+            // include camel-debug dependency
+            Dependency d = new Dependency();
+            d.setGroupId("org.apache.camel.springboot");
+            d.setArtifactId("camel-debug-starter");
+            model.getDependencies().add(d);
+            d = new Dependency();
+            d.setGroupId("org.apache.camel.springboot");
+            d.setArtifactId("camel-cli-connector-starter");
+            model.getDependencies().add(d);
+
+            Profile mp = new Profile();
+            model.addProfile(mp);
+            mp.setId("camel-debug");
+            Activation a = new Activation();
+            a.setActiveByDefault(true);
+            mp.setActivation(a);
+
+            Build b = new Build();
+            mp.setBuild(b);
+
+            Plugin pi = new Plugin();
+            b.addPlugin(pi);
+            pi.setGroupId("org.springframework.boot");
+            pi.setArtifactId("spring-boot-maven-plugin");
+            pi.setVersion("${spring-boot-version}");
+            PluginExecution pe = new PluginExecution();
+            pe.addGoal("repackage");
+            pi.addExecution(pe);
+            Xpp3Dom cfg = new Xpp3Dom("finalName");
+            cfg.setValue("camel-jbang-debug");
+            Xpp3Dom root = new Xpp3Dom("configuration");
+            root.addChild(cfg);
+            pi.setConfiguration(root);
+
+            MavenXpp3Writer w = new MavenXpp3Writer();
+            FileOutputStream fos = new FileOutputStream("camel-jbang-debug-pom.xml", false);
+            w.write(fos, model);
+            IOHelper.close(fos);
+
+            printer().println("Preparing Camel Spring Boot for debugging ...");
+
+            // use maven wrapper if present
+            String mvnw = "/mvnw";
+            if (FileUtil.isWindows()) {
+                mvnw = "/mvnw.cmd";
+            }
+            if (!new File(mvnw).exists()) {
+                mvnw = "mvn";
+            }
+            // use maven to build the JAR and then run the JAR after-wards
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(mvnw, "-Dmaven.test.skip", "--file", "camel-jbang-debug-pom.xml", "package", "spring-boot:repackage");
+            Process p = pb.start();
+
+            if (p.waitFor(30, TimeUnit.SECONDS)) {
+                AtomicReference<Process> processRef = new AtomicReference<>();
+                Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+                    try {
+                        // We need to wait for the process to exit before doing any cleanup
+                        Process process = processRef.get();
+                        if (process != null) {
+                            process.destroy();
+                            for (int i = 0; i < 30; i++) {
+                                if (!process.isAlive()) {
+                                    break;
+                                }
+                                try {
+                                    Thread.sleep(1000);
+                                } catch (InterruptedException e) {
+                                    Thread.currentThread().interrupt();
+                                }
+                            }
+                        }
+                        removeDir(Paths.get(RUN_PLATFORM_DIR));
+                        removeDir(Paths.get(CAMEL_JBANG_WORK_DIR));
+                        Files.deleteIfExists(Paths.get("camel-jbang-debug-pom.xml"));
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }));
+
+                // okay build is complete then run java
+                pb = new ProcessBuilder();
+                pb.command("java",
+                        "-Dcamel.debug.enabled=true",
+                        (breakpoint == null
+                                ? "-Dcamel.debug.breakpoints=_all_routes_" : "-Dcamel.debug.breakpoints=" + breakpoint),
+                        "-Dcamel.debug.loggingLevel=DEBUG",
+                        "-Dcamel.debug.singleStepIncludeStartEnd=true",
+                        loggingColor ? "-Dspring.output.ansi.enabled=ALWAYS" : "-Dspring.output.ansi.enabled=NEVER",
+                        "-jar", "target/camel-jbang-debug.jar");
+
+                p = pb.start();
+                processRef.set(p);
+                this.spawnOutput = p.getInputStream();
+                this.spawnError = p.getErrorStream();
+                this.spawnPid = p.pid();
+                printer().println("Debugging Camel Spring Boot integration: " + name + " with PID: " + p.pid());
+            } else {
+                printer().printErr("Timed out preparing Camel Spring Boot for debugging");
+                this.spawnError = p.getErrorStream();
+                this.spawnPid = p.pid();
+                p.destroy();
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+
+    protected int doRunDebug(KameletMain main) throws Exception {
         List<String> cmds = new ArrayList<>(spec.commandLine().getParseResult().originalArgs());
 
         // debug should be run
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index ea5c63e702c..fa3a84dd8bc 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -87,7 +87,7 @@ public class Run extends CamelCommand {
     public static final String RUN_JAVA_SH = "classpath:templates/run-java.sh";
 
     public static final String RUN_SETTINGS_FILE = "camel-jbang-run.properties";
-    private static final String RUN_PLATFORM_DIR = ".camel-jbang-run";
+    public static final String RUN_PLATFORM_DIR = ".camel-jbang-run";
 
     private static final String[] ACCEPTED_XML_ROOT_ELEMENT_NAMES = new String[] {
             "route", "routes",
@@ -1228,7 +1228,7 @@ public class Run extends CamelCommand {
         if (FileUtil.isWindows()) {
             mvnw = "/mvnw.cmd";
         }
-        pb.command(runDirPath.toString() + mvnw, "--quiet", "--file",
+        pb.command(runDirPath + mvnw, "--quiet", "--file",
                 runDirPath.toRealPath().resolve("pom.xml").toString(),
                 "spring-boot:run");
 
@@ -2013,7 +2013,7 @@ public class Run extends CamelCommand {
         }
     }
 
-    private static void removeDir(Path directory) {
+    protected static void removeDir(Path directory) {
         try {
             if (Files.exists(directory)) {
                 Files.walk(directory)