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 072590ed4f97 CAMEL-23385: camel-diagram - Add watch option (#23121)
072590ed4f97 is described below
commit 072590ed4f9727b48273f727516f15365e598172
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon May 11 16:13:53 2026 +0200
CAMEL-23385: camel-diagram - Add watch option (#23121)
* CAMEL-23385: camel-diagram - Add watch option
* CAMEL-23385: camel-diagram - Auto refresh and remove text tree dump (used
internally for testing)
---
.../main/camel-main-configuration-metadata.json | 2 +-
.../camel-diagram/src/main/docs/diagram.adoc | 26 +----
.../apache/camel/diagram/DiagramDevConsole.java | 54 ++++------
.../apache/camel/diagram/RouteDiagramRenderer.java | 10 +-
.../camel/diagram/DiagramDevConsoleTest.java | 11 --
.../camel-main-configuration-metadata.json | 2 +-
core/camel-main/src/main/docs/main.adoc | 2 +-
.../camel/main/DefaultConfigurationProperties.java | 4 +-
.../camel-jbang-cmd-route-diagram.adoc | 3 +-
.../META-INF/camel-jbang-commands-metadata.json | 2 +-
.../core/commands/action/ActionWatchCommand.java | 15 ++-
.../commands/action/CamelRouteDiagramAction.java | 117 +++++++++++++--------
12 files changed, 125 insertions(+), 123 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index 64c944a9d65a..8cb1b1af58f7 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -55,7 +55,7 @@
{ "name": "camel.main.contextReloadEnabled", "required": false,
"description": "Used for enabling context reloading. If enabled then Camel
allow external systems such as security vaults (AWS secrets manager, etc.) to
trigger refreshing Camel by updating property placeholders and reload all
existing routes to take changes into effect.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.main.description", "required": false, "description":
"Sets the description (intended for humans) of the Camel application.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.main.devConsoleEnabled", "required": false,
"description": "Whether to enable developer console (requires camel-console on
classpath). The developer console is only for assisting during development.
This is NOT for production usage.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false, "security":
"insecure:dev" },
- { "name": "camel.main.dumpRoutes", "required": false, "description": "If
dumping is enabled then Camel will during startup dump all loaded routes (incl
rests and route templates) represented as XML\/YAML DSL into the log. This is
intended for trouble shooting or to assist during development. Sensitive
information that may be configured in the route endpoints could potentially be
included in the dump output and is therefore not recommended being used for
production usage. This require [...]
+ { "name": "camel.main.dumpRoutes", "required": false, "description": "If
dumping is enabled then Camel will during startup dump all loaded routes (incl
rests and route templates) represented as XML\/YAML DSL into the log. This is
intended for trouble shooting or to assist during development. Sensitive
information that may be configured in the route endpoints could potentially be
included in the dump output and is therefore not recommended being used for
production usage. This require [...]
{ "name": "camel.main.dumpRoutesGeneratedIds", "required": false,
"description": "Whether to include auto generated IDs in the dumped output.
Default is false.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.main.dumpRoutesInclude", "required": false,
"description": "Controls what to include in output for route dumping. Possible
values: all, routes, rests, routeConfigurations, routeTemplates, beans,
dataFormats. Multiple values can be separated by comma. Default is routes.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "routes", "secret":
false },
{ "name": "camel.main.dumpRoutesLog", "required": false, "description":
"Whether to log route dumps to Logger", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": true, "secret": false },
diff --git a/components/camel-diagram/src/main/docs/diagram.adoc
b/components/camel-diagram/src/main/docs/diagram.adoc
index 814acfacb812..d4517d27a0c8 100644
--- a/components/camel-diagram/src/main/docs/diagram.adoc
+++ b/components/camel-diagram/src/main/docs/diagram.adoc
@@ -10,13 +10,11 @@
*Since Camel {since}*
The Diagram module provides route diagram rendering capabilities for Apache
Camel routes.
-It can generate visual route diagrams as PNG images or text-based tree
representations
-from route structure data.
+It can generate visual route diagrams as PNG images representations from route
structure data.
== Features
* Render route diagrams as PNG images with colored nodes and scope boxes
-* Generate text-based tree diagrams for terminal output
* Support for all Camel EIPs: choice, doTry/doCatch, filter, split, loop,
multicast, and more
* Scope boxes visually group branching and scoping EIPs
* Multiple color themes: dark, light, transparent, or custom
@@ -70,16 +68,6 @@ BufferedImage image = renderer.renderDiagram(List.of(lr),
lr.maxY + RouteDiagram
ImageIO.write(image, "PNG", new File("diagram.png"));
----
-=== Text diagram
-
-For terminal or log output, generate a text-based tree:
-
-[source,java]
-----
-List<String> lines = renderer.printTextDiagram(routes);
-lines.forEach(System.out::println);
-----
-
=== With Camel JBang
The diagram rendering is used by the `camel cmd route-diagram` command in
Camel JBang:
@@ -96,11 +84,10 @@ The following built-in themes are available:
* `dark` - dark background (default)
* `light` - light background
* `transparent` - transparent background
-* `text` - to use tree diagram instead of visual diagram
Custom colors can be specified using the format:
-[source,text]
+[source,properties]
----
bg=#1e1e1e:from=#2e7d32:to=#1565c0:eip=#8957e5:choice=#d29922
----
@@ -113,12 +100,3 @@ To use dark theme
----
camel cmd route-diagram MyRoute.java --theme=dark
----
-
-To print textual tree diagram instead of visual
-
-[source,bash]
-----
-camel cmd route-diagram MyRoute.java --theme=tree
-----
-
-TIP: You can also show diagrams for an existing running integration by
executing `camel cmd diagram`.
diff --git
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
index 261ae7e50646..74265961117b 100644
---
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
+++
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.StringJoiner;
-import org.apache.camel.console.DevConsoleRegistry;
import org.apache.camel.spi.RouteDiagramDumper;
import org.apache.camel.spi.annotations.DevConsole;
import org.apache.camel.support.PluginHelper;
@@ -36,7 +35,7 @@ public class DiagramDevConsole extends AbstractDevConsole {
public static final String FILTER = "filter";
/**
- * Theme to use: dark, light, or text
+ * Theme to use: dark or light
*/
public static final String THEME = "theme";
@@ -60,6 +59,11 @@ public class DiagramDevConsole extends AbstractDevConsole {
*/
public static final String METRIC = "metric";
+ /**
+ * Whether to auto-refresh page every 5 seconds
+ */
+ public static final String AUTO_REFRESH = "autoRefresh";
+
public DiagramDevConsole() {
super("camel", "route-diagram", "Route Diagram", "Visual route
diagrams");
}
@@ -76,24 +80,24 @@ public class DiagramDevConsole extends AbstractDevConsole {
.parseInt(options.getOrDefault(NODE_WIDTH, "" +
RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH).toString());
String nodeLabel = (String) options.getOrDefault(NODE_LABEL,
RouteDiagramDumper.NodeLabelMode.CODE.name());
boolean metric = "true".equalsIgnoreCase((String)
options.getOrDefault(METRIC, "true"));
+ boolean refresh = "true".equalsIgnoreCase((String)
options.getOrDefault(AUTO_REFRESH, "true"));
- // special for text
- if ("text".equalsIgnoreCase(theme)) {
- sj.add(renderTextTheme(filter));
- } else {
- try {
- RouteDiagramDumper dumper =
PluginHelper.getRouteDiagramDumper(getCamelContext());
- BufferedImage image = dumper.dumpRoutesAsImage(filter,
RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()),
- metric,
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth,
fontSize);
- String base64 = dumper.imageToBase64(image);
- // For HTML embedding:
- String html = String.format(
- "<html>\n<body>\n<img src=\"data:image/png;base64,%s\"
alt=\"Route Diagram\">\n</body>\n</html>",
- base64);
- sj.add(html);
- } catch (Exception e) {
- // ignore
+ try {
+ RouteDiagramDumper dumper =
PluginHelper.getRouteDiagramDumper(getCamelContext());
+ BufferedImage image = dumper.dumpRoutesAsImage(filter,
RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()),
+ metric,
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth,
fontSize);
+ String base64 = dumper.imageToBase64(image);
+ // For HTML embedding:
+ String html = String.format(
+ " <body>\n <img src=\"data:image/png;base64,%s\"
alt=\"Route Diagram\">\n </body>\n",
+ base64);
+ if (refresh) {
+ html = "<head><meta http-equiv=\"refresh\"
content=\"5\"></head>\n" + html;
}
+ html = "<html>\n" + html + "</html>\n";
+ sj.add(html);
+ } catch (Exception e) {
+ // ignore
}
return sj.toString();
@@ -124,18 +128,4 @@ public class DiagramDevConsole extends AbstractDevConsole {
return root;
}
- private String renderTextTheme(String filter) {
- final StringJoiner sj = new StringJoiner("\n");
-
- org.apache.camel.console.DevConsole dc
- =
getCamelContext().getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
- .resolveById("route-structure");
- JsonObject root = (JsonObject) dc.call(MediaType.JSON,
Map.of("filter", filter));
- var routes = RouteDiagramHelper.parseRoutes(root);
- RouteDiagramRenderer renderer = new RouteDiagramRenderer();
- var lines = renderer.printTextDiagram(routes);
- lines.forEach(sj::add);
- sj.add("");
- return sj.toString();
- }
}
diff --git
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
index d261ffbbd135..89f4839c5158 100644
---
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
+++
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
@@ -451,11 +451,17 @@ public class RouteDiagramRenderer {
};
}
- public List<String> printTextDiagram(List<RouteInfo> routes) {
+ /**
+ * Used for testing
+ */
+ List<String> printTextDiagram(List<RouteInfo> routes) {
return printTextDiagram(routes,
RouteDiagramLayoutEngine.NodeLabelMode.CODE);
}
- public List<String> printTextDiagram(List<RouteInfo> routes,
RouteDiagramLayoutEngine.NodeLabelMode mode) {
+ /**
+ * Used for testing
+ */
+ List<String> printTextDiagram(List<RouteInfo> routes,
RouteDiagramLayoutEngine.NodeLabelMode mode) {
List<String> lines = new ArrayList<>();
for (RouteInfo route : routes) {
lines.add("");
diff --git
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
index 017118151793..681a48877ad1 100644
---
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
+++
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
@@ -17,7 +17,6 @@
package org.apache.camel.diagram;
import java.util.Base64;
-import java.util.Map;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.console.DevConsole;
@@ -74,16 +73,6 @@ class DiagramDevConsoleTest extends CamelTestSupport {
assertThat(text).contains("data:image/png;base64,");
}
- @Test
- void testTextOutputTextTheme() {
- DevConsole console = resolveConsole();
- String text = (String) console.call(DevConsole.MediaType.TEXT,
Map.of(DiagramDevConsole.THEME, "text"));
- assertThat(text).contains("Route: myRoute");
- assertThat(text).contains("Route: otherRoute");
- assertThat(text).contains("[from]");
- assertThat(text).contains("[choice]");
- }
-
@Test
void testJsonOutput() {
System.setProperty("java.awt.headless", "true");
diff --git
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 64c944a9d65a..8cb1b1af58f7 100644
---
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -55,7 +55,7 @@
{ "name": "camel.main.contextReloadEnabled", "required": false,
"description": "Used for enabling context reloading. If enabled then Camel
allow external systems such as security vaults (AWS secrets manager, etc.) to
trigger refreshing Camel by updating property placeholders and reload all
existing routes to take changes into effect.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.main.description", "required": false, "description":
"Sets the description (intended for humans) of the Camel application.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "secret": false },
{ "name": "camel.main.devConsoleEnabled", "required": false,
"description": "Whether to enable developer console (requires camel-console on
classpath). The developer console is only for assisting during development.
This is NOT for production usage.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false, "security":
"insecure:dev" },
- { "name": "camel.main.dumpRoutes", "required": false, "description": "If
dumping is enabled then Camel will during startup dump all loaded routes (incl
rests and route templates) represented as XML\/YAML DSL into the log. This is
intended for trouble shooting or to assist during development. Sensitive
information that may be configured in the route endpoints could potentially be
included in the dump output and is therefore not recommended being used for
production usage. This require [...]
+ { "name": "camel.main.dumpRoutes", "required": false, "description": "If
dumping is enabled then Camel will during startup dump all loaded routes (incl
rests and route templates) represented as XML\/YAML DSL into the log. This is
intended for trouble shooting or to assist during development. Sensitive
information that may be configured in the route endpoints could potentially be
included in the dump output and is therefore not recommended being used for
production usage. This require [...]
{ "name": "camel.main.dumpRoutesGeneratedIds", "required": false,
"description": "Whether to include auto generated IDs in the dumped output.
Default is false.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": false, "secret": false },
{ "name": "camel.main.dumpRoutesInclude", "required": false,
"description": "Controls what to include in output for route dumping. Possible
values: all, routes, rests, routeConfigurations, routeTemplates, beans,
dataFormats. Multiple values can be separated by comma. Default is routes.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"string", "javaType": "java.lang.String", "defaultValue": "routes", "secret":
false },
{ "name": "camel.main.dumpRoutesLog", "required": false, "description":
"Whether to log route dumps to Logger", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": true, "secret": false },
diff --git a/core/camel-main/src/main/docs/main.adoc
b/core/camel-main/src/main/docs/main.adoc
index cb17c720af87..aabcf2682df1 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -49,7 +49,7 @@ The camel.main supports 131 options, which are listed below.
| *camel.main.contextReloadEnabled* | Used for enabling context reloading. If
enabled then Camel allow external systems such as security vaults (AWS secrets
manager, etc.) to trigger refreshing Camel by updating property placeholders
and reload all existing routes to take changes into effect. | false | boolean
| *camel.main.description* | Sets the description (intended for humans) of the
Camel application. | | String
| *camel.main.devConsoleEnabled* | Whether to enable developer console
(requires camel-console on classpath). The developer console is only for
assisting during development. This is NOT for production usage. | false |
boolean
-| *camel.main.dumpRoutes* | If dumping is enabled then Camel will during
startup dump all loaded routes (incl rests and route templates) represented as
XML/YAML DSL into the log. This is intended for trouble shooting or to assist
during development. Sensitive information that may be configured in the route
endpoints could potentially be included in the dump output and is therefore not
recommended being used for production usage. This requires to have
camel-xml-io/camel-yaml-io on the cla [...]
+| *camel.main.dumpRoutes* | If dumping is enabled then Camel will during
startup dump all loaded routes (incl rests and route templates) represented as
XML/YAML DSL into the log. This is intended for trouble shooting or to assist
during development. Sensitive information that may be configured in the route
endpoints could potentially be included in the dump output and is therefore not
recommended being used for production usage. This requires to have
camel-xml-io/camel-yaml-io on the cla [...]
| *camel.main.dumpRoutesGeneratedIds* | Whether to include auto generated IDs
in the dumped output. Default is false. | false | boolean
| *camel.main.dumpRoutesInclude* | Controls what to include in output for
route dumping. Possible values: all, routes, rests, routeConfigurations,
routeTemplates, beans, dataFormats. Multiple values can be separated by comma.
Default is routes. | routes | String
| *camel.main.dumpRoutesLog* | Whether to log route dumps to Logger | true |
boolean
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
index 33059fba3bee..966cb50486c9 100644
---
a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
+++
b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
@@ -1581,7 +1581,7 @@ public abstract class DefaultConfigurationProperties<T> {
*
* You can also use png to save route diagrams as PNG image files either
all combined in a single file (default
* camel-route-diagrams.png) or to a given folder, where routes are
grouped by source file name(s) and saved as
- * corresponding .png files.\ This requires to have camel-diagram on the
classpath to be able to render PNG
+ * corresponding .png files. This requires to have camel-diagram on the
classpath to be able to render PNG
* diagrams.
*/
public void setDumpRoutes(String dumpRoutes) {
@@ -2814,7 +2814,7 @@ public abstract class DefaultConfigurationProperties<T> {
*
* You can also use png to save route diagrams as PNG image files either
all combined in a single file (default
* camel-route-diagrams.png) or to a given folder, where routes are
grouped by source file name(s) and saved as
- * corresponding .png files.\ This requires to have camel-diagram on the
classpath to be able to render PNG
+ * corresponding .png files. This requires to have camel-diagram on the
classpath to be able to render PNG
* diagrams.
*/
public T withDumpRoutes(String dumpRoutes) {
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
index be9889a6c1cd..d118b519b5c9 100644
---
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
+++
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
@@ -26,7 +26,8 @@ camel cmd route-diagram [options]
| `--metric` | Whether to include live metrics (only possible for running
Camel application) | true | boolean
| `--node-label` | What text to display in diagram nodes: code, description,
or both (default) | both | String
| `--output` | Save diagram to a PNG file instead of displaying in terminal |
| String
-| `--theme,--colors` | Color theme preset (dark, light, transparent, text) or
custom colors (e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or
ANSI color names (e.g. from=seagreen:to=steelblue). Use bg= for transparent.
Can also be set via DIAGRAM_COLORS env var. | dark | String
+| `--theme` | Color theme preset (dark, light, transparent) or custom colors
(e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or ANSI color
names (e.g. from=seagreen:to=steelblue). Use bg= for transparent. Can also be
set via DIAGRAM_COLORS env var. | transparent | String
+| `--watch` | Execute periodically and showing output fullscreen | | boolean
| `--width` | Image width in pixels (0 = auto) | 0 | int
| `-h,--help` | Display the help and sub-commands | | boolean
|===
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 9e3f577faf42..9e7a209d62c0 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
@@ -2,7 +2,7 @@
"commands": [
{ "name": "bind", "fullName": "bind", "description": "DEPRECATED: Bind
source and sink Kamelets as a new Camel integration", "deprecated": true,
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.bind.Bind", "options":
[ { "names": "--error-handler", "description": "Add error handler
(none|log|sink:<endpoint>). Sink endpoints are expected in the format
[[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet
name.", "javaType": "java.lang.String", "type": "stri [...]
{ "name": "catalog", "fullName": "catalog", "description": "List artifacts
from Camel Catalog", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand", "options": [
{ "names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"component", "fullName": "catalog component", "description": "List components
from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.co [...]
- { "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": "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 [...]
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java
index 976a356c2337..5bc634eed4dd 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java
@@ -31,7 +31,8 @@ abstract class ActionWatchCommand extends ActionBaseCommand {
description = "Execute periodically and showing output
fullscreen")
boolean watch;
- private CommandHelper.ReadConsoleTask waitUserTask;
+ private Runnable waitUserTask;
+ final AtomicBoolean running = new AtomicBoolean(true);
protected ActionWatchCommand(CamelJBangMain main) {
super(main);
@@ -43,7 +44,7 @@ abstract class ActionWatchCommand extends ActionBaseCommand {
final AtomicBoolean running = new AtomicBoolean(true);
if (watch) {
Thread t = new Thread(() -> {
- waitUserTask = new CommandHelper.ReadConsoleTask(() ->
running.set(false));
+ waitUserTask = waitForUserEnter();
waitUserTask.run();
}, "WaitForUser");
t.start();
@@ -53,7 +54,7 @@ abstract class ActionWatchCommand extends ActionBaseCommand {
// use 2-sec delay in watch mode
try {
StopWatch watch = new StopWatch();
- while (running.get() && watch.taken() < 2000) {
+ while (running.get() && watchWait(watch)) {
Thread.sleep(100);
}
} catch (Exception e) {
@@ -67,10 +68,18 @@ abstract class ActionWatchCommand extends ActionBaseCommand
{
return exit;
}
+ protected Runnable waitForUserEnter() {
+ return new CommandHelper.ReadConsoleTask(() -> running.set(false));
+ }
+
protected void clearScreen() {
AnsiConsole.out().print(Ansi.ansi().eraseScreen().cursor(1, 1));
}
+ protected boolean watchWait(StopWatch watch) {
+ return watch.taken() < 2000;
+ }
+
protected abstract Integer doWatchCall() throws Exception;
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
index c4989bfc8485..d79fdc876a10 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
@@ -23,7 +23,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.imageio.ImageIO;
@@ -41,17 +40,21 @@ import
org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
import org.apache.camel.dsl.jbang.core.common.PathUtils;
import org.apache.camel.main.KameletMain;
import org.apache.camel.support.PatternHelper;
+import org.apache.camel.util.StopWatch;
import org.apache.camel.util.json.JsonObject;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.impl.TerminalGraphics;
import org.jline.terminal.impl.TerminalGraphicsManager;
+import org.jline.utils.InfoCmp;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@Command(name = "route-diagram", description = "Display Camel route diagram in
the terminal", sortOptions = false,
showDefaultValues = true)
-public class CamelRouteDiagramAction extends ActionBaseCommand {
+public class CamelRouteDiagramAction extends ActionWatchCommand {
@CommandLine.Parameters(description = "Source file name, or name/pid of a
running Camel integration", arity = "0..1")
String name = "*";
@@ -68,12 +71,12 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
description = "Save diagram to a PNG file instead of
displaying in terminal")
String output;
- @CommandLine.Option(names = { "--theme", "--colors" },
- description = "Color theme preset (dark, light,
transparent, text) or custom colors "
+ @CommandLine.Option(names = { "--theme" },
+ description = "Color theme preset (dark, light,
transparent) or custom colors "
+ "(e.g.
bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or "
+ "ANSI color names (e.g.
from=seagreen:to=steelblue). "
+ "Use bg= for transparent. Can also be
set via DIAGRAM_COLORS env var.",
- defaultValue = "dark")
+ defaultValue = "transparent")
String theme;
@CommandLine.Option(names = { "--font-size" },
@@ -99,6 +102,11 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
private volatile long pid;
+ private DiagramColors colors;
+ private Terminal terminal;
+ private TerminalGraphics terminalGraphics;
+ private LineReader lineReader;
+
public CamelRouteDiagramAction(CamelJBangMain main) {
super(main);
}
@@ -107,9 +115,25 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
public Integer doCall() throws Exception {
System.setProperty("java.awt.headless", "true");
- String colorSpec = System.getenv("DIAGRAM_COLORS");
- DiagramColors colors = !"text".equals(theme) ?
DiagramColors.parse(colorSpec != null ? colorSpec : theme) : null;
+ // if output in terminal then ensure terminal supports this
+ if (output == null) {
+ String colorSpec = System.getenv("DIAGRAM_COLORS");
+ colors = DiagramColors.parse(colorSpec != null ? colorSpec :
theme);
+ terminal = TerminalBuilder.builder().system(true).build();
+ terminalGraphics =
TerminalGraphicsManager.getBestProtocol(terminal).orElse(null);
+ if (terminalGraphics == null) {
+ printer().println("Terminal does not support graphics
protocols (Kitty, iTerm2, or Sixel).");
+ printer().println("Try running in a supported terminal: Kitty,
iTerm2, WezTerm, Ghostty, or VS Code.");
+ return 1;
+ }
+ lineReader =
LineReaderBuilder.builder().terminal(terminal).build();
+ }
+ return super.doCall();
+ }
+
+ @Override
+ protected Integer doWatchCall() throws Exception {
Path outputFile;
int exit = 0;
List<Long> pids = findPids(name);
@@ -143,7 +167,7 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
List<RouteInfo> routes = parseRoutes(jo);
if (routes.isEmpty()) {
printer().println("No routes found");
- return 0;
+ return 1;
}
if (filter != null) {
@@ -155,7 +179,7 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
if (routes.isEmpty()) {
printer().println("No routes match filter: " + filter);
- return 0;
+ return 1;
}
NodeLabelMode labelMode = parseNodeLabelMode(nodeLabel);
@@ -164,13 +188,6 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
engine.getNodeWidth(), fontSize *
RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(),
pid > 0 && metric);
- if ("text".equals(theme)) {
- for (String line : renderer.printTextDiagram(routes,
labelMode)) {
- printer().println(line);
- }
- return 0;
- }
-
List<LayoutRoute> layoutRoutes = new ArrayList<>();
int currentY = RouteDiagramLayoutEngine.PADDING;
for (RouteInfo route : routes) {
@@ -196,35 +213,10 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
ImageIO.write(image, "PNG", file);
printer().println("Diagram saved to: " +
file.getAbsolutePath());
} else {
- try (Terminal terminal =
TerminalBuilder.builder().system(true).build()) {
- try {
- Optional<TerminalGraphics> protocol =
TerminalGraphicsManager.getBestProtocol(terminal);
- if (protocol.isPresent()) {
- TerminalGraphics.ImageOptions opts = new
TerminalGraphics.ImageOptions()
- .preserveAspectRatio(true);
- if (width > 0) {
- opts.width(width);
- }
- protocol.get().displayImage(terminal, image, opts);
- terminal.writer().println();
- terminal.flush();
- } else {
- printer().println(
- "Terminal does not support graphics
protocols (Kitty, iTerm2, or Sixel).");
- printer().println(
- "Try running in a supported terminal:
Kitty, iTerm2, WezTerm, Ghostty, or VS Code.");
- for (String line :
renderer.printTextDiagram(routes, labelMode)) {
- printer().println(line);
- }
- }
- } catch (IOException | UnsupportedOperationException e) {
- printer().println("Failed to display diagram in
terminal: " + e.getMessage());
- printer().println("Falling back to text diagram.");
- for (String line : renderer.printTextDiagram(routes,
labelMode)) {
- printer().println(line);
- }
- }
+ if (watch) {
+ clearScreen();
}
+ doDisplayDiagram(image);
}
return 0;
@@ -233,6 +225,43 @@ public class CamelRouteDiagramAction extends
ActionBaseCommand {
}
}
+ @Override
+ protected boolean watchWait(StopWatch watch) {
+ return watch.taken() < 5000;
+ }
+
+ @Override
+ protected void clearScreen() {
+ if (terminal != null) {
+ terminal.puts(InfoCmp.Capability.clear_screen);
+ terminal.flush();
+ }
+ }
+
+ @Override
+ protected Runnable waitForUserEnter() {
+ return () -> {
+ if (lineReader != null) {
+ try {
+ lineReader.readLine();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ };
+ }
+
+ private void doDisplayDiagram(BufferedImage image) throws IOException {
+ TerminalGraphics.ImageOptions opts = new
TerminalGraphics.ImageOptions()
+ .preserveAspectRatio(true);
+ if (width > 0) {
+ opts.width(width);
+ }
+ terminalGraphics.displayImage(terminal, image, opts);
+ terminal.writer().println();
+ terminal.flush();
+ }
+
private void doCallPid(Long pid) {
this.pid = pid;