diff --git a/components/camel-diagram/src/main/docs/diagram.adoc b/components/camel-diagram/src/main/docs/diagram.adoc index d4517d27a0c8e..87b765dedc59d 100644 --- a/components/camel-diagram/src/main/docs/diagram.adoc +++ b/components/camel-diagram/src/main/docs/diagram.adoc @@ -10,14 +10,15 @@ *Since Camel {since}* The Diagram module provides route diagram rendering capabilities for Apache Camel routes. -It can generate visual route diagrams as PNG images representations from route structure data. +It can generate visual route diagrams as PNG images or plain ASCII art text representations from route structure data. == Features * Render route diagrams as PNG images with colored nodes and scope boxes +* Render route diagrams as plain ASCII art text 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 +* Multiple color themes: dark, light, transparent, or custom (PNG only) == Usage @@ -35,7 +36,7 @@ Add the `camel-diagram` dependency to your project: ==== Using Camel Java API -You can use the diagram render with Camel based APIs such as: +You can use the diagram renderer with the Camel API to render as PNG images: [source,java] ---- @@ -43,6 +44,14 @@ RouteDiagramDumper dumper = PluginHelper.getRouteDiagramDumper(context); BufferedImage image = dumper.dumpRoutesAsImage("*", RouteDiagramDumper.Theme.DARK); ---- +Or render as ASCII art text: + +[source,java] +---- +RouteDiagramDumper dumper = PluginHelper.getRouteDiagramDumper(context); +String ascii = dumper.dumpRoutesAsAsciiArt("*"); +---- + ==== Using standalone Java API Then use the API to render diagrams: @@ -68,6 +77,25 @@ BufferedImage image = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagram ImageIO.write(image, "PNG", new File("diagram.png")); ---- +To render as ASCII art instead: + +[source,java] +---- +import org.apache.camel.diagram.*; +import org.apache.camel.diagram.RouteDiagramLayoutEngine.*; + +// Parse route structure from JSON +List routes = RouteDiagramHelper.parseRoutes(jsonObject); + +// Layout and render as ASCII +RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); +LayoutRoute lr = engine.layoutRoute(routes.get(0), RouteDiagramLayoutEngine.PADDING); + +RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); +String ascii = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); +System.out.println(ascii); +---- + === With Camel JBang The diagram rendering is used by the `camel cmd route-diagram` command in Camel JBang: @@ -100,3 +128,92 @@ To use dark theme ---- camel cmd route-diagram MyRoute.java --theme=dark ---- + +== ASCII Art Rendering + +The ASCII art renderer produces plain text diagrams using box-drawing characters. +This is useful for terminal output where images cannot be displayed. + +Nodes are drawn as boxes using `+`, `-`, and `|` characters, with arrows using `|` and `v`. +Branching EIPs (choice, multicast, etc.) produce L-shaped arrows with horizontal connector lines. +Long labels are automatically wrapped to fit within the box width. + +Example output for a simple route: + +[source,text] +---- +route1 + +----------------------+ + | timer:tick | + +----------------------+ + | + | + | + v + +----------------------+ + | log:a | + +----------------------+ +---- + +Example output for a branching route with choice: + +[source,text] +---- +route1 + +----------------------+ + | timer:tick | + +----------------------+ + | + v + +----------------------+ + | choice() | + +----------------------+ + | + +---------------+---------------+ + v v + +----------------------+ +----------------------+ + | when(...) | | otherwise() | + +----------------------+ +----------------------+ + | | + v v + +----------------------+ +----------------------+ + | log:a | | log:b | + +----------------------+ +----------------------+ +---- + +Scope boxes (for filter, split, doTry, etc.) are rendered with dashed borders using `:` for vertical +and `- - -` for horizontal lines. + +Use `--theme=ascii` for plain ASCII art: + +[source,bash] +---- +camel cmd route-diagram MyRoute.java --theme=ascii +---- + +== Unicode Rendering + +The `unicode` theme uses Unicode box-drawing characters for a cleaner look. +Node boxes use `┌──┐ │ └──┘`, arrows use `│` and `▼`, and branch junctions use `┴`. +Scope boxes use `╌` (dashed horizontal) and `╎` (dashed vertical) with no corners. + +[source,bash] +---- +camel cmd route-diagram MyRoute.java --theme=unicode +---- + +Example output: + +[source,text] +---- +route1 +┌──────────────────────┐ +│ timer:tick │ +└──────────────────────┘ + │ + │ + ▼ +┌──────────────────────┐ +│ log:a │ +└──────────────────────┘ +---- diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java index 364d579cf7ddd..ba0d83311d387 100644 --- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java +++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java @@ -116,6 +116,16 @@ public BufferedImage dumpRoutesAsImage( return renderImage(routes, theme.name(), fontSize, nodeWidth, nodeLabel.name(), metrics); } + @Override + public String dumpRoutesAsAsciiArt( + String filter, RouteDiagramDumper.NodeLabelMode nodeLabel, int nodeWidth, boolean unicode) { + DevConsole dc = getCamelContext().getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class) + .resolveById("route-structure"); + JsonObject root = (JsonObject) dc.call(DevConsole.MediaType.JSON, Map.of("filter", filter)); + var routes = RouteDiagramHelper.parseRoutes(root); + return renderAscii(routes, nodeWidth, nodeLabel.name(), unicode); + } + @Override public String imageToBase64(BufferedImage image) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -152,4 +162,23 @@ private static BufferedImage renderImage( return renderer.renderDiagram(layoutRoutes, currentY, colors); } + private static String renderAscii( + List routes, int nodeWidth, String nodeLabel, boolean unicode) { + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine( + nodeWidth, RouteDiagramLayoutEngine.DEFAULT_FONT_SIZE, + RouteDiagramLayoutEngine.NodeLabelMode.valueOf(nodeLabel.toUpperCase())); + + List layoutRoutes = new ArrayList<>(); + int currentY = RouteDiagramLayoutEngine.PADDING; + for (RouteDiagramLayoutEngine.RouteInfo route : routes) { + RouteDiagramLayoutEngine.LayoutRoute lr = engine.layoutRoute(route, currentY); + layoutRoutes.add(lr); + currentY = lr.maxY + RouteDiagramLayoutEngine.V_GAP; + } + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer( + nodeWidth * RouteDiagramLayoutEngine.SCALE, unicode); + return renderer.renderDiagram(layoutRoutes, currentY); + } + } 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 74265961117b2..e41c3b8cf15fb 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 @@ -35,7 +35,7 @@ public class DiagramDevConsole extends AbstractDevConsole { public static final String FILTER = "filter"; /** - * Theme to use: dark or light + * Theme to use: dark, light, transparent, ascii, or unicode */ public static final String THEME = "theme"; @@ -84,18 +84,25 @@ protected String doCallText(Map options) { 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( - " \n \"Route\n \n", - base64); - if (refresh) { - html = "\n" + html; + if (isTextTheme(theme)) { + String text = dumper.dumpRoutesAsAsciiArt(filter, + RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), + nodeWidth, isUnicodeTheme(theme)); + sj.add(text); + } else { + BufferedImage image = dumper.dumpRoutesAsImage(filter, + RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), + metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); + String base64 = dumper.imageToBase64(image); + String html = String.format( + " \n \"Route\n \n", + base64); + if (refresh) { + html = "\n" + html; + } + html = "\n" + html + "\n"; + sj.add(html); } - html = "\n" + html + "\n"; - sj.add(html); } catch (Exception e) { // ignore } @@ -117,10 +124,18 @@ protected Map doCallJson(Map options) { JsonObject root = new JsonObject(); 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); - root.put("image", base64); + if (isTextTheme(theme)) { + String text = dumper.dumpRoutesAsAsciiArt(filter, + RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), + nodeWidth, isUnicodeTheme(theme)); + root.put("text", text); + } else { + BufferedImage image = dumper.dumpRoutesAsImage(filter, + RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), + metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); + String base64 = dumper.imageToBase64(image); + root.put("image", base64); + } } catch (Exception e) { // ignore } @@ -128,4 +143,12 @@ protected Map doCallJson(Map options) { return root; } + private static boolean isTextTheme(String theme) { + return "ascii".equalsIgnoreCase(theme) || "unicode".equalsIgnoreCase(theme); + } + + private static boolean isUnicodeTheme(String theme) { + return "unicode".equalsIgnoreCase(theme); + } + } diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java new file mode 100644 index 0000000000000..853c08ec7cd4d --- /dev/null +++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java @@ -0,0 +1,391 @@ +/* + * 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.diagram; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.camel.diagram.RouteDiagramLayoutEngine.Bounds; +import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutNode; +import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute; +import org.apache.camel.diagram.RouteDiagramLayoutEngine.TreeNode; + +import static org.apache.camel.diagram.RouteDiagramLayoutEngine.PADDING; +import static org.apache.camel.diagram.RouteDiagramLayoutEngine.SCOPE_BOX_PAD; + +/** + * Renders route diagrams as plain ASCII art or Unicode box-drawing text. + */ +public class RouteDiagramAsciiRenderer { + + private static final int MAX_WRAP_LINES = 3; + private static final int Y_SCALE = 20; + private static final int MIN_BOX_WIDTH = 16; + private static final int X_DIVISOR = 15; + + // Unicode box-drawing characters + private static final char UNI_H = '─'; // ─ + private static final char UNI_V = '│'; // │ + private static final char UNI_TL = '┌'; // ┌ + private static final char UNI_TR = '┐'; // ┐ + private static final char UNI_BL = '└'; // └ + private static final char UNI_BR = '┘'; // ┘ + private static final char UNI_T_DOWN = '┬'; // ┬ + private static final char UNI_T_UP = '┴'; // ┴ + private static final char UNI_CROSS = '┼'; // ┼ + private static final char UNI_ARROW = '▼'; // ▼ + private static final char UNI_DASH_H = '╌'; // ╌ + private static final char UNI_DASH_V = '╎'; // ╎ + + private final int nodeWidth; + private final int boxWidth; + private final boolean unicode; + + public RouteDiagramAsciiRenderer(int nodeWidth) { + this(nodeWidth, false); + } + + public RouteDiagramAsciiRenderer(int nodeWidth, boolean unicode) { + this.nodeWidth = nodeWidth; + this.boxWidth = Math.max(MIN_BOX_WIDTH, nodeWidth / X_DIVISOR); + this.unicode = unicode; + } + + public int getBoxWidth() { + return boxWidth; + } + + public String renderDiagram(List layoutRoutes, int totalHeight) { + int maxPixelX = layoutRoutes.stream() + .mapToInt(lr -> lr.maxX).max().orElse(nodeWidth) + PADDING; + int gridWidth = toCol(maxPixelX) + boxWidth + 4; + int gridHeight = totalHeight / Y_SCALE + 20; + + char[][] grid = new char[gridHeight][gridWidth]; + for (char[] row : grid) { + Arrays.fill(row, ' '); + } + + for (LayoutRoute lr : layoutRoutes) { + drawRoute(grid, lr); + } + + return gridToString(grid); + } + + private void drawRoute(char[][] grid, LayoutRoute lr) { + int labelRow = toRow(lr.labelY); + String label = lr.routeId; + if (lr.source != null && !lr.source.isEmpty()) { + label += " (" + lr.source + ")"; + } + drawText(grid, labelRow, toCol(PADDING), label); + + for (LayoutNode ln : lr.nodes) { + if (ln.treeNode != null && RouteDiagramLayoutEngine.hasScope(ln.treeNode)) { + drawScopeBox(grid, ln); + } + } + + for (LayoutNode ln : lr.nodes) { + if (ln.parentNode != null) { + if (ln.connectFromMerge) { + drawMergeArrow(grid, ln); + } else { + drawArrow(grid, ln.parentNode, ln); + } + } + } + + for (LayoutNode ln : lr.nodes) { + drawNode(grid, ln); + } + } + + private void drawNode(char[][] grid, LayoutNode node) { + int col = toCol(node.x); + int row = toRow(node.y); + int innerWidth = boxWidth - 4; + List lines = rewrapText(node, innerWidth); + int height = 2 + lines.size(); + + if (row + height >= grid.length) { + return; + } + + char h = unicode ? UNI_H : '-'; + char v = unicode ? UNI_V : '|'; + + setChar(grid, row, col, unicode ? UNI_TL : '+'); + for (int c = col + 1; c < col + boxWidth - 1; c++) { + setChar(grid, row, c, h); + } + setChar(grid, row, col + boxWidth - 1, unicode ? UNI_TR : '+'); + + int bottom = row + height - 1; + setChar(grid, bottom, col, unicode ? UNI_BL : '+'); + for (int c = col + 1; c < col + boxWidth - 1; c++) { + setChar(grid, bottom, c, h); + } + setChar(grid, bottom, col + boxWidth - 1, unicode ? UNI_BR : '+'); + + for (int i = 0; i < lines.size(); i++) { + int r = row + 1 + i; + setChar(grid, r, col, v); + setChar(grid, r, col + boxWidth - 1, v); + for (int c = col + 1; c < col + boxWidth - 1; c++) { + setChar(grid, r, c, ' '); + } + String text = lines.get(i); + if (text.length() > innerWidth) { + text = text.substring(0, Math.max(1, innerWidth - 3)) + "..."; + } + int textCol = col + 2 + Math.max(0, (innerWidth - text.length()) / 2); + drawText(grid, r, textCol, text); + } + } + + private void drawArrow(char[][] grid, LayoutNode from, LayoutNode to) { + int fromCx = centerCol(from); + int fromBottom = toRow(from.y) + boxHeight(from); + int toCx = centerCol(to); + int toTop = getTopRow(to); + + drawArrowPath(grid, fromCx, fromBottom, toCx, toTop); + } + + private void drawMergeArrow(char[][] grid, LayoutNode to) { + int fromCx = toCol(to.mergeCx); + int fromRow = toRow(to.mergeY); + int toCx = centerCol(to); + int toTop = getTopRow(to); + + drawArrowPath(grid, fromCx, fromRow, toCx, toTop); + } + + private void drawArrowPath(char[][] grid, int fromCx, int fromRow, int toCx, int toRow) { + if (fromRow >= toRow) { + return; + } + + char v = unicode ? UNI_V : '|'; + char h = unicode ? UNI_H : '-'; + char arrow = unicode ? UNI_ARROW : 'v'; + + if (fromCx == toCx) { + for (int r = fromRow; r < toRow - 1; r++) { + plotLine(grid, r, fromCx, v); + } + setChar(grid, toRow - 1, toCx, arrow); + } else { + int midRow = fromRow + (toRow - fromRow) / 2; + + for (int r = fromRow; r < midRow; r++) { + plotLine(grid, r, fromCx, v); + } + + int minC = Math.min(fromCx, toCx); + int maxC = Math.max(fromCx, toCx); + for (int c = minC; c <= maxC; c++) { + plotLine(grid, midRow, c, h); + } + + if (unicode) { + setChar(grid, midRow, fromCx, UNI_T_UP); + setChar(grid, midRow, toCx, UNI_T_DOWN); + } else { + setChar(grid, midRow, fromCx, '+'); + setChar(grid, midRow, toCx, '+'); + } + + for (int r = midRow + 1; r < toRow - 1; r++) { + plotLine(grid, r, toCx, v); + } + setChar(grid, toRow - 1, toCx, arrow); + } + } + + private void drawScopeBox(char[][] grid, LayoutNode scopeNode) { + TreeNode tn = scopeNode.treeNode; + Bounds bounds = new Bounds( + scopeNode.x, scopeNode.y, + scopeNode.x + nodeWidth, scopeNode.y + scopeNode.height); + for (TreeNode child : tn.children) { + RouteDiagramLayoutEngine.expandBoundsForBox(child, bounds, nodeWidth); + } + + int col1 = toCol(bounds.minX - SCOPE_BOX_PAD); + int row1 = toRow(bounds.minY - SCOPE_BOX_PAD); + int col2 = toCol(bounds.maxX + SCOPE_BOX_PAD); + int row2 = toRow(bounds.maxY + SCOPE_BOX_PAD); + + if (unicode) { + for (int c = col1; c <= col2; c++) { + setChar(grid, row1, c, UNI_DASH_H); + setChar(grid, row2, c, UNI_DASH_H); + } + for (int r = row1 + 1; r < row2; r++) { + setChar(grid, r, col1, UNI_DASH_V); + setChar(grid, r, col2, UNI_DASH_V); + } + } else { + drawDashedHLine(grid, row1, col1, col2); + drawDashedHLine(grid, row2, col1, col2); + for (int r = row1 + 1; r < row2; r++) { + setChar(grid, r, col1, ':'); + setChar(grid, r, col2, ':'); + } + } + } + + private void drawDashedHLine(char[][] grid, int row, int col1, int col2) { + for (int c = col1; c <= col2; c++) { + if (c == col1 || c == col2) { + setChar(grid, row, c, '+'); + } else if ((c - col1) % 2 == 0) { + setChar(grid, row, c, '-'); + } + } + } + + private int getTopRow(LayoutNode node) { + if (node.treeNode != null && RouteDiagramLayoutEngine.hasScope(node.treeNode)) { + return toRow(node.y - SCOPE_BOX_PAD); + } + return toRow(node.y); + } + + private int centerCol(LayoutNode node) { + return toCol(node.x + nodeWidth / 2); + } + + private int boxHeight(LayoutNode node) { + return 2 + rewrapText(node, boxWidth - 4).size(); + } + + private List rewrapText(LayoutNode node, int maxWidth) { + String label = String.join("", node.wrappedLines); + return wrapText(label, maxWidth); + } + + static List wrapText(String text, int maxWidth) { + if (maxWidth <= 0 || text.length() <= maxWidth) { + return List.of(text); + } + + List lines = new ArrayList<>(); + String remaining = text; + + while (!remaining.isEmpty() && lines.size() < MAX_WRAP_LINES) { + if (remaining.length() <= maxWidth) { + lines.add(remaining); + remaining = ""; + break; + } + + int breakAt = -1; + for (int i = 0; i < maxWidth && i < remaining.length(); i++) { + char c = remaining.charAt(i); + if (c == ' ' || c == ':' || c == '/' || c == '.' || c == ',' || c == '&' || c == '?') { + breakAt = i + 1; + } + } + if (breakAt <= 0) { + breakAt = maxWidth; + } + + lines.add(remaining.substring(0, breakAt).stripTrailing()); + remaining = remaining.substring(breakAt).stripLeading(); + } + + if (!remaining.isEmpty()) { + int lastIdx = lines.size() - 1; + String lastLine = lines.get(lastIdx); + if (lastLine.length() + remaining.length() <= maxWidth) { + lines.set(lastIdx, lastLine + remaining); + } else { + String combined = lastLine + remaining; + lines.set(lastIdx, combined.substring(0, Math.max(1, maxWidth - 3)) + "..."); + } + } + + return lines; + } + + private int toCol(int pixelX) { + if (nodeWidth == 0) { + return 0; + } + return pixelX * boxWidth / nodeWidth; + } + + private int toRow(int pixelY) { + return pixelY / Y_SCALE; + } + + private void setChar(char[][] grid, int row, int col, char ch) { + if (row >= 0 && row < grid.length && col >= 0 && col < grid[0].length) { + grid[row][col] = ch; + } + } + + private char getChar(char[][] grid, int row, int col) { + if (row >= 0 && row < grid.length && col >= 0 && col < grid[0].length) { + return grid[row][col]; + } + return ' '; + } + + private boolean isVertical(char ch) { + return ch == '|' || ch == UNI_V; + } + + private boolean isHorizontal(char ch) { + return ch == '-' || ch == UNI_H; + } + + private void plotLine(char[][] grid, int row, int col, char ch) { + char current = getChar(grid, row, col); + if ((isVertical(current) && isHorizontal(ch)) || (isHorizontal(current) && isVertical(ch))) { + setChar(grid, row, col, unicode ? UNI_CROSS : '+'); + } else { + setChar(grid, row, col, ch); + } + } + + private void drawText(char[][] grid, int row, int col, String text) { + for (int i = 0; i < text.length(); i++) { + setChar(grid, row, col + i, text.charAt(i)); + } + } + + private String gridToString(char[][] grid) { + int lastRow = 0; + for (int r = 0; r < grid.length; r++) { + if (!new String(grid[r]).isBlank()) { + lastRow = r; + } + } + StringBuilder sb = new StringBuilder(); + for (int r = 0; r <= lastRow; r++) { + sb.append(new String(grid[r]).stripTrailing()); + sb.append('\n'); + } + return sb.toString(); + } +} diff --git a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java index 80e8f23595410..b6ab0d9fd7d8a 100644 --- a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java +++ b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java @@ -852,6 +852,212 @@ void testExtractSourceNameBlank() { assertNull(RouteDiagramHelper.extractSourceName("")); } + @Test + void testAsciiDiagramSequential() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "timer:tick", 0)); + route.nodes.add(node("to", "log:a", 1)); + route.nodes.add(node("to", "log:b", 1)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("route1")); + assertTrue(result.contains("timer:tick")); + assertTrue(result.contains("log:a")); + assertTrue(result.contains("log:b")); + assertTrue(result.contains("+")); + assertTrue(result.contains("v")); + } + + @Test + void testAsciiDiagramBranching() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "timer:tick", 0)); + route.nodes.add(node("choice", "choice()", 1)); + route.nodes.add(node("when", "when(...)", 2)); + route.nodes.add(node("to", "log:a", 3)); + route.nodes.add(node("otherwise", "otherwise()", 2)); + route.nodes.add(node("to", "log:b", 3)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("route1")); + assertTrue(result.contains("timer:tick")); + assertTrue(result.contains("choice()")); + assertTrue(result.contains("when(...)")); + assertTrue(result.contains("otherwise()")); + assertTrue(result.contains("log:a")); + assertTrue(result.contains("log:b")); + // branching arrows use horizontal lines + assertTrue(result.contains("-"), "Branching should contain horizontal arrow lines"); + } + + @Test + void testAsciiDiagramEmptyRoute() { + RouteInfo route = new RouteInfo(); + route.routeId = "empty"; + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("empty")); + } + + @Test + void testAsciiDiagramWithSource() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.source = "test.yaml"; + route.nodes.add(node("from", "timer:tick", 0)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("route1 (test.yaml)")); + } + + @Test + void testAsciiDiagramLongLabel() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "kafka:my-topic?brokers=localhost:9092&groupId=myGroup", 0)); + route.nodes.add(node("to", "log:a", 1)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("kafka:")); + assertTrue(result.contains("log:a")); + } + + @Test + void testAsciiDiagramWithScopeBox() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "direct:start", 0)); + route.nodes.add(node("filter", "filter[header(x)]", 1)); + route.nodes.add(node("log", "log[filtered]", 2)); + route.nodes.add(node("to", "to[mock:end]", 1)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth()); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("filter[header(x)]")); + assertTrue(result.contains("log[filtered]")); + assertTrue(result.contains("to[mock:end]")); + assertTrue(result.contains(":"), "Scope box should have dashed vertical borders"); + assertTrue(result.contains("+ -"), "Scope box should have dashed horizontal borders"); + } + + @Test + void testUnicodeDiagramSequential() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "timer:tick", 0)); + route.nodes.add(node("to", "log:a", 1)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth(), true); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("timer:tick")); + assertTrue(result.contains("log:a")); + assertTrue(result.contains("┌"), "Should contain Unicode top-left corner"); + assertTrue(result.contains("─"), "Should contain Unicode horizontal line"); + assertTrue(result.contains("│"), "Should contain Unicode vertical line"); + assertTrue(result.contains("▼"), "Should contain Unicode arrow head"); + } + + @Test + void testUnicodeDiagramBranching() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "timer:tick", 0)); + route.nodes.add(node("choice", "choice()", 1)); + route.nodes.add(node("when", "when(...)", 2)); + route.nodes.add(node("to", "log:a", 3)); + route.nodes.add(node("otherwise", "otherwise()", 2)); + route.nodes.add(node("to", "log:b", 3)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth(), true); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("choice()")); + assertTrue(result.contains("when(...)")); + assertTrue(result.contains("┴"), "Should contain Unicode T-up junction for branch split"); + } + + @Test + void testUnicodeDiagramWithScopeBox() { + RouteInfo route = new RouteInfo(); + route.routeId = "route1"; + route.nodes.add(node("from", "direct:start", 0)); + route.nodes.add(node("filter", "filter[header(x)]", 1)); + route.nodes.add(node("log", "log[filtered]", 2)); + route.nodes.add(node("to", "to[mock:end]", 1)); + + RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(); + LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING); + + RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth(), true); + String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP); + + assertTrue(result.contains("filter[header(x)]")); + assertTrue(result.contains("╌"), "Scope box should have Unicode dashed horizontal"); + assertTrue(result.contains("╎"), "Scope box should have Unicode dashed vertical"); + } + + @Test + void testAsciiWrapTextShort() { + List lines = RouteDiagramAsciiRenderer.wrapText("timer:tick", 20); + assertEquals(1, lines.size()); + assertEquals("timer:tick", lines.get(0)); + } + + @Test + void testAsciiWrapTextWrap() { + List lines = RouteDiagramAsciiRenderer.wrapText("kafka:my-topic?brokers=localhost:9092", 20); + assertTrue(lines.size() > 1, "Long text should wrap"); + String rejoined = String.join("", lines); + assertTrue(rejoined.contains("kafka:")); + assertTrue(rejoined.contains("9092")); + } + + @Test + void testAsciiWrapTextTruncate() { + String veryLong = "a]".repeat(60); + List lines = RouteDiagramAsciiRenderer.wrapText(veryLong, 20); + assertTrue(lines.size() <= 3, "Should not exceed 3 lines"); + assertTrue(lines.get(lines.size() - 1).endsWith("..."), "Truncated text should end with ..."); + } + private static NodeInfo node(String type, String code, int level) { return node(type, code, level, null); } diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java index 7060cc5d0da26..1194e2e73f7d8 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java @@ -89,4 +89,36 @@ BufferedImage dumpRoutesAsImage( */ String imageToBase64(BufferedImage image) throws IOException; + /** + * Dumps the routes as ASCII art text + * + * @param filter to filter routes + */ + default String dumpRoutesAsAsciiArt(String filter) { + return dumpRoutesAsAsciiArt(filter, NodeLabelMode.CODE, 180, false); + } + + /** + * Dumps the routes as ASCII art text + * + * @param filter to filter routes + * @param nodeLabel what information to display in the nodes + * @param nodeWidth the width in pixels of the node boxes + */ + default String dumpRoutesAsAsciiArt(String filter, NodeLabelMode nodeLabel, int nodeWidth) { + return dumpRoutesAsAsciiArt(filter, nodeLabel, nodeWidth, false); + } + + /** + * Dumps the routes as ASCII art or Unicode box-drawing text + * + * @param filter to filter routes + * @param nodeLabel what information to display in the nodes + * @param nodeWidth the width in pixels of the node boxes + * @param unicode whether to use Unicode box-drawing characters + */ + default String dumpRoutesAsAsciiArt(String filter, NodeLabelMode nodeLabel, int nodeWidth, boolean unicode) { + throw new UnsupportedOperationException(); + } + } 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 d118b519b5c9d..354db16e98f48 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 @@ -25,8 +25,8 @@ camel cmd route-diagram [options] | `--ignore-loading-error` | Whether to ignore route loading and compilation errors (use this with care!) | false | boolean | `--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` | 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 +| `--output` | Save diagram to a file (PNG for image themes, text for ascii theme) | | String +| `--theme` | Color theme preset (dark, light, transparent, ascii, unicode) 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. Use ascii/unicode for plain text output. 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 9e7a209d62c0d..399406bab646b 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:). Sink endpoints are expected in the format [[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet name.", "javaType": "java.lang.String", "type": "string" }, { "names": "--output", "description": "Output format generated by this command (supports: file, yaml, json).", "defaultValue": "file", "javaType": "java.lang.String", "type": "string" }, { "names": "--property", "description": "Adds a pipe property in the form of [source|sink|error-handler|step-].= where is the step number starting from 1", "javaType": "java.lang.String", "type": "string" }, { "names": "--sink", "description": "Sink (to) such as a Kamelet or Camel endpoint uri", "javaType": "java.lang.String", "type": "string", "required": true }, { "names": "--source", "description": "Source (from) such as a Kamelet or Camel endpoint uri", "javaType": "java.lang.String", "type": "string", "required": true }, { "names": "--step", "description": "Optional steps such as a Kamelet or Camel endpoint uri", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "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.core.commands.catalog.CatalogComponent", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "dataformat", "fullName": "catalog dataformat", "description": "List data formats from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDataFormat", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "dev-console", "fullName": "catalog dev-console", "description": "List dev-consoles from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDevConsole", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "kamelet", "fullName": "catalog kamelet", "description": "List Kamelets from the Kamelet Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogKamelet", "options": [ { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--kamelets-version", "description": "Apache Camel Kamelets version", "defaultValue": "RuntimeType.KAMELETS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, type, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "--type,--filter-type", "description": "Filter by type: source, sink, or action", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "language", "fullName": "catalog language", "description": "List expression languages from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogLanguage", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "other", "fullName": "catalog other", "description": "List miscellaneous components from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogOther", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "transformer", "fullName": "catalog transformer", "description": "List data type transformers from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogTransformer", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--display-gav", "description": "Display Maven GAV instead of name", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter by name or description", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--since-after", "description": "Filter by version more recent (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--since-before", "description": "Filter by version older (inclusive)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name, support-level, or description", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, - { "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", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelBrowseAction", "options": [ { "names": "--body-max-chars", "description": "Maximum size of the message body to include in the dump", "javaType": "int", "type": "integer" }, { "names": "--dump", "description": "Whether to include message dumps", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--endpoint", "description": "Endpoint to browse messages from (can be uri, pattern, or refer to a route id)", "javaType": "java.lang.String", "type": "string" }, { "names": "--fresh-size", "description": "Whether to calculate fresh queue size information (performance overhead)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Limits the number of messages to dump per endpoint", "defaultValue": "100", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--only-body", "description": "Show only message body in browsed messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in browsed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in browsed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by uri, or size", "defaultValue": "uri", "javaType": "java.lang.String", "type": "string" }, { "names": "--tail", "description": "The number of messages from the end (latest) to dump", "javaType": "int", "type": "integer" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "disable-processor", "fullName": "cmd disable-processor", "description": "Disable Camel processor", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelProcessorDisableAction", "options": [ { "names": "--id", "description": "Processor ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "enable-processor", "fullName": "cmd enable-processor", "description": "Enable Camel processor", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelProcessorEnableAction", "options": [ { "names": "--id", "description": "Processor ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "gc", "fullName": "cmd gc", "description": "Trigger Java Memory Garbage Collector", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelGCAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "load", "fullName": "cmd load", "description": "Loads new source files into an existing Camel", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelLoadAction", "options": [ { "names": "--restart", "description": "To force restart all routes after loading source files", "javaType": "boolean", "type": "boolean" }, { "names": "--source", "description": "Source file(s) to load", "javaType": "java.util.List", "type": "array" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "logger", "fullName": "cmd logger", "description": "List or change logging levels", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.LoggerAction", "options": [ { "names": "--logger", "description": "The logger name", "defaultValue": "root", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-level", "description": "To change logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "receive", "fullName": "cmd receive", "description": "Receive and dump messages from remote endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelReceiveAction", "options": [ { "names": "--action", "description": "Action to start, stop, clear, status, or dump messages", "defaultValue": "status", "javaType": "java.lang.String", "type": "string" }, { "names": "--compact", "description": "Compact output (no empty line separating messages)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--endpoint,--uri", "description": "Endpoint to receive messages from (can be uri or pattern to refer to existing endpoint)", "javaType": "java.lang.String", "type": "string" }, { "names": "--find", "description": "Find and highlight matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--follow", "description": "Keep following and outputting new messages (press enter to exit).", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--grep", "description": "Filter messages to only output matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--only-body", "description": "Show only message body in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--output", "description": "Output format (auto, true, false)", "javaType": "java.lang.String", "type": "string" }, { "names": "--prefix", "description": "Print prefix with running Camel integration name. auto=only prefix when running multiple integrations. true=always prefix. false=prefix off.", "defaultValue": "auto", "javaType": "java.lang.String", "type": "string" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--prop,--property", "description": "Additional properties; override existing (only applicable when NOT using an existing running Camel)", "javaType": "java.lang.String", "type": "string" }, { "names": "--properties", "description": "comma separated list of properties file (only applicable when NOT using an existing running Camel) (ex. \/path\/to\/file.properties,\/path\/to\/other.properties", "javaType": "java.lang.String", "type": "string" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in received messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in received messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--since", "description": "Return messages newer than a relative duration like 5s, 2m, or 1h. The value is in seconds if no unit specified.", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age for showing status of messages", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--tail", "description": "The number of messages from the end to show. Use -1 to read from the beginning. Use 0 to read only new lines. Defaults to showing all messages from beginning.", "defaultValue": "-1", "javaType": "int", "type": "integer" }, { "names": "--timeout", "description": "Timeout in millis waiting for message to be received", "defaultValue": "20000", "javaType": "long", "type": "integer" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "reload", "fullName": "cmd reload", "description": "Trigger reloading Camel", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelReloadAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "reset-stats", "fullName": "cmd reset-stats", "description": "Reset performance statistics", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelResetStatsAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "resume-route", "fullName": "cmd resume-route", "description": "Resume Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteResumeAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-diagram", "fullName": "cmd route-diagram", "description": "Display Camel route diagram in the terminal", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteDiagramAction", "options": [ { "names": "--box-width", "description": "Node box width in logical pixels", "defaultValue": "180", "javaType": "int", "type": "integer" }, { "names": "--filter", "description": "Filter route by filename or route id", "javaType": "java.lang.String", "type": "string" }, { "names": "--font-size", "description": "Font size in logical pixels for node text", "defaultValue": "12", "javaType": "int", "type": "integer" }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--metric", "description": "Whether to include live metrics (only possible for running Camel application)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--node-label", "description": "What text to display in diagram nodes: code, description, or both (default)", "defaultValue": "both", "javaType": "java.lang.String", "type": "string" }, { "names": "--output", "description": "Save diagram to a PNG file instead of displaying in terminal", "javaType": "java.lang.String", "type": "string" }, { "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": "transparent", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--width", "description": "Image width in pixels (0 = auto)", "defaultValue": "0", "javaType": "int", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-structure", "fullName": "cmd route-structure", "description": "Dump Camel route structure", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStructureAction", "options": [ { "names": "--brief", "description": "To show less detailed route structure", "javaType": "boolean", "type": "boolean" }, { "names": "--description", "description": "To show description instead of code", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter route by filename or route id (multiple names can be separated by comma)", "javaType": "java.lang.String", "type": "string" }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--raw", "description": "To output raw without metadata", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort route by name or id", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "send", "fullName": "cmd send", "description": "Send messages to endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelSendAction", "options": [ { "names": "--body", "description": "Message body to send (prefix with file: to refer to loading message body from file)", "javaType": "java.lang.String", "type": "string" }, { "names": "--endpoint,--uri", "description": "Endpoint where to send the message (can be uri, pattern, or refer to a route id)", "javaType": "java.lang.String", "type": "string" }, { "names": "--header", "description": "Message header (key=value)", "javaType": "java.util.List", "type": "array" }, { "names": "--infra", "description": "Send to infrastructure service (e.g., nats, kafka)", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--poll", "description": "Poll instead of sending a message. This can be used to receive latest message from a Kafka topic or JMS queue.", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print response message body (InOut) when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--prop,--property", "description": "Additional properties; override existing (only applicable when NOT using an existing running Camel)", "javaType": "java.lang.String", "type": "string" }, { "names": "--properties", "description": "comma separated list of properties file (only applicable when NOT using an existing running Camel) (ex. \/path\/to\/file.properties,\/path\/to\/other.properties", "javaType": "java.lang.String", "type": "string" }, { "names": "--reply", "description": "Whether to expect a reply message (InOut vs InOut messaging style)", "javaType": "boolean", "type": "boolean" }, { "names": "--reply-file", "description": "Saves reply message to the file with the given name (override if exists)", "javaType": "java.lang.String", "type": "string" }, { "names": "--show-body", "description": "Show message body from response message (InOut)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exception", "description": "Show exception and stacktrace for failed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties from response message (InOut)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables from response message (InOut)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers from response message (InOut)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--timeout", "description": "Timeout in millis waiting for message to be sent (and reply message if InOut messaging)", "defaultValue": "20000", "javaType": "long", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "start-group", "fullName": "cmd start-group", "description": "Start Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteGroupStartAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "start-route", "fullName": "cmd start-route", "description": "Start Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStartAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stop-group", "fullName": "cmd stop-group", "description": "Stop Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteGroupStopAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stop-route", "fullName": "cmd stop-route", "description": "Stop Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStopAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stub", "fullName": "cmd stub", "description": "Browse stub endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelStubAction", "options": [ { "names": "--browse", "description": "Whether to browse messages queued in the stub endpoints", "javaType": "boolean", "type": "boolean" }, { "names": "--compact", "description": "Compact output (no empty line separating browsed messages)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter endpoints by queue name", "javaType": "java.lang.String", "type": "string" }, { "names": "--find", "description": "Find and highlight matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--grep", "description": "Filter browsing messages to only output trace matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--limit", "description": "Filter browsing queues by limiting to the given latest number of messages", "defaultValue": "10", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in traced messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in traced messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by name, or total", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "--top", "description": "Whether to browse top (latest) messages queued in the stub endpoints", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "suspend-route", "fullName": "cmd suspend-route", "description": "Suspend Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteSuspendAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "thread-dump", "fullName": "cmd thread-dump", "description": "List threads in a running Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelThreadDump", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--filter", "description": "Filter thread names\/ids (use all to include all threads)", "defaultValue": "Camel", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by id, name or state", "defaultValue": "id", "javaType": "java.lang.String", "type": "string" }, { "names": "--state", "description": "To only show threads for a given state", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, + { "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", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelBrowseAction", "options": [ { "names": "--body-max-chars", "description": "Maximum size of the message body to include in the dump", "javaType": "int", "type": "integer" }, { "names": "--dump", "description": "Whether to include message dumps", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--endpoint", "description": "Endpoint to browse messages from (can be uri, pattern, or refer to a route id)", "javaType": "java.lang.String", "type": "string" }, { "names": "--fresh-size", "description": "Whether to calculate fresh queue size information (performance overhead)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Limits the number of messages to dump per endpoint", "defaultValue": "100", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--only-body", "description": "Show only message body in browsed messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in browsed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in browsed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by uri, or size", "defaultValue": "uri", "javaType": "java.lang.String", "type": "string" }, { "names": "--tail", "description": "The number of messages from the end (latest) to dump", "javaType": "int", "type": "integer" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "disable-processor", "fullName": "cmd disable-processor", "description": "Disable Camel processor", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelProcessorDisableAction", "options": [ { "names": "--id", "description": "Processor ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "enable-processor", "fullName": "cmd enable-processor", "description": "Enable Camel processor", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelProcessorEnableAction", "options": [ { "names": "--id", "description": "Processor ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "gc", "fullName": "cmd gc", "description": "Trigger Java Memory Garbage Collector", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelGCAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "load", "fullName": "cmd load", "description": "Loads new source files into an existing Camel", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelLoadAction", "options": [ { "names": "--restart", "description": "To force restart all routes after loading source files", "javaType": "boolean", "type": "boolean" }, { "names": "--source", "description": "Source file(s) to load", "javaType": "java.util.List", "type": "array" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "logger", "fullName": "cmd logger", "description": "List or change logging levels", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.LoggerAction", "options": [ { "names": "--logger", "description": "The logger name", "defaultValue": "root", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-level", "description": "To change logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "receive", "fullName": "cmd receive", "description": "Receive and dump messages from remote endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelReceiveAction", "options": [ { "names": "--action", "description": "Action to start, stop, clear, status, or dump messages", "defaultValue": "status", "javaType": "java.lang.String", "type": "string" }, { "names": "--compact", "description": "Compact output (no empty line separating messages)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--endpoint,--uri", "description": "Endpoint to receive messages from (can be uri or pattern to refer to existing endpoint)", "javaType": "java.lang.String", "type": "string" }, { "names": "--find", "description": "Find and highlight matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--follow", "description": "Keep following and outputting new messages (press enter to exit).", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--grep", "description": "Filter messages to only output matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--only-body", "description": "Show only message body in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--output", "description": "Output format (auto, true, false)", "javaType": "java.lang.String", "type": "string" }, { "names": "--prefix", "description": "Print prefix with running Camel integration name. auto=only prefix when running multiple integrations. true=always prefix. false=prefix off.", "defaultValue": "auto", "javaType": "java.lang.String", "type": "string" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--prop,--property", "description": "Additional properties; override existing (only applicable when NOT using an existing running Camel)", "javaType": "java.lang.String", "type": "string" }, { "names": "--properties", "description": "comma separated list of properties file (only applicable when NOT using an existing running Camel) (ex. \/path\/to\/file.properties,\/path\/to\/other.properties", "javaType": "java.lang.String", "type": "string" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in received messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables in received messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in received messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--since", "description": "Return messages newer than a relative duration like 5s, 2m, or 1h. The value is in seconds if no unit specified.", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age for showing status of messages", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--tail", "description": "The number of messages from the end to show. Use -1 to read from the beginning. Use 0 to read only new lines. Defaults to showing all messages from beginning.", "defaultValue": "-1", "javaType": "int", "type": "integer" }, { "names": "--timeout", "description": "Timeout in millis waiting for message to be received", "defaultValue": "20000", "javaType": "long", "type": "integer" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "reload", "fullName": "cmd reload", "description": "Trigger reloading Camel", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelReloadAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "reset-stats", "fullName": "cmd reset-stats", "description": "Reset performance statistics", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelResetStatsAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "resume-route", "fullName": "cmd resume-route", "description": "Resume Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteResumeAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-diagram", "fullName": "cmd route-diagram", "description": "Display Camel route diagram in the terminal", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteDiagramAction", "options": [ { "names": "--box-width", "description": "Node box width in logical pixels", "defaultValue": "180", "javaType": "int", "type": "integer" }, { "names": "--filter", "description": "Filter route by filename or route id", "javaType": "java.lang.String", "type": "string" }, { "names": "--font-size", "description": "Font size in logical pixels for node text", "defaultValue": "12", "javaType": "int", "type": "integer" }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--metric", "description": "Whether to include live metrics (only possible for running Camel application)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--node-label", "description": "What text to display in diagram nodes: code, description, or both (default)", "defaultValue": "both", "javaType": "java.lang.String", "type": "string" }, { "names": "--output", "description": "Save diagram to a file (PNG for image themes, text for ascii theme)", "javaType": "java.lang.String", "type": "string" }, { "names": "--theme", "description": "Color theme preset (dark, light, transparent, ascii, unicode) 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. Use ascii\/unicode for plain text output. Can also be set via DIAGRAM_COLORS env var.", "defaultValue": "transparent", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--width", "description": "Image width in pixels (0 = auto)", "defaultValue": "0", "javaType": "int", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-structure", "fullName": "cmd route-structure", "description": "Dump Camel route structure", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStructureAction", "options": [ { "names": "--brief", "description": "To show less detailed route structure", "javaType": "boolean", "type": "boolean" }, { "names": "--description", "description": "To show description instead of code", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter route by filename or route id (multiple names can be separated by comma)", "javaType": "java.lang.String", "type": "string" }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--raw", "description": "To output raw without metadata", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort route by name or id", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "send", "fullName": "cmd send", "description": "Send messages to endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelSendAction", "options": [ { "names": "--body", "description": "Message body to send (prefix with file: to refer to loading message body from file)", "javaType": "java.lang.String", "type": "string" }, { "names": "--endpoint,--uri", "description": "Endpoint where to send the message (can be uri, pattern, or refer to a route id)", "javaType": "java.lang.String", "type": "string" }, { "names": "--header", "description": "Message header (key=value)", "javaType": "java.util.List", "type": "array" }, { "names": "--infra", "description": "Send to infrastructure service (e.g., nats, kafka)", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--poll", "description": "Poll instead of sending a message. This can be used to receive latest message from a Kafka topic or JMS queue.", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print response message body (InOut) when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--prop,--property", "description": "Additional properties; override existing (only applicable when NOT using an existing running Camel)", "javaType": "java.lang.String", "type": "string" }, { "names": "--properties", "description": "comma separated list of properties file (only applicable when NOT using an existing running Camel) (ex. \/path\/to\/file.properties,\/path\/to\/other.properties", "javaType": "java.lang.String", "type": "string" }, { "names": "--reply", "description": "Whether to expect a reply message (InOut vs InOut messaging style)", "javaType": "boolean", "type": "boolean" }, { "names": "--reply-file", "description": "Saves reply message to the file with the given name (override if exists)", "javaType": "java.lang.String", "type": "string" }, { "names": "--show-body", "description": "Show message body from response message (InOut)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exception", "description": "Show exception and stacktrace for failed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties from response message (InOut)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables from response message (InOut)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers from response message (InOut)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--timeout", "description": "Timeout in millis waiting for message to be sent (and reply message if InOut messaging)", "defaultValue": "20000", "javaType": "long", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "start-group", "fullName": "cmd start-group", "description": "Start Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteGroupStartAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "start-route", "fullName": "cmd start-route", "description": "Start Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStartAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stop-group", "fullName": "cmd stop-group", "description": "Stop Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteGroupStopAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stop-route", "fullName": "cmd stop-route", "description": "Stop Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStopAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stub", "fullName": "cmd stub", "description": "Browse stub endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelStubAction", "options": [ { "names": "--browse", "description": "Whether to browse messages queued in the stub endpoints", "javaType": "boolean", "type": "boolean" }, { "names": "--compact", "description": "Compact output (no empty line separating browsed messages)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter endpoints by queue name", "javaType": "java.lang.String", "type": "string" }, { "names": "--find", "description": "Find and highlight matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--grep", "description": "Filter browsing messages to only output trace matching text (ignore case).", "javaType": "java.lang.String", "type": "string" }, { "names": "--limit", "description": "Filter browsing queues by limiting to the given latest number of messages", "defaultValue": "10", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in traced messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in traced messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by name, or total", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "--top", "description": "Whether to browse top (latest) messages queued in the stub endpoints", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "suspend-route", "fullName": "cmd suspend-route", "description": "Suspend Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelRouteSuspendAction", "options": [ { "names": "--id", "description": "Route ids (multiple ids can be separated by comma)", "defaultValue": "*", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "thread-dump", "fullName": "cmd thread-dump", "description": "List threads in a running Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelThreadDump", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--filter", "description": "Filter thread names\/ids (use all to include all threads)", "defaultValue": "Camel", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by id, name or state", "defaultValue": "id", "javaType": "java.lang.String", "type": "string" }, { "names": "--state", "description": "To only show threads for a given state", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, { "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.ConfigGet", "options": [ { "names": "--global", "description": "Use global or local configuration", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "list", "fullName": "config list", "description": "Displays user configuration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigList", "options": [ { "names": "--global", "description": "Use global or local configuration", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "set", "fullName": "config set", "description": "Set user configuration value", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigSet", "options": [ { "names": "--global", "description": "Use global or local configuration", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "unset", "fullName": "config unset", "description": "Remove user configuration value", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigUnset", "options": [ { "names": "--global", "description": "Use global or local configurations", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, { "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 wait for run in background to startup successfully, before returning", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--backlog-trace", "description": "Enables backlog tracing of the routed messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--breakpoint", "description": "To set breakpoint at the given node id (Multiple ids can be separated by comma). If no breakpoint is set, then the first route is automatic selected.", "javaType": "java.lang.String", "type": "string" }, { "names": "--camel-spring-boot-version", "description": "To run using a different Camel Spring Boot version than the default version.", "javaType": "java.lang.String", "type": "string" }, { "names": "--camel-version", "description": "To run using a different Camel version than the default version.", "javaType": "java.lang.String", "type": "string" }, { "names": "--code", "description": "Run the given text or file as Java DSL routes", "javaType": "java.lang.String", "type": "string" }, { "names": "--console", "description": "Developer console at \/q\/dev on local HTTP server (port 8080 by default)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--dep,--dependency", "description": "Add additional dependencies", "javaType": "java.util.List", "type": "array" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--empty", "description": "Run an empty Camel without loading source files", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--exclude", "description": "Exclude files by name or pattern", "javaType": "java.util.List", "type": "array" }, { "names": "--fresh", "description": "Make sure we use fresh (i.e. non-cached) resources", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--gav", "description": "The Maven group:artifact:version (used during exporting)", "javaType": "java.lang.String", "type": "string" }, { "names": "--health", "description": "Deprecated: use --observe instead. Health check at \/q\/health on local HTTP server (port 8080 by default)", "defaultValue": "false", "javaType": "boolean", "type": "boolean", "deprecated": true }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--java-version,--java", "description": "Java version (21, 25)", "defaultValue": "21", "javaType": "java.lang.String", "type": "string" }, { "names": "--jfr", "description": "Enables Java Flight Recorder saving recording to disk on exit", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--jfr-profile", "description": "Java Flight Recorder profile to use (such as default or profile)", "javaType": "java.lang.String", "type": "string" }, { "names": "--jvm-debug", "description": "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", "javaType": "int", "type": "integer", "paramLabel": "" }, { "names": "--kamelets-version", "description": "Apache Camel Kamelets version", "javaType": "java.lang.String", "type": "string" }, { "names": "--lazy-bean", "description": "Whether to use lazy bean initialization (can help with complex classloading issues)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--local-kamelet-dir", "description": "Local directory (or github link) for loading Kamelets (takes precedence). Multiple directories can be specified separated by comma.", "javaType": "java.lang.String", "type": "string" }, { "names": "--log-lines", "description": "Number of log lines to display on top of screen", "defaultValue": "10", "javaType": "int", "type": "integer" }, { "names": "--logging", "description": "Can be used to turn off logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--logging-category", "description": "Used for individual logging levels (ex: org.apache.kafka=DEBUG)", "javaType": "java.util.List", "type": "array" }, { "names": "--logging-color", "description": "Use colored logging. Default is auto-detected based on NO_COLOR, CI, FORCE_COLOR environment variables and terminal capabilities", "javaType": "boolean", "type": "boolean" }, { "names": "--logging-config-path", "description": "Path to file with custom logging configuration", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging-json", "description": "Use JSON logging (ECS Layout)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--logging-level", "description": "Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "defaultValue": "info", "javaType": "java.lang.String", "type": "string" }, { "names": "--management-port", "description": "To use a dedicated port for HTTP management (use 0 to dynamic assign a free random port number)", "javaType": "int", "type": "integer" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--maven-apache-snapshot-enabled", "description": "Whether downloading JARs from ASF Maven Snapshot repository is enabled", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--maven-central-enabled", "description": "Whether downloading JARs from Maven Central repository is enabled", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--maven-settings", "description": "Optional location of Maven settings.xml file to configure servers, repositories, mirrors and proxies. If set to false, not even the default ~\/.m2\/settings.xml will be used.", "javaType": "java.lang.String", "type": "string" }, { "names": "--maven-settings-security", "description": "Optional location of Maven settings-security.xml file to decrypt settings.xml", "javaType": "java.lang.String", "type": "string" }, { "names": "--max-idle-seconds", "description": "For how long time in seconds Camel can be idle before stopping", "defaultValue": "0", "javaType": "int", "type": "integer" }, { "names": "--max-messages", "description": "Max number of messages to process before stopping", "defaultValue": "0", "javaType": "int", "type": "integer" }, { "names": "--max-seconds", "description": "Max seconds to run before stopping", "defaultValue": "0", "javaType": "int", "type": "integer" }, { "names": "--metrics", "description": "Deprecated: use --observe instead. Metrics (Micrometer and Prometheus) at \/q\/metrics on local HTTP server (port 8080 by default)", "defaultValue": "false", "javaType": "boolean", "type": "boolean", "deprecated": true }, { "names": "--modeline", "description": "Whether to support JBang style \/\/DEPS to specify additional dependencies", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--name", "description": "The name of the Camel application", "defaultValue": "CamelJBang", "javaType": "java.lang.String", "type": "string" }, { "names": "--observe", "description": "Enable observability services", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--open-api", "description": "Adds an OpenAPI spec from the given file (json or yaml file)", "javaType": "java.lang.String", "type": "string" }, { "names": "--output", "description": "File to store the current message body (will override). This allows for manual inspecting the message later.", "javaType": "java.lang.String", "type": "string" }, { "names": "--package-scan-jars", "description": "Whether to automatic package scan JARs for custom Spring or Quarkus beans making them available for Camel JBang", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--port", "description": "Embeds a local HTTP server on this port (port 8080 by default; use 0 to dynamic assign a free random port number)", "javaType": "int", "type": "integer" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--profile", "description": "Profile to run (dev, test, prod).", "defaultValue": "dev", "javaType": "java.lang.String", "type": "string" }, { "names": "--prompt", "description": "Allow user to type in required parameters in prompt if not present in application", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--prop,--property", "description": "Additional properties (override existing)", "javaType": "java.lang.String", "type": "string" }, { "names": "--properties", "description": "comma separated list of properties file (ex. \/path\/to\/file.properties,\/path\/to\/other.properties", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-artifact-id", "description": "Quarkus Platform Maven artifactId", "defaultValue": "quarkus-bom", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-group-id", "description": "Quarkus Platform Maven groupId", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "Quarkus Platform version", "defaultValue": "RuntimeType.QUARKUS_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--reload,--dev", "description": "Enables dev mode (live reload when source files are updated and saved)", "javaType": "boolean", "type": "boolean" }, { "names": "--remote-attach", "description": "Attaches debugger remotely to an existing running Camel integration. (Add camel-cli-debug JAR to the existing Camel application and run before attaching this debugger)", "javaType": "boolean", "type": "boolean" }, { "names": "--repo,--repos", "description": "Additional maven repositories (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "defaultValue": "camel-main", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--show-body", "description": "Show message body in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exception", "description": "Show exception and stacktrace for failed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties in debug messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--skip-plugins", "description": "Skip resolving plugin dependencies", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--source-dir", "description": "Source directory for dynamically loading Camel file(s) to run. When using this, then files cannot be specified at the same time.", "javaType": "java.lang.String", "type": "string" }, { "names": "--spring-boot-version", "description": "Spring Boot version", "defaultValue": "RuntimeType.SPRING_BOOT_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--stop-on-exit", "description": "Whether to stop the running Camel on exit", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--stub", "description": "Stubs all the matching endpoint uri with the given component name or pattern. Multiple names can be separated by comma. (all = stub all endpoints).", "javaType": "java.lang.String", "type": "string" }, { "names": "--timestamp", "description": "Print timestamp.", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--trace", "description": "Enables trace logging of the routed messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--verbose", "description": "Verbose output of startup activity (dependency resolution and downloading", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "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/action/CamelRouteDiagramAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java index 165b53b1cd7ee..cde562c5235a7 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 @@ -26,6 +26,7 @@ import javax.imageio.ImageIO; +import org.apache.camel.diagram.RouteDiagramAsciiRenderer; import org.apache.camel.diagram.RouteDiagramHelper; import org.apache.camel.diagram.RouteDiagramLayoutEngine; import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute; @@ -68,14 +69,15 @@ public class CamelRouteDiagramAction extends ActionWatchCommand { int width; @CommandLine.Option(names = { "--output" }, - description = "Save diagram to a PNG file instead of displaying in terminal") + description = "Save diagram to a file (PNG for image themes, text for ascii theme)") String output; @CommandLine.Option(names = { "--theme" }, - description = "Color theme preset (dark, light, transparent) or custom colors " + description = "Color theme preset (dark, light, transparent, ascii, unicode) 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.", + + "Use bg= for transparent. Use ascii/unicode for plain text output. " + + "Can also be set via DIAGRAM_COLORS env var.", defaultValue = "transparent") String theme; @@ -115,18 +117,26 @@ public CamelRouteDiagramAction(CamelJBangMain main) { public Integer doCall() throws Exception { System.setProperty("java.awt.headless", "true"); - // if output in terminal then ensure terminal supports this - if (output == null) { + boolean textMode = isTextTheme(); + if (!textMode) { String colorSpec = System.getenv("DIAGRAM_COLORS"); colors = DiagramColors.parse(colorSpec != null ? colorSpec : theme); + } + + // if output in terminal then set up terminal + if (output == null) { 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(); + if (!textMode) { + 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."); + printer().println("Or use --theme=ascii or --theme=unicode for plain text output."); + return 1; + } + } } try { @@ -190,9 +200,6 @@ protected Integer doWatchCall() throws Exception { NodeLabelMode labelMode = parseNodeLabelMode(nodeLabel); RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(boxWidth, fontSize, labelMode); - RouteDiagramRenderer renderer = new RouteDiagramRenderer( - engine.getNodeWidth(), fontSize * RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(), - pid > 0 && metric); List layoutRoutes = new ArrayList<>(); int currentY = RouteDiagramLayoutEngine.PADDING; @@ -202,27 +209,54 @@ protected Integer doWatchCall() throws Exception { currentY = lr.maxY + RouteDiagramLayoutEngine.V_GAP; } - BufferedImage image; - try { - image = renderer.renderDiagram(layoutRoutes, currentY, colors); - } catch (IllegalStateException e) { - printer().println(e.getMessage()); - return 1; - } - - if (output != null) { - File file = new File(output); - File parentDir = file.getParentFile(); - if (parentDir != null) { - parentDir.mkdirs(); + if (isTextTheme()) { + RouteDiagramAsciiRenderer asciiRenderer + = new RouteDiagramAsciiRenderer(engine.getNodeWidth(), isUnicodeTheme()); + String ascii = asciiRenderer.renderDiagram(layoutRoutes, currentY); + + if (output != null) { + String fileName = output.endsWith(".png") + ? output.substring(0, output.length() - 4) + ".txt" : output; + File file = new File(fileName); + File parentDir = file.getParentFile(); + if (parentDir != null) { + parentDir.mkdirs(); + } + Files.writeString(file.toPath(), ascii); + printer().println("Diagram saved to: " + file.getAbsolutePath()); + } else { + if (watch) { + clearScreen(); + } + printer().println(ascii); } - ImageIO.write(image, "PNG", file); - printer().println("Diagram saved to: " + file.getAbsolutePath()); } else { - if (watch) { - clearScreen(); + RouteDiagramRenderer renderer = new RouteDiagramRenderer( + engine.getNodeWidth(), fontSize * RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(), + pid > 0 && metric); + + BufferedImage image; + try { + image = renderer.renderDiagram(layoutRoutes, currentY, colors); + } catch (IllegalStateException e) { + printer().println(e.getMessage()); + return 1; + } + + if (output != null) { + File file = new File(output); + File parentDir = file.getParentFile(); + if (parentDir != null) { + parentDir.mkdirs(); + } + ImageIO.write(image, "PNG", file); + printer().println("Diagram saved to: " + file.getAbsolutePath()); + } else { + if (watch) { + clearScreen(); + } + doDisplayDiagram(image); } - doDisplayDiagram(image); } return 0; @@ -319,6 +353,14 @@ List parseRoutes(JsonObject jo) { return RouteDiagramHelper.parseRoutes(jo); } + private boolean isTextTheme() { + return "ascii".equalsIgnoreCase(theme) || "unicode".equalsIgnoreCase(theme); + } + + private boolean isUnicodeTheme() { + return "unicode".equalsIgnoreCase(theme); + } + static NodeLabelMode parseNodeLabelMode(String value) { if (value == null || value.isBlank() || "code".equalsIgnoreCase(value)) { return NodeLabelMode.CODE;