This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 84d94f86200 CAMEL-19747: camel-jbang - Add command to browse stub queues (#11124) 84d94f86200 is described below commit 84d94f8620006d6d06964c9bfa483315be124af0 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Aug 16 20:47:31 2023 +0200 CAMEL-19747: camel-jbang - Add command to browse stub queues (#11124) --- .../apache/camel/component/stub/StubConsole.java | 119 +++++-- .../camel/impl/console/EndpointDevConsole.java | 12 +- .../camel/impl/console/SourceDevConsole.java | 4 +- .../camel/cli/connector/LocalCliConnector.java | 16 +- .../dsl/jbang/core/commands/CamelJBangMain.java | 2 + .../jbang/core/commands/action/CamelSourceTop.java | 4 +- .../core/commands/action/CamelStubAction.java | 378 +++++++++++++++++++++ .../core/commands/action/CamelThreadDump.java | 4 +- .../core/commands/action/MessageTableHelper.java | 3 + .../commands/action/RouteControllerAction.java | 4 +- .../jbang/core/commands/process/ListEndpoint.java | 3 + 11 files changed, 509 insertions(+), 40 deletions(-) diff --git a/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubConsole.java b/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubConsole.java index 0e5430d7b76..4974e752ebe 100644 --- a/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubConsole.java +++ b/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubConsole.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.camel.Exchange; import org.apache.camel.spi.annotations.DevConsole; import org.apache.camel.support.MessageHelper; +import org.apache.camel.support.PatternHelper; import org.apache.camel.support.console.AbstractDevConsole; import org.apache.camel.util.json.JsonArray; import org.apache.camel.util.json.JsonObject; @@ -33,24 +34,45 @@ import org.apache.camel.util.json.JsonObject; @DevConsole("stub") public class StubConsole extends AbstractDevConsole { + /** + * Filters the routes matching by queue name + */ + public static final String FILTER = "filter"; + + /** + * Limits the number of messages dumped + */ + public static final String LIMIT = "limit"; + /** * To use either xml or json output format */ public static final String FORMAT = "format"; + /** + * Whether to browse messages + */ + public static final String BROWSE = "browse"; + public StubConsole() { super("camel", "stub", "Stub", "Browse messages on stub"); } @Override protected String doCallText(Map<String, Object> options) { - StringBuilder sb = new StringBuilder(); + String filter = (String) options.get(FILTER); + String limit = (String) options.get(LIMIT); + String browse = (String) options.get(BROWSE); + final int max = limit == null ? Integer.MAX_VALUE : Integer.parseInt(limit); + final boolean dump = browse == null ? Boolean.FALSE : Boolean.parseBoolean(browse); - StubComponent sc = getCamelContext().getComponent("stub", StubComponent.class); + StringBuilder sb = new StringBuilder(); List<StubEndpoint> list = getCamelContext().getEndpoints() - .stream().filter(e -> e instanceof StubEndpoint) + .stream() + .filter(e -> e instanceof StubEndpoint) .map(StubEndpoint.class::cast) + .filter(e -> accept(e.getName(), filter)) .toList(); Set<String> names = new HashSet<>(); @@ -65,23 +87,30 @@ public class StubConsole extends AbstractDevConsole { sb.append(String.format("Queue: %s (max: %d, size: %d)%n", name, se.getSize(), se.getCurrentQueueSize())); - // browse messages - Queue<Exchange> q = se.getQueue(); - for (Exchange exchange : q) { - // dump to xml or json - try { - String format = (String) options.get(FORMAT); - String dump = null; - if (format == null || "xml".equals(format)) { - dump = MessageHelper.dumpAsXml(exchange.getMessage(), true, 4); - } else if ("json".equals(format)) { - dump = MessageHelper.dumpAsJSon(exchange.getMessage(), true, 4); - } - if (dump != null) { - sb.append("\n").append(dump).append("\n"); + if (dump) { + Queue<Exchange> q = se.getQueue(); + List<Exchange> copy = new ArrayList<>(q); + if (max > 0 && q.size() > max) { + int pos = q.size() - 1 - max; + int end = q.size() - 1; + copy = copy.subList(pos, end); + } + for (Exchange exchange : copy) { + // dump to xml or json + try { + String format = (String) options.get(FORMAT); + String msg = null; + if (format == null || "xml".equals(format)) { + msg = MessageHelper.dumpAsXml(exchange.getMessage(), true, 4); + } else if ("json".equals(format)) { + msg = MessageHelper.dumpAsJSon(exchange.getMessage(), true, 4); + } + if (msg != null) { + sb.append("\n").append(msg).append("\n"); + } + } catch (Exception e) { + // ignore } - } catch (Exception e) { - // ignore } } } @@ -91,12 +120,19 @@ public class StubConsole extends AbstractDevConsole { @Override protected JsonObject doCallJson(Map<String, Object> options) { + String filter = (String) options.get(FILTER); + String limit = (String) options.get(LIMIT); + String browse = (String) options.get(BROWSE); + final int max = limit == null ? Integer.MAX_VALUE : Integer.parseInt(limit); + final boolean dump = browse == null ? Boolean.FALSE : Boolean.parseBoolean(browse); + JsonObject root = new JsonObject(); JsonArray queues = new JsonArray(); List<StubEndpoint> list = getCamelContext().getEndpoints() .stream().filter(e -> e instanceof StubEndpoint) .map(StubEndpoint.class::cast) + .filter(e -> accept(e.getName(), filter)) .toList(); Set<String> names = new HashSet<>(); @@ -111,21 +147,34 @@ public class StubConsole extends AbstractDevConsole { JsonObject jo = new JsonObject(); jo.put("name", name); + jo.put("endpointUri", se.getEndpointUri()); jo.put("max", se.getSize()); jo.put("size", se.getCurrentQueueSize()); - List<JsonObject> arr = new ArrayList<>(); - Queue<Exchange> q = se.getQueue(); - for (Exchange exchange : q) { - try { - JsonObject dump - = MessageHelper.dumpAsJSonObject(exchange.getMessage(), false, true, true, false, true, 128 * 1024); - arr.add(dump); - } catch (Exception e) { - // ignore + + // browse messages + if (dump) { + List<JsonObject> arr = new ArrayList<>(); + + Queue<Exchange> q = se.getQueue(); + List<Exchange> copy = new ArrayList<>(q); + if (max > 0 && q.size() > max) { + int pos = q.size() - 1 - max; + int end = q.size() - 1; + copy = copy.subList(pos, end); + } + for (Exchange exchange : copy) { + try { + JsonObject msg + = MessageHelper.dumpAsJSonObject(exchange.getMessage(), false, true, true, false, true, + 128 * 1024); + arr.add(msg); + } catch (Exception e) { + // ignore + } + } + if (!arr.isEmpty()) { + jo.put("messages", arr); } - } - if (!arr.isEmpty()) { - jo.put("messages", arr); } queues.add(jo); } @@ -134,4 +183,12 @@ public class StubConsole extends AbstractDevConsole { return root; } + private static boolean accept(String name, String filter) { + if (filter == null || filter.isBlank()) { + return true; + } + + return PatternHelper.matchPattern(name, filter); + } + } diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/EndpointDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/EndpointDevConsole.java index 13caa235924..d5180306d91 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/EndpointDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/EndpointDevConsole.java @@ -53,12 +53,18 @@ public class EndpointDevConsole extends AbstractDevConsole { Collection<Endpoint> col = reg.getReadOnlyValues(); if (!col.isEmpty()) { for (Endpoint e : col) { + boolean stub = e.getComponent().getClass().getSimpleName().equals("StubComponent"); + String uri = e.toString(); + if (!uri.startsWith("stub:") && stub) { + // shadow-stub + uri = uri + " (stub)"; + } var stat = findStats(stats, e.getEndpointUri()); if (stat.isPresent()) { var st = stat.get(); - sb.append(String.format("\n %s (direction: %s, usage: %s)", e, st.getDirection(), st.getHits())); + sb.append(String.format("\n %s (direction: %s, usage: %s)", uri, st.getDirection(), st.getHits())); } else { - sb.append(String.format("\n %s", e)); + sb.append(String.format("\n %s", uri)); } } } @@ -88,7 +94,9 @@ public class EndpointDevConsole extends AbstractDevConsole { Collection<Endpoint> col = reg.getReadOnlyValues(); for (Endpoint e : col) { JsonObject jo = new JsonObject(); + boolean stub = e.getComponent().getClass().getSimpleName().equals("StubComponent"); jo.put("uri", e.getEndpointUri()); + jo.put("stub", stub); var stat = findStats(stats, e.getEndpointUri()); if (stat.isPresent()) { var st = stat.get(); diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/SourceDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/SourceDevConsole.java index 079a6ccf4a6..ea96a307af6 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/SourceDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/SourceDevConsole.java @@ -64,7 +64,7 @@ public class SourceDevConsole extends AbstractDevConsole { try { Resource resource = PluginHelper.getResourceLoader(getCamelContext()).resolveResource(loc); if (resource != null) { - if (sb.length() > 0) { + if (!sb.isEmpty()) { sb.append("\n"); } @@ -87,7 +87,7 @@ public class SourceDevConsole extends AbstractDevConsole { if (mrb.getSourceLocation() != null) { sb.append(String.format("\n Source: %s", mrb.getSourceLocation())); } - if (code.length() > 0) { + if (!code.isEmpty()) { sb.append(code); } } diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java index c748fd242dd..347dbd9256d 100644 --- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java +++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java @@ -36,7 +36,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; @@ -225,7 +224,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C } return false; }) - .collect(Collectors.toList()); + .toList(); for (String id : ids) { try { String command = root.getString("command"); @@ -325,6 +324,19 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C LOG.trace("Updating output file: {}", outputFile); IOHelper.writeText(json.toJson(), outputFile); } + } else if ("stub".equals(action)) { + String filter = root.getString("filter"); + String limit = root.getString("limit"); + String browse = root.getString("browse"); + + DevConsole dc = camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class) + .resolveById("stub"); + if (dc != null) { + JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, + Map.of("filter", filter, "limit", limit, "browse", browse)); + LOG.trace("Updating output file: {}", outputFile); + IOHelper.writeText(json.toJson(), outputFile); + } } else if ("send".equals(action)) { StopWatch watch = new StopWatch(); long timestamp = System.currentTimeMillis(); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java index 220f3907b9d..b602c8a7426 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java @@ -31,6 +31,7 @@ import org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStopAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelSendAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelSourceAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelSourceTop; +import org.apache.camel.dsl.jbang.core.commands.action.CamelStubAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelThreadDump; import org.apache.camel.dsl.jbang.core.commands.action.CamelTraceAction; import org.apache.camel.dsl.jbang.core.commands.action.LoggerAction; @@ -118,6 +119,7 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("reset-stats", new CommandLine(new CamelResetStatsAction(main))) .addSubcommand("reload", new CommandLine(new CamelReloadAction(main))) .addSubcommand("send", new CommandLine(new CamelSendAction(main))) + .addSubcommand("stub", new CommandLine(new CamelStubAction(main))) .addSubcommand("thread-dump", new CommandLine(new CamelThreadDump(main))) .addSubcommand("logger", new CommandLine(new LoggerAction(main))) .addSubcommand("gc", new CommandLine(new CamelGCAction(main)))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java index b378979df26..816fe6f8484 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java @@ -140,7 +140,9 @@ public class CamelSourceTop extends ActionWatchCommand { // sort rows rows.sort(this::sortRow); - clearScreen(); + if (watch) { + clearScreen(); + } if (!rows.isEmpty()) { printSource(rows); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubAction.java new file mode 100644 index 00000000000..ba9241d1e36 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubAction.java @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.dsl.jbang.core.commands.action; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import com.github.freva.asciitable.AsciiTable; +import com.github.freva.asciitable.Column; +import com.github.freva.asciitable.HorizontalAlign; +import com.github.freva.asciitable.OverflowBehaviour; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.support.PatternHelper; +import org.apache.camel.util.FileUtil; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.StopWatch; +import org.apache.camel.util.URISupport; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; +import org.apache.camel.util.json.Jsoner; +import org.fusesource.jansi.Ansi; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "stub", description = "Browse stub endpoints", sortOptions = false) +public class CamelStubAction extends ActionWatchCommand { + + @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "0..1") + String name = "*"; + + @CommandLine.Option(names = { "--sort" }, + description = "Sort by name, or total", defaultValue = "name") + String sort; + + @CommandLine.Option(names = { "--filter" }, + description = "Filter endpoints by queue name") + String filter; + + @CommandLine.Option(names = { "--browse" }, + description = "Whether to browse messages queued in the stub endpoints") + boolean browse; + + @CommandLine.Option(names = { "--top" }, defaultValue = "true", + description = "Whether to browse top (latest) messages queued in the stub endpoints") + boolean top = true; + + @CommandLine.Option(names = { "--limit" }, defaultValue = "10", + description = "Filter browsing queues by limiting to the given latest number of messages") + int limit = 10; + + @CommandLine.Option(names = { "--find" }, + description = "Find and highlight matching text (ignore case).", arity = "0..*") + String[] find; + + @CommandLine.Option(names = { "--grep" }, + description = "Filter browsing messages to only output trace matching text (ignore case).", + arity = "0..*") + String[] grep; + + @CommandLine.Option(names = { "--show-headers" }, defaultValue = "true", + description = "Show message headers in traced messages") + boolean showHeaders = true; + + @CommandLine.Option(names = { "--show-body" }, defaultValue = "true", + description = "Show message body in traced messages") + boolean showBody = true; + + @CommandLine.Option(names = { "--compact" }, defaultValue = "true", + description = "Compact output (no empty line separating browsed messages)") + boolean compact = true; + + @CommandLine.Option(names = { "--mask" }, + description = "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys") + boolean mask; + + @CommandLine.Option(names = { "--pretty" }, + description = "Pretty print message body when using JSon or XML format") + boolean pretty; + + @CommandLine.Option(names = { "--logging-color" }, defaultValue = "true", description = "Use colored logging") + boolean loggingColor = true; + + private volatile long pid; + String findAnsi; + private MessageTableHelper tableHelper; + private final Map<String, Ansi.Color> exchangeIdColors = new HashMap<>(); + private int exchangeIdColorsIndex = 1; + + public CamelStubAction(CamelJBangMain main) { + super(main); + } + + @Override + protected Integer doWatchCall() throws Exception { + // setup table helper + tableHelper = new MessageTableHelper(); + tableHelper.setPretty(pretty); + tableHelper.setLoggingColor(loggingColor); + tableHelper.setExchangeIdColorChooser(value -> { + Ansi.Color color = exchangeIdColors.get(value); + if (color == null) { + // grab a new color + exchangeIdColorsIndex++; + if (exchangeIdColorsIndex > 6) { + exchangeIdColorsIndex = 2; + } + color = Ansi.Color.values()[exchangeIdColorsIndex]; + exchangeIdColors.put(value, color); + } + return color; + }); + if (find != null || grep != null) { + findAnsi = Ansi.ansi().fg(Ansi.Color.BLACK).bg(Ansi.Color.YELLOW).a("$0").reset().toString(); + } + + List<Row> rows = new ArrayList<>(); + + List<Long> pids = findPids(name); + if (pids.isEmpty()) { + return 0; + } else if (pids.size() > 1) { + System.out.println("Name or pid " + name + " matches " + pids.size() + + " running Camel integrations. Specify a name or PID that matches exactly one."); + return 0; + } + + this.pid = pids.get(0); + + if (filter == null) { + filter = "*"; + } + + // ensure output file is deleted before executing action + File outputFile = getOutputFile(Long.toString(pid)); + FileUtil.deleteFile(outputFile); + + JsonObject root = new JsonObject(); + root.put("action", "stub"); + root.put("format", "json"); + root.put("browse", browse); + root.put("filter", "*"); + root.put("limit", limit); + File file = getActionFile(Long.toString(pid)); + try { + IOHelper.writeText(root.toJson(), file); + } catch (Exception e) { + // ignore + } + + JsonObject jo = waitForOutputFile(outputFile); + if (jo != null) { + JsonObject me = loadStatus(pid); + if (me != null) { + me = (JsonObject) me.get("context"); + } + + JsonArray arr = (JsonArray) jo.get("queues"); + if (arr != null) { + for (int i = 0; i < arr.size(); i++) { + JsonObject o = (JsonObject) arr.get(i); + Row row = new Row(); + row.pid = pid; + row.name = me != null ? me.getString("name") : null; + row.queue = o.getString("name"); + row.max = o.getInteger("max"); + row.size = o.getInteger("size"); + String uri = o.getString("endpointUri"); + if (uri != null) { + row.endpoint = new JsonObject(); + if (mask) { + uri = URISupport.sanitizeUri(uri); + } + row.endpoint.put("endpoint", uri); + } + row.messages = o.getCollection("messages"); + boolean add = true; + if (filter != null) { + String f = filter; + boolean negate = filter.startsWith("-"); + if (negate) { + f = f.substring(1); + } + // make filtering easier + if (!f.endsWith("*")) { + f += "*"; + } + boolean match = PatternHelper.matchPattern(row.queue, f); + if (negate) { + match = !match; + } + if (!match) { + add = false; + } + } + if (add) { + rows.add(row); + } + } + } + } else { + System.out.println("Response from running Camel with PID " + pid + " not received within 5 seconds"); + return 1; + } + + // sort rows + rows.sort(this::sortRow); + + if (watch) { + clearScreen(); + } + if (!rows.isEmpty()) { + printStub(rows); + } + + // delete output file after use + FileUtil.deleteFile(outputFile); + + return 0; + } + + protected int sortRow(Row o1, Row o2) { + String s = sort; + int negate = 1; + if (s.startsWith("-")) { + s = s.substring(1); + negate = -1; + } + switch (s) { + case "name": + return o1.name.compareToIgnoreCase(o2.name) * negate; + case "total": + return Integer.compare(o1.size, o2.size) * negate; + default: + return 0; + } + } + + private boolean isValidGrep(String line) { + if (grep == null) { + return true; + } + for (String g : grep) { + boolean m = Pattern.compile("(?i)" + g).matcher(line).find(); + if (m) { + return true; + } + } + return false; + } + + protected void printStub(List<Row> rows) { + if (browse) { + for (Row row : rows) { + System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(row), Arrays.asList( + new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> "" + r.pid), + new Column().header("NAME").dataAlign(HorizontalAlign.LEFT) + .maxWidth(30, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(r -> r.name), + new Column().header("QUEUE").dataAlign(HorizontalAlign.LEFT).with(r -> r.queue), + new Column().header("MAX").dataAlign(HorizontalAlign.RIGHT).with(r -> "" + r.max), + new Column().header("TOTAL").dataAlign(HorizontalAlign.RIGHT).with(r -> "" + r.size)))); + + if (row.messages != null) { + List<JsonObject> list = row.messages; + if (top) { + Collections.reverse(list); + } + boolean first = true; + for (JsonObject jo : list) { + JsonObject root = (JsonObject) jo.get("message"); + if (!showHeaders) { + root.remove("headers"); + } + if (!showBody) { + root.remove("body"); + } + String data + = tableHelper.getDataAsTable(root.getString("exchangeId"), root.getString("exchangePattern"), + row.endpoint, root, null); + if (data != null) { + String[] lines = data.split(System.lineSeparator()); + if (lines.length > 0) { + + boolean valid = isValidGrep(data); + if (!valid) { + continue; + } + + if (!compact && first) { + System.out.println(); + } + for (String line : lines) { + if (find != null) { + for (String f : find) { + line = line.replaceAll("(?i)" + f, findAnsi); + } + } + if (grep != null) { + for (String g : grep) { + line = line.replaceAll("(?i)" + g, findAnsi); + } + } + System.out.print(" "); + System.out.println(line); + } + if (!compact) { + System.out.println(); + } + first = false; + } + } + } + } + } + } else { + System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList( + new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> "" + r.pid), + new Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(30, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(r -> r.name), + new Column().header("QUEUE").dataAlign(HorizontalAlign.LEFT).with(r -> r.queue), + new Column().header("MAX").dataAlign(HorizontalAlign.RIGHT).with(r -> "" + r.max), + new Column().header("TOTAL").dataAlign(HorizontalAlign.RIGHT).with(r -> "" + r.size)))); + } + } + + protected JsonObject waitForOutputFile(File outputFile) { + StopWatch watch = new StopWatch(); + while (watch.taken() < 5000) { + try { + // give time for response to be ready + Thread.sleep(100); + + if (outputFile.exists()) { + FileInputStream fis = new FileInputStream(outputFile); + String text = IOHelper.loadText(fis); + IOHelper.close(fis); + return (JsonObject) Jsoner.deserialize(text); + } + + } catch (Exception e) { + // ignore + } + } + return null; + } + + private static class Row { + long pid; + String name; + String queue; + int max; + int size; + JsonObject endpoint; + List<JsonObject> messages; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java index 257c424c074..991c8cf1bcd 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java @@ -145,7 +145,9 @@ public class CamelThreadDump extends ActionWatchCommand { // sort rows rows.sort(this::sortRow); - clearScreen(); + if (watch) { + clearScreen(); + } if (!rows.isEmpty()) { int total = jo.getInteger("threadCount"); int peak = jo.getInteger("peakThreadCount"); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java index f8080cf796f..8e35ad46696 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java @@ -401,6 +401,9 @@ public class MessageTableHelper { } String exchangeIdAsValue() { + if (value == null) { + return ""; + } String s = value.toString(); if (loggingColor) { Ansi.Color color = exchangeIdColorChooser != null ? exchangeIdColorChooser.color(s) : Ansi.Color.DEFAULT; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java index a6280cb63e0..a1bbb302ef5 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java @@ -148,7 +148,9 @@ public class RouteControllerAction extends ActionWatchCommand { // sort rows rows.sort(this::sortRow); - clearScreen(); + if (watch) { + clearScreen(); + } if (!rows.isEmpty()) { if (supervising) { if (header) { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java index eb174fb731f..fc29a58f9b8 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java @@ -112,6 +112,7 @@ public class ListEndpoint extends ProcessWatchCommand { } row.pid = Long.toString(ph.pid()); row.endpoint = o.getString("uri"); + row.stub = o.getBooleanOrDefault("stub", false); row.direction = o.getString("direction"); row.total = o.getString("hits"); row.uptime = extractSince(ph); @@ -166,6 +167,7 @@ public class ListEndpoint extends ProcessWatchCommand { new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age), new Column().header("DIR").with(r -> r.direction), new Column().header("TOTAL").with(r -> r.total), + new Column().header("STUB").dataAlign(HorizontalAlign.CENTER).with(r -> r.stub ? "x" : ""), new Column().header("URI").visible(!wideUri).dataAlign(HorizontalAlign.LEFT) .maxWidth(90, OverflowBehaviour.ELLIPSIS_RIGHT) .with(this::getUri), @@ -214,6 +216,7 @@ public class ListEndpoint extends ProcessWatchCommand { String endpoint; String direction; String total; + boolean stub; } }