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 5f40eb4fb576 CAMEL-26930: camel-jbang - --jvm-debug and --jvm-args run
in a JVM of the launcher, not through jbang (#29921)
5f40eb4fb576 is described below
commit 5f40eb4fb5768f9f989d3b7b472d0f812119cf1d
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 19:25:19 2026 +0100
CAMEL-26930: camel-jbang - --jvm-debug and --jvm-args run in a JVM of the
launcher, not through jbang (#29921)
Under the Camel launcher, --jvm-debug, --jvm-args and
--open-telemetry-agent re-ran the
integration through jbang: without jbang they failed with exit 70, and with
jbang the run
used another Camel build on jbang's JDK.
Under the launcher (and without --camel-version) --jvm-debug and --jvm-args
start a JVM
of the launcher with those JVM options (--jvm-debug waits for the debugger,
as jbang
--debug does). --open-telemetry-agent, whose agent jbang downloads, exits 2
with a clear
message under the launcher, as --camel-version does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../modules/ROOT/pages/camel-jbang-debugging.adoc | 4 ++
.../apache/camel/dsl/jbang/core/commands/Run.java | 75 ++++++++++++++++++++++
.../core/commands/RunLauncherJvmCommandTest.java | 67 +++++++++++++++++++
3 files changed, 146 insertions(+)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
index c80f246ba659..3a228958bec7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
@@ -139,6 +139,10 @@
https://code.visualstudio.com/docs/java/java-debugging#_attach[VS Code], or
https://www.vogella.com/tutorials/EclipseDebugging/article.html#remote-debugging[Eclipse].
Port configuration is described in the
https://www.jbang.dev/documentation/guide/latest/debugging.html[JBang
Debugging] guide.
+The integration runs in a new JVM that waits until a debugger attaches, and
`--jvm-args` gives that JVM more options.
+With the Camel launcher (the Camel CLI installed without JBang), it is a JVM
of the launcher, listening on
+`localhost`. With JBang, `jbang --debug` starts it. `--open-telemetry-agent`
needs the Camel CLI installed via JBang.
+
=== JBang-level debugging
Use JBang's `--debug` flag to debug any CLI command (not just `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 67ae5ba0551a..d6a3027b8e06 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
@@ -859,6 +859,15 @@ public class Run extends CamelCommand implements
CommandLifecycle {
}
// okay we have validated all input and are ready to run
+ if (camelVersion == null && isDebugMode() &&
LauncherHelper.isRunningFromLauncher()) {
+ // the launcher has no jbang: a JVM of the launcher with the JVM
options (CAMEL-26930)
+ if (debugOptions.openTelemetryAgent) {
+ printer().printErr("The --open-telemetry-agent option is not
supported when running from camel-launcher.");
+ printer().printDiagnostic("Install Camel CLI via JBang to use
it: jbang app install camel@apache/camel");
+ return CliExitCode.USAGE;
+ }
+ return runLauncherJvm();
+ }
if (camelVersion != null || isDebugMode()) {
// TODO: debug camel specific version
boolean custom = false;
@@ -1255,6 +1264,72 @@ public class Run extends CamelCommand implements
CommandLifecycle {
}
}
+ /**
+ * Runs this integration in a new JVM of the launcher, with the JVM
options of --jvm-debug and --jvm-args. Before,
+ * they ran it through jbang, which the launcher does not need, and which
ran another Camel build on its own JDK.
+ */
+ protected int runLauncherJvm() throws Exception {
+ List<String> cmds =
createLauncherJvmCommand(LauncherHelper.getCamelCommand());
+
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command(cmds);
+ markSpawned(pb);
+
+ if (verbose) {
+ printer().println(String.join(" ", cmds));
+ }
+
+ if (background) {
+ return runBackgroundProcess(pb, "Camel Main");
+ } else {
+ pb.inheritIO(); // run in foreground (with IO so logs are visible)
+ Process p = pb.start();
+ this.spawnPid = p.pid();
+ // wait for that process to exit as we run in foreground
+ return p.waitFor();
+ }
+ }
+
+ /**
+ * The command that runs this integration in a new JVM of the launcher:
the java command of the launcher, with the
+ * JVM options of --jvm-debug (suspended until a debugger attaches, as
jbang --debug does) and --jvm-args, and the
+ * run options without them.
+ *
+ * @param camelCommand the command of the launcher ({@link
LauncherHelper#getCamelCommand()}): java, its options,
+ * -jar and the launcher JAR
+ */
+ List<String> createLauncherJvmCommand(List<String> camelCommand) {
+ List<String> cmds;
+ if (originalArgs() != null) {
+ cmds = new ArrayList<>(originalArgs());
+ } else {
+ cmds = new ArrayList<>();
+ cmds.add("run");
+ }
+ if (background) {
+ cmds = BackgroundWait.withoutBackgroundOptions(cmds);
+ }
+ List<String> jvm = new ArrayList<>();
+ // tooling may signal to run JMX debugger in suspended mode via JVM
system property
+ String debugSuspend =
System.getProperty(BacklogDebugger.SUSPEND_MODE_SYSTEM_PROP_NAME);
+ if (debugSuspend != null) {
+ jvm.add("-D" + BacklogDebugger.SUSPEND_MODE_SYSTEM_PROP_NAME + "="
+ debugSuspend);
+ }
+ if (jvmDebugPort > 0) {
+
jvm.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:"
+ jvmDebugPort);
+ cmds.removeIf(arg -> arg.startsWith("--jvm-debug"));
+ }
+ if (jvmArgs != null && !jvmArgs.isBlank()) {
+ jvm.addAll(Arrays.asList(jvmArgs.trim().split("\\s+")));
+ cmds.removeIf(a -> a.startsWith("--jvm-args"));
+ }
+ List<String> answer = new ArrayList<>(camelCommand);
+ int jar = answer.indexOf("-jar");
+ answer.addAll(jar >= 0 ? jar : answer.size(), jvm);
+ answer.addAll(cmds);
+ return answer;
+ }
+
// The jbang command that runs this integration in a new JVM.
List<String> createCamelVersionCommand() {
List<String> cmds;
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunLauncherJvmCommandTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunLauncherJvmCommandTest.java
new file mode 100644
index 000000000000..a3621a252495
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunLauncherJvmCommandTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands;
+
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.core.common.CommandLineTestHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Under the launcher, --jvm-debug and --jvm-args run the integration in a JVM
of the launcher, with those JVM options.
+ * Before, they went through jbang, which the launcher does not need, and
which ran another Camel build on its own JDK
+ * (CAMEL-26930).
+ */
+class RunLauncherJvmCommandTest {
+
+ private static final List<String> LAUNCHER = List.of("/jdk/bin/java",
"-Xmx512m", "-jar", "/opt/camel/lib/launcher.jar");
+
+ @Test
+ void jvmDebugOpensADebugSocketInTheLauncherJvm() {
+ List<String> cmd = command("route.camel.yaml", "--jvm-debug=5005");
+
+ // the arguments of the command line follow, without --jvm-debug (here
without run, which a test does not give)
+ assertThat(cmd).containsExactly("/jdk/bin/java", "-Xmx512m",
+
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005",
+ "-jar", "/opt/camel/lib/launcher.jar", "route.camel.yaml");
+ assertThat(cmd).doesNotContain("jbang");
+ }
+
+ @Test
+ void jvmArgsGoToTheLauncherJvm() {
+ List<String> cmd = command("route.camel.yaml", "--jvm-args=-Xmx1g
-Dfoo=bar");
+
+ assertThat(cmd).containsSequence("-Xmx1g", "-Dfoo=bar", "-jar");
+ assertThat(cmd).noneMatch(arg -> arg.startsWith("--jvm-args"));
+ assertThat(cmd).endsWith("/opt/camel/lib/launcher.jar",
"route.camel.yaml");
+ }
+
+ @Test
+ void inTheBackgroundTheJvmDoesNotGoToTheBackgroundAgain() {
+ List<String> cmd = command("route.camel.yaml", "--jvm-debug=5005",
"--background");
+
+ assertThat(cmd).doesNotContain("--background");
+ }
+
+ private static List<String> command(String... args) {
+ Run run = new Run(new CamelJBangMain());
+ CommandLineTestHelper.parse(run, args);
+ return run.createLauncherJvmCommand(LAUNCHER);
+ }
+}