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 2026/07/03 17:29:05 UTC
(camel) branch main updated (e4878810af67 -> 0eec94759e40)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
from e4878810af67 CAMEL-23893: Fix flaky SpringFileWatcherTest.testDefaultConfig on JDK 25
new 92f52bb3d6c5 CAMEL-23831: Add --jvm-args option to camel run and TUI run dialog
new b94520859178 CAMEL-23831: Add intermediate time markers to heap chart x-axis in camel-tui
new 0eec94759e40 CAMEL-23831: Use stacked colors in heap chart bars in camel-tui
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../pages/jbang-commands/camel-jbang-debug.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-dev.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 1 +
.../ROOT/partials/jbang-commands/examples/run.adoc | 14 ++
.../META-INF/camel-jbang-commands-metadata.json | 6 +-
.../apache/camel/dsl/jbang/core/commands/Run.java | 40 ++++-
.../dsl/jbang/core/commands/tui/ActionsPopup.java | 5 +-
.../dsl/jbang/core/commands/tui/MemoryTab.java | 89 +++++++----
.../jbang/core/commands/tui/RunOptionsForm.java | 163 +++++++++++++++++++--
9 files changed, 269 insertions(+), 51 deletions(-)
(camel) 02/03: CAMEL-23831: Add intermediate time markers to heap chart x-axis in camel-tui
Posted by da...@apache.org.
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
commit b94520859178b362f6967f1b219c9f5b001a90ed
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Jul 3 18:46:50 2026 +0200
CAMEL-23831: Add intermediate time markers to heap chart x-axis in camel-tui
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
.../dsl/jbang/core/commands/tui/MemoryTab.java | 61 ++++++++++++++++------
1 file changed, 44 insertions(+), 17 deletions(-)
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 529cfaf9adc3..a269e1576d51 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
@@ -316,31 +316,58 @@ class MemoryTab extends AbstractTab {
private void renderTimeAxis(Frame frame, Rect area, IntegrationInfo info) {
LinkedList<Long> hist = heapMemHistory.get(info.pid);
int points = hist != null ? hist.size() : 0;
- long totalSeconds = points * 5L;
-
- String timeLabel;
- if (totalSeconds < 60) {
- timeLabel = totalSeconds + "s ago";
- } else {
- timeLabel = (totalSeconds / 60) + "m ago";
- }
int w = area.width();
if (w < 10) {
return;
}
- // Pad to fill the width: "Xm ago" on the left, "now" on the right
- String left = " " + timeLabel;
- String right = "now ";
- int gap = Math.max(1, w - left.length() - right.length());
+ // The chart area has the same width; each column = 1 data point = 5 seconds
+ // chartW columns map to the rightmost `chartW` points of history
+ int chartW = w;
+ int totalPoints = Math.min(points, chartW);
+ long totalSeconds = totalPoints * 5L;
+
+ Buffer buf = frame.buffer();
+ Style dimStyle = Style.EMPTY.dim();
+ int xAxisY = area.y();
+ int startX = area.x();
+
+ // "now" label at the right edge
+ int nowX = startX + chartW - 3;
+ if (nowX >= startX) {
+ buf.setString(nowX, xAxisY, "now", dimStyle);
+ }
- Line line = Line.from(
- Span.styled(left, Style.EMPTY.dim()),
- Span.raw(" ".repeat(gap)),
- Span.styled(right, Style.EMPTY.dim()));
+ // Pick step interval based on total time span
+ int stepSeconds;
+ if (totalSeconds <= 120) {
+ stepSeconds = 30;
+ } else if (totalSeconds <= 300) {
+ stepSeconds = 60;
+ } else if (totalSeconds <= 900) {
+ stepSeconds = 120;
+ } else {
+ stepSeconds = 300;
+ }
- frame.renderWidget(Paragraph.builder().text(Text.from(line)).build(), area);
+ // Place markers from right to left at regular time intervals
+ for (int s = stepSeconds; s <= totalSeconds; s += stepSeconds) {
+ int col = chartW - 1 - (s / 5);
+ if (col < 0) {
+ break;
+ }
+ String label;
+ if (s < 60) {
+ label = "-" + s + "s";
+ } else {
+ label = "-" + (s / 60) + "m";
+ }
+ int markerX = startX + col;
+ if (markerX + label.length() <= startX + chartW - 4 && markerX >= startX) {
+ buf.setString(markerX, xAxisY, label, dimStyle);
+ }
+ }
}
private static Span computeTrendSpan(LinkedList<Long> hist) {
(camel) 01/03: CAMEL-23831: Add --jvm-args option to camel run and TUI run dialog
Posted by da...@apache.org.
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
commit 92f52bb3d6c56c3cdf42f8d6ba907cb0ea353aa3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Jul 3 17:28:59 2026 +0200
CAMEL-23831: Add --jvm-args option to camel run and TUI run dialog
- Add --jvm-args CLI option for passing JVM arguments (e.g. -Xms128m -Xmx256m)
- Wire into all three runtimes: JBang (--java-options), Spring Boot
(-Dspring-boot.run.jvmArguments), Quarkus (-Djvm.args)
- Spawn separate JVM via JBang when --jvm-args is set for Camel Main
- Add Init heap and Max heap fields to TUI run dialog with placeholder hints
- Add keystroke-level input validation (digits + single k/m/g suffix)
- Add launch-time validation (port range, max >= init heap)
- Show validation errors inline in the run dialog
- Color heap chart bars individually based on heap load at each point in time
- Document --jvm-args with examples in run.adoc
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
.../pages/jbang-commands/camel-jbang-debug.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-dev.adoc | 1 +
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 1 +
.../ROOT/partials/jbang-commands/examples/run.adoc | 14 ++
.../META-INF/camel-jbang-commands-metadata.json | 6 +-
.../apache/camel/dsl/jbang/core/commands/Run.java | 40 ++++-
.../dsl/jbang/core/commands/tui/ActionsPopup.java | 5 +-
.../dsl/jbang/core/commands/tui/MemoryTab.java | 12 +-
.../jbang/core/commands/tui/RunOptionsForm.java | 163 +++++++++++++++++++--
9 files changed, 212 insertions(+), 31 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index 56291aa0ad2e..8302578f9113 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -40,6 +40,7 @@ camel debug [options]
| `--java-version,--java` | Java version (21, 25) | 21 | String
| `--jfr` | Enables Java Flight Recorder saving recording to disk on exit | false | boolean
| `--jfr-profile` | Java Flight Recorder profile to use (such as default or profile) | | String
+| `--jvm-args` | Additional JVM arguments (e.g. -Xmx256m -Xms128m) | | String
| `--jvm-debug` | To enable JVM remote debugging on port 4004 by default. The supported values are true to enable the remote debugging, false to disable the remote debugging or a number to use a custom port | | int
| `--kamelets-version` | Apache Camel Kamelets version (auto-detected from classpath if not set) | | String
| `--lazy-bean` | Whether to use lazy bean initialization (can help with complex classloading issues) | false | boolean
diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
index 271834e0c8c0..7e2fa362fbb3 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
@@ -38,6 +38,7 @@ camel dev [options]
| `--java-version,--java` | Java version (21, 25) | 21 | String
| `--jfr` | Enables Java Flight Recorder saving recording to disk on exit | false | boolean
| `--jfr-profile` | Java Flight Recorder profile to use (such as default or profile) | | String
+| `--jvm-args` | Additional JVM arguments (e.g. -Xmx256m -Xms128m) | | String
| `--jvm-debug` | To enable JVM remote debugging on port 4004 by default. The supported values are true to enable the remote debugging, false to disable the remote debugging or a number to use a custom port | | int
| `--kamelets-version` | Apache Camel Kamelets version (auto-detected from classpath if not set) | | String
| `--lazy-bean` | Whether to use lazy bean initialization (can help with complex classloading issues) | false | boolean
diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 79eba60ebcc2..554c5aab82b5 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -38,6 +38,7 @@ camel run [options]
| `--java-version,--java` | Java version (21, 25) | 21 | String
| `--jfr` | Enables Java Flight Recorder saving recording to disk on exit | false | boolean
| `--jfr-profile` | Java Flight Recorder profile to use (such as default or profile) | | String
+| `--jvm-args` | Additional JVM arguments (e.g. -Xmx256m -Xms128m) | | String
| `--jvm-debug` | To enable JVM remote debugging on port 4004 by default. The supported values are true to enable the remote debugging, false to disable the remote debugging or a number to use a custom port | | int
| `--kamelets-version` | Apache Camel Kamelets version (auto-detected from classpath if not set) | | String
| `--lazy-bean` | Whether to use lazy bean initialization (can help with complex classloading issues) | false | boolean
diff --git a/docs/user-manual/modules/ROOT/partials/jbang-commands/examples/run.adoc b/docs/user-manual/modules/ROOT/partials/jbang-commands/examples/run.adoc
index 9610a6b09f77..e6eb95721105 100644
--- a/docs/user-manual/modules/ROOT/partials/jbang-commands/examples/run.adoc
+++ b/docs/user-manual/modules/ROOT/partials/jbang-commands/examples/run.adoc
@@ -27,3 +27,17 @@ Run with additional dependencies:
----
camel run hello.java --dep=camel-jackson
----
+
+Run with custom JVM memory settings (init and max heap):
+
+[source,bash]
+----
+camel run hello.java --jvm-args="-Xms128m -Xmx256m"
+----
+
+Run with max heap only:
+
+[source,bash]
+----
+camel run hello.java --jvm-args="-Xmx512m"
+----
diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 48228ac50fbe..d30315094616 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -6,9 +6,9 @@
{ "name": "cmd", "fullName": "cmd", "description": "Performs commands in the running Camel integrations, such as start\/stop route, or change logging levels.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "browse", "fullName": "cmd browse", "description": "Browse pending messages on endpoints [...]
{ "name": "completion", "fullName": "completion", "description": "Generate completion script for bash\/zsh", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] },
{ "name": "config", "fullName": "config", "description": "Get and set user configuration values", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", "fullName": "config get", "description": "Display user configuration value", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
- { "name": "debug", "fullName": "debug", "description": "Debug local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To [...]
+ { "name": "debug", "fullName": "debug", "description": "Debug local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To [...]
{ "name": "dependency", "fullName": "dependency", "description": "Displays all Camel dependencies required to run", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "copy", "fullName": "dependency copy", "description": "Copies all Camel dependencies required to run to a specific directory", "sourc [...]
- { "name": "dev", "fullName": "dev", "description": "Run in dev mode with live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To wait for run in background to startup successfully, before returning", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, [...]
+ { "name": "dev", "fullName": "dev", "description": "Run in dev mode with live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To wait for run in background to startup successfully, before returning", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, [...]
{ "name": "dirty", "fullName": "dirty", "description": "Check if there are dirty files from previous Camel runs that did not terminate gracefully", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty", "options": [ { "names": "--clean", "description": "Clean dirty files which are no longer in use", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", " [...]
{ "name": "doc", "fullName": "doc", "description": "Shows documentation for kamelet, component, and other Camel resources", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet [...]
{ "name": "doctor", "fullName": "doctor", "description": "Checks the environment and reports potential issues", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] },
@@ -26,7 +26,7 @@
{ "name": "plugin", "fullName": "plugin", "description": "Manage plugins that add sub-commands to this CLI", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "add", "fullName": "plugin add", "description": "Add new plugin", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.plugin.PluginA [...]
{ "name": "ps", "fullName": "ps", "description": "List running Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListProcess", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--pid", "description": "List only pid in the output", "javaType": "boolean", "type": "boolean" }, { "names": "--remote", "description": "Break down counters into remote\/total pairs", "javaType": [...]
{ "name": "restart", "fullName": "restart", "description": "Restarts running Camel integrations (stop + re-launch)", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.RestartProcess", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] },
- { "name": "run", "fullName": "run", "description": "Run as local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To wait for run in background to startup successfully, before returning", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { [...]
+ { "name": "run", "fullName": "run", "description": "Run as local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To wait for run in background to startup successfully, before returning", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { [...]
{ "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX or SPDX SBOM for a specific project", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ { "names": "--build-property", "description": "Maven build properties, ex. --build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { "names": "--camel-spring-boot-version", "description": "Camel version to use with Spring Boot", "javaType": "java.lang.String", "type" [...]
{ "name": "script", "fullName": "script", "description": "Run Camel integration as shell script for terminal scripting", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names": "--logging", "description": "Can be used to turn on logging (logs to file in <user home>\/.camel directory)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--logging-level", "description": "Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
{ "name": "shell", "fullName": "shell", "description": "Interactive Camel CLI shell.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Shell", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] },
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 b3336b687eb9..230163bbb11e 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
@@ -261,6 +261,10 @@ public class Run extends CamelCommand {
"enable the remote debugging, false to disable the remote debugging or a number to use a custom port")
int jvmDebugPort;
+ @Option(names = { "--jvm-args" },
+ description = "Additional JVM arguments (e.g. -Xmx256m -Xms128m)")
+ String jvmArgs;
+
@Option(names = { "--name" }, defaultValue = "CamelJBang", description = "The name of the Camel application")
String name;
@@ -558,7 +562,8 @@ public class Run extends CamelCommand {
}
private boolean isDebugMode() {
- return jvmDebugPort > 0 || debugOptions.openTelemetryAgent;
+ return jvmDebugPort > 0 || debugOptions.openTelemetryAgent
+ || (jvmArgs != null && !jvmArgs.isBlank());
}
private void writeSetting(KameletMain main, Properties existing, String key, String value) {
@@ -1398,9 +1403,17 @@ public class Run extends CamelCommand {
mvnw = "/mvnw.cmd";
}
ProcessBuilder pb = new ProcessBuilder();
- pb.command(runDirPath + mvnw, "--quiet", "--file",
- runDirPath.toRealPath().resolve("pom.xml").toString(), "package",
- "quarkus:" + (dev ? "dev" : "run"));
+ List<String> mvnCmd = new ArrayList<>();
+ mvnCmd.add(runDirPath + mvnw);
+ mvnCmd.add("--quiet");
+ mvnCmd.add("--file");
+ mvnCmd.add(runDirPath.toRealPath().resolve("pom.xml").toString());
+ if (jvmArgs != null && !jvmArgs.isBlank()) {
+ mvnCmd.add("-Djvm.args=" + jvmArgs.trim());
+ }
+ mvnCmd.add("package");
+ mvnCmd.add("quarkus:" + (dev ? "dev" : "run"));
+ pb.command(mvnCmd);
pb.inheritIO(); // run in foreground (with IO so logs are visible)
Process p = pb.start();
@@ -1522,9 +1535,16 @@ public class Run extends CamelCommand {
if (FileUtil.isWindows()) {
mvnw = "/mvnw.cmd";
}
- pb.command(runDirPath + mvnw, "--quiet", "--file",
- runDirPath.toRealPath().resolve("pom.xml").toString(),
- "spring-boot:run");
+ List<String> mvnCmd = new ArrayList<>();
+ mvnCmd.add(runDirPath + mvnw);
+ mvnCmd.add("--quiet");
+ mvnCmd.add("--file");
+ mvnCmd.add(runDirPath.toRealPath().resolve("pom.xml").toString());
+ if (jvmArgs != null && !jvmArgs.isBlank()) {
+ mvnCmd.add("-Dspring-boot.run.jvmArguments=" + jvmArgs.trim());
+ }
+ mvnCmd.add("spring-boot:run");
+ pb.command(mvnCmd);
pb.inheritIO(); // run in foreground (with IO so logs are visible)
Process p = pb.start();
@@ -1728,6 +1748,12 @@ public class Run extends CamelCommand {
jbangArgs.add("--debug=" + jvmDebugPort); // jbang --debug=port
cmds.removeIf(arg -> arg.startsWith("--jvm-debug"));
}
+ if (jvmArgs != null && !jvmArgs.isBlank()) {
+ for (String arg : jvmArgs.trim().split("\\s+")) {
+ jbangArgs.add("--java-options=" + arg);
+ }
+ cmds.removeIf(a -> a.startsWith("--jvm-args"));
+ }
if (debugOptions.openTelemetryAgent) {
boolean jaegerExport = "jaeger".equals(debugOptions.openTelemetryAgentExport);
jbangArgs.add("--javaagent=io.opentelemetry.javaagent:opentelemetry-javaagent:RELEASE");
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 6a85b2f3d91d..5f5373b39c45 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
@@ -540,7 +540,10 @@ class ActionsPopup {
showExampleBrowser = true;
}
} else if (ke.isConfirm()) {
- if (selectedFolder != null) {
+ String error = runOptionsForm.validate();
+ if (error != null) {
+ runOptionsForm.setError(error);
+ } else if (selectedFolder != null) {
launchFolder();
} else {
launchWithName();
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 a3a9acaabfc3..529cfaf9adc3 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
@@ -266,9 +266,6 @@ class MemoryTab extends AbstractTab {
ceiling = observedMax;
}
- long pct = ceiling > 0 ? info.heapMemUsed * 100 / ceiling : 0;
- Color barColor = pct >= 80 ? Color.LIGHT_RED : pct >= 60 ? Color.YELLOW : Color.GREEN;
-
String title = String.format(" Heap Usage (%s / %s committed) ", formatBytes(info.heapMemUsed), formatBytes(ceiling));
// Render the block border first
@@ -291,11 +288,14 @@ class MemoryTab extends AbstractTab {
data[dataOffset + (i - startIdx)] = hist.get(i);
}
- // Render multi-row bar chart into the buffer
+ // Render multi-row bar chart with per-column color based on heap load at that point
Buffer buf = frame.buffer();
- Style barStyle = Style.EMPTY.fg(barColor);
for (int col = 0; col < chartW; col++) {
+ long colPct = ceiling > 0 ? data[col] * 100 / ceiling : 0;
+ Color colColor = colPct >= 80 ? Color.LIGHT_RED : colPct >= 60 ? Color.YELLOW : Color.GREEN;
+ Style colStyle = Style.EMPTY.fg(colColor);
+
double ratio = (double) data[col] / ceiling;
// Total eighths this column fills (chartH rows * 8 eighths per row)
double fillEighths = ratio * chartH * 8.0;
@@ -307,7 +307,7 @@ class MemoryTab extends AbstractTab {
int x = inner.x() + col;
int rowEighths = Math.min(8, Math.max(0, totalEighths - row * 8));
if (rowEighths > 0) {
- buf.setString(x, y, BAR_EIGHTHS[rowEighths], barStyle);
+ buf.setString(x, y, BAR_EIGHTHS[rowEighths], colStyle);
}
}
}
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RunOptionsForm.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RunOptionsForm.java
index 967947065946..77e395313c09 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RunOptionsForm.java
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RunOptionsForm.java
@@ -47,18 +47,21 @@ class RunOptionsForm {
private static final int ROW_RUNTIME = 1;
private static final int ROW_PROFILE = 2;
private static final int ROW_PORT = 3;
- private static final int ROW_MAX = 4;
- private static final int ROW_CONSOLE = 5;
- private static final int ROW_DEV = 6;
- private static final int ROW_OBSERVE = 7;
- private static final int ROW_TRACE = 8;
- private static final int ROW_STUB = 9;
- private static final int ROW_OTEL_AGENT = 10;
- private static final int ROW_COUNT = 11;
+ private static final int ROW_INIT_HEAP = 4;
+ private static final int ROW_MAX_HEAP = 5;
+ private static final int ROW_MAX = 6;
+ private static final int ROW_CONSOLE = 7;
+ private static final int ROW_DEV = 8;
+ private static final int ROW_OBSERVE = 9;
+ private static final int ROW_TRACE = 10;
+ private static final int ROW_STUB = 11;
+ private static final int ROW_OTEL_AGENT = 12;
+ private static final int ROW_COUNT = 13;
private boolean visible;
private int page;
private int selectedRow;
+ private String errorMessage;
private static final String[] MAX_MODES = { "Max seconds:", "Max messages:", "Max idle secs:" };
private static final String[] MAX_FLAGS = { "--max-seconds=", "--max-messages=", "--max-idle-seconds=" };
@@ -70,6 +73,8 @@ class RunOptionsForm {
// Text fields
private TextInputState nameInput;
private TextInputState portInput;
+ private TextInputState initHeapInput;
+ private TextInputState maxHeapInput;
private TextInputState maxInput;
private int maxMode;
private int runtimeMode;
@@ -102,6 +107,8 @@ class RunOptionsForm {
void open(String defaultName, String exampleName, boolean bundled, boolean dev) {
nameInput = new TextInputState(defaultName != null ? defaultName : "");
portInput = new TextInputState("");
+ initHeapInput = new TextInputState("");
+ maxHeapInput = new TextInputState("");
maxInput = new TextInputState("");
maxMode = 0;
runtimeMode = 0;
@@ -133,6 +140,10 @@ class RunOptionsForm {
return stubMode;
}
+ void setError(String error) {
+ this.errorMessage = error;
+ }
+
boolean isJaegerExport() {
return otelAgent && otelExportTarget == 1;
}
@@ -204,6 +215,21 @@ class RunOptionsForm {
if (!port.isEmpty()) {
args.add("--port=" + port);
}
+ StringBuilder jvmArgs = new StringBuilder();
+ String initHeap = initHeapInput.text().trim();
+ if (!initHeap.isEmpty()) {
+ jvmArgs.append("-Xms").append(initHeap);
+ }
+ String maxHeap = maxHeapInput.text().trim();
+ if (!maxHeap.isEmpty()) {
+ if (!jvmArgs.isEmpty()) {
+ jvmArgs.append(" ");
+ }
+ jvmArgs.append("-Xmx").append(maxHeap);
+ }
+ if (!jvmArgs.isEmpty()) {
+ args.add("--jvm-args=" + jvmArgs);
+ }
String maxVal = maxInput.text().trim();
if (!maxVal.isEmpty() && !"0".equals(maxVal)) {
args.add(MAX_FLAGS[maxMode] + maxVal);
@@ -247,6 +273,7 @@ class RunOptionsForm {
// ---- Options page (page 0) ----
private boolean handleOptionsPage(KeyEvent ke) {
+ errorMessage = null;
if (ke.isUp()) {
selectedRow = (selectedRow - 1 + ROW_COUNT) % ROW_COUNT;
return true;
@@ -336,7 +363,11 @@ class RunOptionsForm {
if (selectedRow <= ROW_MAX && selectedRow != ROW_RUNTIME && selectedRow != ROW_PROFILE) {
TextInputState active = activeInput();
if (active != null) {
- handleTextInput(ke, active, selectedRow == ROW_PORT || selectedRow == ROW_MAX);
+ if (selectedRow == ROW_INIT_HEAP || selectedRow == ROW_MAX_HEAP) {
+ handleHeapInput(ke, active);
+ } else {
+ handleTextInput(ke, active, selectedRow == ROW_PORT || selectedRow == ROW_MAX);
+ }
}
return true;
}
@@ -444,7 +475,7 @@ class RunOptionsForm {
private void renderOptionsPage(Frame frame, Rect area) {
int popupW = Math.min(64, area.width() - 4);
- int popupH = 15;
+ int popupH = errorMessage != null ? 18 : 17;
int x = area.left() + Math.max(0, (area.width() - popupW) / 2);
int y = area.top() + Math.max(0, (area.height() - popupH) / 4);
Rect popup = new Rect(x, y, Math.min(popupW, area.width()), Math.min(popupH, area.height()));
@@ -466,7 +497,7 @@ class RunOptionsForm {
int innerX = popup.left() + 2;
int innerW = popup.width() - 4;
- int labelW = 16;
+ int labelW = 18;
int fieldW = innerW - labelW;
int rowY = popup.top() + 1;
@@ -486,6 +517,16 @@ class RunOptionsForm {
renderTextInput(frame, innerX + labelW, rowY, fieldW, portInput, selectedRow == ROW_PORT);
rowY++;
+ renderLabel(frame, innerX, rowY, labelW, "Init heap (-Xms):", selectedRow == ROW_INIT_HEAP);
+ renderTextInputWithHint(frame, innerX + labelW, rowY, fieldW, initHeapInput, selectedRow == ROW_INIT_HEAP,
+ "e.g. 128m, 1g");
+ rowY++;
+
+ renderLabel(frame, innerX, rowY, labelW, "Max heap (-Xmx):", selectedRow == ROW_MAX_HEAP);
+ renderTextInputWithHint(frame, innerX + labelW, rowY, fieldW, maxHeapInput, selectedRow == ROW_MAX_HEAP,
+ "e.g. 256m, 2g");
+ rowY++;
+
renderLabel(frame, innerX, rowY, labelW, MAX_MODES[maxMode], selectedRow == ROW_MAX);
renderTextInput(frame, innerX + labelW, rowY, fieldW, maxInput, selectedRow == ROW_MAX);
rowY++;
@@ -519,6 +560,12 @@ class RunOptionsForm {
Span.styled(" ", Style.EMPTY),
Span.styled(jaegerLabel, jaegerStyle))), exportArea);
}
+ if (errorMessage != null) {
+ rowY++;
+ Rect errorArea = new Rect(innerX, rowY, innerW, 1);
+ frame.renderWidget(Paragraph.from(Line.from(
+ Span.styled("⚠ " + errorMessage, Style.EMPTY.bold()))), errorArea);
+ }
}
private void renderPropertiesPage(Frame frame, Rect area) {
@@ -638,11 +685,89 @@ class RunOptionsForm {
return switch (selectedRow) {
case ROW_NAME -> nameInput;
case ROW_PORT -> portInput;
+ case ROW_INIT_HEAP -> initHeapInput;
+ case ROW_MAX_HEAP -> maxHeapInput;
case ROW_MAX -> maxInput;
default -> null;
};
}
+ private void handleHeapInput(KeyEvent ke, TextInputState active) {
+ if (ke.isDeleteBackward()) {
+ active.deleteBackward();
+ } else if (ke.isDeleteForward()) {
+ active.deleteForward();
+ } else if (ke.isLeft()) {
+ active.moveCursorLeft();
+ } else if (ke.isRight()) {
+ active.moveCursorRight();
+ } else if (ke.isHome()) {
+ active.moveCursorToStart();
+ } else if (ke.isEnd()) {
+ active.moveCursorToEnd();
+ } else if (ke.code() == KeyCode.CHAR) {
+ char c = ke.string().charAt(0);
+ String text = active.text();
+ if (Character.isDigit(c)) {
+ // only allow digits before any suffix
+ if (text.isEmpty() || Character.isDigit(text.charAt(text.length() - 1))) {
+ active.insert(c);
+ }
+ } else if ((c == 'k' || c == 'm' || c == 'g') && !text.isEmpty()
+ && Character.isDigit(text.charAt(text.length() - 1))) {
+ active.insert(c);
+ }
+ }
+ }
+
+ String validate() {
+ String port = portInput.text().trim();
+ if (!port.isEmpty()) {
+ try {
+ int p = Integer.parseInt(port);
+ if (p < 0 || p > 65535) {
+ return "Port must be 0-65535";
+ }
+ } catch (NumberFormatException e) {
+ return "Invalid port: " + port;
+ }
+ }
+ String initHeap = initHeapInput.text().trim();
+ String maxHeap = maxHeapInput.text().trim();
+ if (!initHeap.isEmpty() && !isValidHeap(initHeap)) {
+ return "Invalid init heap: " + initHeap;
+ }
+ if (!maxHeap.isEmpty() && !isValidHeap(maxHeap)) {
+ return "Invalid max heap: " + maxHeap;
+ }
+ if (!initHeap.isEmpty() && !maxHeap.isEmpty()) {
+ long initBytes = parseHeapBytes(initHeap);
+ long maxBytes = parseHeapBytes(maxHeap);
+ if (initBytes > maxBytes) {
+ return "Init heap cannot exceed max heap";
+ }
+ }
+ return null;
+ }
+
+ private static boolean isValidHeap(String value) {
+ return value.matches("\\d+[kmg]?");
+ }
+
+ private static long parseHeapBytes(String value) {
+ char last = value.charAt(value.length() - 1);
+ if (Character.isDigit(last)) {
+ return Long.parseLong(value);
+ }
+ long num = Long.parseLong(value.substring(0, value.length() - 1));
+ return switch (last) {
+ case 'k' -> num * 1024;
+ case 'm' -> num * 1024 * 1024;
+ case 'g' -> num * 1024 * 1024 * 1024;
+ default -> num;
+ };
+ }
+
private void handleTextInput(KeyEvent ke, TextInputState active, boolean digitsOnly) {
if (ke.isDeleteBackward()) {
active.deleteBackward();
@@ -674,6 +799,11 @@ class RunOptionsForm {
}
private void renderTextInput(Frame frame, int x, int y, int w, TextInputState state, boolean active) {
+ renderTextInputWithHint(frame, x, y, w, state, active, null);
+ }
+
+ private void renderTextInputWithHint(
+ Frame frame, int x, int y, int w, TextInputState state, boolean active, String hint) {
Rect inputArea = new Rect(x, y, w, 1);
if (active) {
TextInput textInput = TextInput.builder()
@@ -682,9 +812,14 @@ class RunOptionsForm {
frame.renderStatefulWidget(textInput, inputArea, state);
} else {
String text = state.text();
- Style style = text.isEmpty() ? Style.EMPTY.dim() : Style.EMPTY;
- frame.renderWidget(Paragraph.from(Line.from(
- Span.styled(text.isEmpty() ? "—" : text, style))), inputArea);
+ if (text.isEmpty() && hint != null) {
+ frame.renderWidget(Paragraph.from(Line.from(
+ Span.styled(hint, Style.EMPTY.dim()))), inputArea);
+ } else {
+ Style style = text.isEmpty() ? Style.EMPTY.dim() : Style.EMPTY;
+ frame.renderWidget(Paragraph.from(Line.from(
+ Span.styled(text.isEmpty() ? "—" : text, style))), inputArea);
+ }
}
}
(camel) 03/03: CAMEL-23831: Use stacked colors in heap chart bars in camel-tui
Posted by da...@apache.org.
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
commit 0eec94759e404520d68c251a8a2236aaff875ea8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Jul 3 19:28:37 2026 +0200
CAMEL-23831: Use stacked colors in heap chart bars in camel-tui
Green at bottom (<60%), yellow in middle (60-80%), light red at top (>80%)
so each bar visually shows the severity zones of heap usage.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
.../dsl/jbang/core/commands/tui/MemoryTab.java | 28 +++++++++++++++-------
1 file changed, 19 insertions(+), 9 deletions(-)
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 a269e1576d51..3081c54bff79 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
@@ -288,18 +288,18 @@ class MemoryTab extends AbstractTab {
data[dataOffset + (i - startIdx)] = hist.get(i);
}
- // Render multi-row bar chart with per-column color based on heap load at that point
+ // Render multi-row bar chart with stacked colors: green (<60%), yellow (60-80%), light red (>80%)
Buffer buf = frame.buffer();
+ // Precompute eighths thresholds for the color bands
+ int greenEighths = (int) Math.round(0.6 * chartH * 8.0);
+ int yellowEighths = (int) Math.round(0.8 * chartH * 8.0);
+ Style greenStyle = Style.EMPTY.fg(Color.GREEN);
+ Style yellowStyle = Style.EMPTY.fg(Color.YELLOW);
+ Style redStyle = Style.EMPTY.fg(Color.LIGHT_RED);
for (int col = 0; col < chartW; col++) {
- long colPct = ceiling > 0 ? data[col] * 100 / ceiling : 0;
- Color colColor = colPct >= 80 ? Color.LIGHT_RED : colPct >= 60 ? Color.YELLOW : Color.GREEN;
- Style colStyle = Style.EMPTY.fg(colColor);
-
double ratio = (double) data[col] / ceiling;
- // Total eighths this column fills (chartH rows * 8 eighths per row)
- double fillEighths = ratio * chartH * 8.0;
- int totalEighths = (int) Math.round(fillEighths);
+ int totalEighths = (int) Math.round(ratio * chartH * 8.0);
// Render from bottom to top
for (int row = 0; row < chartH; row++) {
@@ -307,7 +307,17 @@ class MemoryTab extends AbstractTab {
int x = inner.x() + col;
int rowEighths = Math.min(8, Math.max(0, totalEighths - row * 8));
if (rowEighths > 0) {
- buf.setString(x, y, BAR_EIGHTHS[rowEighths], colStyle);
+ // Determine color based on vertical position in the bar
+ int eighthsAtRowBottom = row * 8;
+ Style style;
+ if (eighthsAtRowBottom >= yellowEighths) {
+ style = redStyle;
+ } else if (eighthsAtRowBottom >= greenEighths) {
+ style = yellowStyle;
+ } else {
+ style = greenStyle;
+ }
+ buf.setString(x, y, BAR_EIGHTHS[rowEighths], style);
}
}
}