You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/08/16 18:47:38 UTC
[camel] branch main updated: CAMEL-19747: camel-jbang - Add command to browse stub queues (#11124)
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 <cl...@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;
}
}