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/04/14 08:38:46 UTC
[camel] 01/03: CAMEL-19236: camel-jbang - Command to send a message.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 3a26c35414c3b5536d6efa998128949a32fdaffe
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Apr 14 08:42:58 2023 +0200
CAMEL-19236: camel-jbang - Command to send a message.
---
.../org/apache/camel/support/MessageHelper.java | 79 ++--
.../camel/cli/connector/LocalCliConnector.java | 143 ++++++++
.../dsl/jbang/core/commands/CamelJBangMain.java | 2 +
.../core/commands/action/CamelSendAction.java | 272 ++++++++++++++
.../core/commands/action/CamelTraceAction.java | 341 ++---------------
.../core/commands/action/MessageTableHelper.java | 408 +++++++++++++++++++++
6 files changed, 902 insertions(+), 343 deletions(-)
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index cbc6289d849..e903dbdb25a 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -808,7 +808,7 @@ public final class MessageHelper {
}
/**
- * Dumps the message as a generic JSon structure.
+ * Dumps the message as a generic JSon structure as text.
*
* @param message the message
* @return the JSon
@@ -818,7 +818,7 @@ public final class MessageHelper {
}
/**
- * Dumps the message as a generic JSon structure.
+ * Dumps the message as a generic JSon structure as text.
*
* @param message the message
* @param includeBody whether or not to include the message body
@@ -829,7 +829,7 @@ public final class MessageHelper {
}
/**
- * Dumps the message as a generic JSon structure.
+ * Dumps the message as a generic JSon structure as text.
*
* @param message the message
* @param includeBody whether or not to include the message body
@@ -841,7 +841,7 @@ public final class MessageHelper {
}
/**
- * Dumps the message as a generic JSon structure.
+ * Dumps the message as a generic JSon structure as text.
*
* @param message the message
* @param includeBody whether or not to include the message body
@@ -859,7 +859,7 @@ public final class MessageHelper {
}
/**
- * Dumps the message as a generic JSon structure.
+ * Dumps the message as a generic JSon structure as text.
*
* @param message the message
* @param includeExchangeProperties whether or not to include exchange properties
@@ -877,6 +877,35 @@ public final class MessageHelper {
Message message, boolean includeExchangeProperties, boolean includeBody, int indent,
boolean allowCachedStreams, boolean allowStreams, boolean allowFiles, int maxChars, boolean pretty) {
+ JsonObject jo = dumpAsJSonObject(message, includeExchangeProperties, includeBody, allowCachedStreams, allowStreams, allowFiles, maxChars);
+ String answer = jo.toJson();
+ if (pretty) {
+ if (indent > 0) {
+ answer = Jsoner.prettyPrint(answer, indent);
+ } else {
+ answer = Jsoner.prettyPrint(answer);
+ }
+ }
+ return answer;
+ }
+
+ /**
+ * Dumps the message as a generic JSon Object.
+ *
+ * @param message the message
+ * @param includeExchangeProperties whether or not to include exchange properties
+ * @param includeBody whether or not to include the message body
+ * @param allowCachedStreams whether to include message body if they are stream cached based
+ * @param allowStreams whether to include message body if they are stream based
+ * @param allowFiles whether to include message body if they are file based
+ * @param maxChars clip body after maximum chars (to avoid very big messages). Use 0 or negative
+ * value to not limit at all.
+ * @return the JSon Object
+ */
+ public static JsonObject dumpAsJSonObject(
+ Message message, boolean includeExchangeProperties, boolean includeBody,
+ boolean allowCachedStreams, boolean allowStreams, boolean allowFiles, int maxChars) {
+
JsonObject root = new JsonObject();
JsonObject jo = new JsonObject();
root.put("message", jo);
@@ -981,15 +1010,7 @@ public final class MessageHelper {
}
}
- String answer = root.toJson();
- if (pretty) {
- if (indent > 0) {
- answer = Jsoner.prettyPrint(answer, indent);
- } else {
- answer = Jsoner.prettyPrint(answer);
- }
- }
- return answer;
+ return root;
}
/**
@@ -1029,13 +1050,31 @@ public final class MessageHelper {
}
/**
- * Dumps the exception as a generic JSon structure.
+ * Dumps the exception as a generic JSon structure as text.
*
* @param indent number of spaces to indent
* @param pretty whether to pretty print JSon
* @return the JSon
*/
public static String dumpExceptionAsJSon(Throwable exception, int indent, boolean pretty) {
+ JsonObject jo = dumpExceptionAsJSonObject(exception);
+ String answer = jo.toJson();
+ if (pretty) {
+ if (indent > 0) {
+ answer = Jsoner.prettyPrint(answer, indent);
+ } else {
+ answer = Jsoner.prettyPrint(answer);
+ }
+ }
+ return answer;
+ }
+
+ /**
+ * Dumps the exception as a generic JSon object.
+ *
+ * @return the JSon object
+ */
+ public static JsonObject dumpExceptionAsJSonObject(Throwable exception) {
JsonObject root = new JsonObject();
JsonObject jo = new JsonObject();
root.put("exception", jo);
@@ -1058,15 +1097,7 @@ public final class MessageHelper {
} catch (Throwable e) {
// ignore as the body is for logging purpose
}
- String answer = root.toJson();
- if (pretty) {
- if (indent > 0) {
- answer = Jsoner.prettyPrint(answer, indent);
- } else {
- answer = Jsoner.prettyPrint(answer);
- }
- }
- return answer;
+ return root;
}
}
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 b63e17482d9..6dd479e5bd0 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
@@ -18,12 +18,15 @@ package org.apache.camel.cli.connector;
import java.io.File;
import java.io.FileInputStream;
+import java.io.InputStream;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -37,7 +40,12 @@ import java.util.stream.Collectors;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
+import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
import org.apache.camel.Route;
import org.apache.camel.api.management.ManagedCamelContext;
import org.apache.camel.console.DevConsole;
@@ -46,12 +54,15 @@ import org.apache.camel.spi.CliConnector;
import org.apache.camel.spi.CliConnectorFactory;
import org.apache.camel.spi.ContextReloadStrategy;
import org.apache.camel.support.DefaultContextReloadStrategy;
+import org.apache.camel.support.EndpointHelper;
+import org.apache.camel.support.MessageHelper;
import org.apache.camel.support.PatternHelper;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StopWatch;
import org.apache.camel.util.concurrent.ThreadHelper;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -66,6 +77,8 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
private static final Logger LOG = LoggerFactory.getLogger(LocalCliConnector.class);
+ private static final int BODY_MAX_CHARS = 128 * 1024;
+
private final CliConnectorFactory cliConnectorFactory;
private CamelContext camelContext;
private int delay = 2000;
@@ -75,6 +88,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
private final AtomicBoolean terminating = new AtomicBoolean();
private ScheduledExecutorService executor;
private volatile ExecutorService terminateExecutor;
+ private ProducerTemplate producer;
private File lockFile;
private File statusFile;
private File actionFile;
@@ -126,6 +140,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
}
}
platformVersion = cliConnectorFactory.getRuntimeVersion();
+ producer = camelContext.createProducerTemplate();
// create thread from JDK so it is not managed by Camel because we want the pool to be independent when
// camel is being stopped which otherwise can lead to stopping the thread pool while the task is running
@@ -294,6 +309,133 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
LOG.trace("Updating output file: {}", outputFile);
IOHelper.writeText(json.toJson(), outputFile);
}
+ } else if ("send".equals(action)) {
+ StopWatch watch = new StopWatch();
+ long timestamp = System.currentTimeMillis();
+ String endpoint = root.getString("endpoint");
+ String body = root.getString("body");
+ String exchangePattern = root.getString("exchangePattern");
+ Collection<JsonObject> headers = root.getCollection("headers");
+ if (body != null) {
+ InputStream is = null;
+ Object b = body;
+ Map<String, Object> map = null;
+ if (body.startsWith("file:")) {
+ File file = new File(body.substring(5));
+ is = new FileInputStream(file);
+ b = is;
+ }
+ if (headers != null) {
+ map = new HashMap<>();
+ for (JsonObject jo : headers) {
+ map.put(jo.getString("key"), jo.getString("value"));
+ }
+ }
+ final Object inputBody = b;
+ final Map<String, Object> inputHeaders = map;
+ Exchange out;
+ Endpoint target = null;
+ if (endpoint == null) {
+ List<Route> routes = camelContext.getRoutes();
+ if (!routes.isEmpty()) {
+ // grab endpoint from 1st route
+ target = routes.get(0).getEndpoint();
+ }
+ } else {
+ // is the endpoint a pattern or route id
+ boolean scheme = endpoint.contains(":");
+ boolean pattern = endpoint.endsWith("*");
+ if (!scheme || pattern) {
+ if (!scheme) {
+ endpoint = endpoint + "*";
+ }
+ for (Route route : camelContext.getRoutes()) {
+ Endpoint e = route.getEndpoint();
+ if (EndpointHelper.matchEndpoint(camelContext, e.getEndpointUri(), endpoint)) {
+ target = e;
+ break;
+ }
+ }
+ if (target == null) {
+ // okay it may refer to a route id
+ for (Route route : camelContext.getRoutes()) {
+ String id = route.getRouteId();
+ Endpoint e = route.getEndpoint();
+ if (EndpointHelper.matchEndpoint(camelContext, id, endpoint)) {
+ target = e;
+ break;
+ }
+ }
+ }
+ } else {
+ target = camelContext.getEndpoint(endpoint);
+ }
+ }
+
+ if (target != null) {
+ out = producer.send(target, new Processor() {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ exchange.getMessage().setBody(inputBody);
+ if (inputHeaders != null) {
+ exchange.getMessage().setHeaders(inputHeaders);
+ }
+ exchange.setPattern(
+ "InOut".equals(exchangePattern) ? ExchangePattern.InOut : ExchangePattern.InOnly);
+ }
+ });
+ IOHelper.close(is);
+ LOG.trace("Updating output file: {}", outputFile);
+ if (out.getException() != null) {
+ JsonObject jo = new JsonObject();
+ jo.put("endpoint", target.getEndpointUri());
+ jo.put("exchangeId", out.getExchangeId());
+ jo.put("exchangePattern", exchangePattern);
+ jo.put("timestamp", timestamp);
+ jo.put("elapsed", watch.taken());
+ jo.put("status", "failed");
+ // avoid double wrap
+ jo.put("exception",
+ MessageHelper.dumpExceptionAsJSonObject(out.getException()).getMap("exception"));
+ IOHelper.writeText(jo.toJson(), outputFile);
+ } else if ("InOut".equals(exchangePattern)) {
+ JsonObject jo = new JsonObject();
+ jo.put("endpoint", target.getEndpointUri());
+ jo.put("exchangeId", out.getExchangeId());
+ jo.put("exchangePattern", exchangePattern);
+ jo.put("timestamp", timestamp);
+ jo.put("elapsed", watch.taken());
+ jo.put("status", "success");
+ // avoid double wrap
+ jo.put("message", MessageHelper.dumpAsJSonObject(out.getMessage(), true, true, true, true, true,
+ BODY_MAX_CHARS).getMap("message"));
+ IOHelper.writeText(jo.toJson(), outputFile);
+ } else {
+ JsonObject jo = new JsonObject();
+ jo.put("endpoint", target.getEndpointUri());
+ jo.put("exchangeId", out.getExchangeId());
+ jo.put("exchangePattern", exchangePattern);
+ jo.put("timestamp", timestamp);
+ jo.put("elapsed", watch.taken());
+ jo.put("status", "success");
+ IOHelper.writeText(jo.toJson(), outputFile);
+ }
+ } else {
+ // there is no valid endpoint
+ JsonObject jo = new JsonObject();
+ jo.put("endpoint", root.getString("endpoint"));
+ jo.put("exchangeId", "");
+ jo.put("exchangePattern", exchangePattern);
+ jo.put("timestamp", timestamp);
+ jo.put("elapsed", watch.taken());
+ jo.put("status", "failed");
+ // avoid double wrap
+ jo.put("exception",
+ MessageHelper.dumpExceptionAsJSonObject(new NoSuchEndpointException(root.getString("endpoint")))
+ .getMap("exception"));
+ IOHelper.writeText(jo.toJson(), outputFile);
+ }
+ }
}
// action done so delete file
@@ -629,6 +771,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
camelContext.getExecutorServiceManager().shutdown(executor);
executor = null;
}
+ ServiceHelper.stopService(producer);
}
private static String getPid() {
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 d0b9bfa7e9f..f536129e90d 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
@@ -27,6 +27,7 @@ import org.apache.camel.dsl.jbang.core.commands.action.CamelReloadAction;
import org.apache.camel.dsl.jbang.core.commands.action.CamelResetStatsAction;
import org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStartAction;
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.CamelThreadDump;
@@ -114,6 +115,7 @@ public class CamelJBangMain implements Callable<Integer> {
.addSubcommand("stop-route", new CommandLine(new CamelRouteStopAction(main)))
.addSubcommand("reset-stats", new CommandLine(new CamelResetStatsAction(main)))
.addSubcommand("reload", new CommandLine(new CamelReloadAction(main)))
+ .addSubcommand("send", new CommandLine(new CamelSendAction(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/CamelSendAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSendAction.java
new file mode 100644
index 00000000000..a8cdf8d650e
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSendAction.java
@@ -0,0 +1,272 @@
+/*
+ * 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.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
+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 org.fusesource.jansi.AnsiConsole;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "send",
+ description = "Sends a message to a system via an existing running Camel integration")
+public class CamelSendAction extends ActionBaseCommand {
+
+ @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "1")
+ String name;
+
+ @CommandLine.Option(names = { "--endpoint" },
+ description = "Endpoint where to send the message (can be uri, pattern, or refer to a route id)")
+ String endpoint;
+
+ @CommandLine.Option(names = { "--reply" },
+ description = "Whether to expect a reply message (InOut vs InOut messaging style)")
+ boolean reply;
+
+ @CommandLine.Option(names = { "--reply-file" },
+ description = "Saves reply message to the file with the given name (override if exists)")
+ String replyFile;
+
+ @CommandLine.Option(names = { "--body" }, required = true,
+ description = "Message body to send (prefix with file: to refer to loading message body from file)")
+ String body;
+
+ @CommandLine.Option(names = { "--header" },
+ description = "Message header (key=value)")
+ List<String> headers;
+
+ @CommandLine.Option(names = { "--timeout" }, defaultValue = "20000",
+ description = "Timeout in millis waiting for message to be sent (and reply message if InOut messaging)")
+ long timeout = 20000;
+
+ @CommandLine.Option(names = { "--show-exchange-properties" }, defaultValue = "false",
+ description = "Show exchange properties in traced messages")
+ boolean showExchangeProperties;
+
+ @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 = { "--show-exception" }, defaultValue = "true",
+ description = "Show exception and stacktrace for failed messages")
+ boolean showException = true;
+
+ @CommandLine.Option(names = { "--logging-color" }, defaultValue = "true", description = "Use colored logging")
+ boolean loggingColor = true;
+
+ @CommandLine.Option(names = { "--pretty" },
+ description = "Pretty print message body when using JSon or XML format")
+ boolean pretty;
+
+ private volatile long pid;
+
+ private MessageTableHelper tableHelper;
+
+ public CamelSendAction(CamelJBangMain main) {
+ super(main);
+ }
+
+ @Override
+ public Integer doCall() throws Exception {
+ 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);
+
+ // ensure output file is deleted before executing action
+ File outputFile = getOutputFile(Long.toString(pid));
+ FileUtil.deleteFile(outputFile);
+
+ JsonObject root = new JsonObject();
+ root.put("action", "send");
+ root.put("endpoint", endpoint);
+ String mep = (reply || replyFile != null) ? "InOut" : "InOnly";
+ root.put("exchangePattern", mep);
+ root.put("body", body);
+ if (headers != null) {
+ JsonArray arr = new JsonArray();
+ for (String h : headers) {
+ JsonObject jo = new JsonObject();
+ if (!h.contains("=")) {
+ System.out.println("Header must be in key=value format, was: " + h);
+ return 0;
+ }
+ jo.put("key", StringHelper.before(h, "="));
+ jo.put("value", StringHelper.after(h, "="));
+ arr.add(jo);
+ }
+ root.put("headers", arr);
+ }
+ File f = getActionFile(Long.toString(pid));
+ try {
+ IOHelper.writeText(root.toJson(), f);
+ } catch (Exception e) {
+ // ignore
+ }
+
+ JsonObject jo = waitForOutputFile(outputFile);
+ if (jo != null) {
+ printStatusLine(jo);
+ String exchangeId = jo.getString("exchangeId");
+ JsonObject message = jo.getMap("message");
+ JsonObject cause = jo.getMap("exception");
+ if (message != null || cause != null) {
+ if (replyFile != null) {
+ File target = new File(replyFile);
+ String json = jo.toJson();
+ if (pretty) {
+ json = Jsoner.prettyPrint(json, 2);
+ }
+ IOHelper.writeText(json, target);
+ }
+ if (!showExchangeProperties && message != null) {
+ message.remove("exchangeProperties");
+ }
+ if (!showHeaders && message != null) {
+ message.remove("headers");
+ }
+ if (!showBody && message != null) {
+ message.remove("body");
+ }
+ if (!showException && cause != null) {
+ cause = null;
+ }
+ if (replyFile == null) {
+ tableHelper = new MessageTableHelper();
+ tableHelper.setPretty(pretty);
+ tableHelper.setLoggingColor(loggingColor);
+ tableHelper.setShowExchangeProperties(showExchangeProperties);
+ String table = tableHelper.getDataAsTable(exchangeId, mep, jo, message, cause);
+ System.out.println(table);
+ }
+ }
+ }
+
+ // delete output file after use
+ FileUtil.deleteFile(outputFile);
+
+ return 0;
+ }
+
+ private void printStatusLine(JsonObject jo) {
+ // timstamp
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ String ts = sdf.format(new Date(jo.getLong("timestamp")));
+ if (loggingColor) {
+ AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(ts).reset());
+ } else {
+ System.out.print(ts);
+ }
+ // pid
+ System.out.print(" ");
+ String p = String.format("%5.5s", this.pid);
+ if (loggingColor) {
+ AnsiConsole.out().print(Ansi.ansi().fgMagenta().a(p).reset());
+ AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(" --- ").reset());
+ } else {
+ System.out.print(p);
+ System.out.print(" --- ");
+ }
+ // endpoint
+ String ids = jo.getString("endpoint");
+ if (ids.length() > 40) {
+ ids = ids.substring(ids.length() - 40);
+ }
+ ids = String.format("%40.40s", ids);
+ if (loggingColor) {
+ AnsiConsole.out().print(Ansi.ansi().fgCyan().a(ids).reset());
+ } else {
+ System.out.print(ids);
+ }
+ System.out.print(" : ");
+ // status
+ System.out.print(getStatus(jo));
+ // elapsed
+ String e = TimeUtils.printDuration(jo.getLong("elapsed"), true);
+ if (loggingColor) {
+ AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(" (" + e + ")").reset());
+ } else {
+ System.out.print("(" + e + ")");
+ }
+ System.out.println();
+ }
+
+ private String getStatus(JsonObject r) {
+ boolean failed = "failed".equals(r.getString("status"));
+ boolean reply = r.containsKey("message");
+ String status;
+ if (failed) {
+ status = "Failed (exception)";
+ } else if (replyFile != null) {
+ status = "Reply saved to file (success)";
+ } else if (reply) {
+ status = "Reply received (success)";
+ } else {
+ status = "Sent (success)";
+ }
+ if (loggingColor) {
+ return Ansi.ansi().fg(failed ? Ansi.Color.RED : Ansi.Color.GREEN).a(status).reset().toString();
+ } else {
+ return status;
+ }
+ }
+
+ protected JsonObject waitForOutputFile(File outputFile) {
+ StopWatch watch = new StopWatch();
+ while (watch.taken() < timeout) {
+ try {
+ // give time for response to be ready
+ Thread.sleep(20);
+
+ 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;
+ }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
index 97de403abc0..6b449764e43 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
@@ -23,7 +23,6 @@ import java.io.LineNumberReader;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,15 +35,9 @@ import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
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.catalog.impl.TimePatternConverter;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
-import org.apache.camel.dsl.jbang.core.common.JSonHelper;
import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
-import org.apache.camel.dsl.jbang.core.common.XmlHelper;
import org.apache.camel.util.StopWatch;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.TimeUtils;
@@ -152,6 +145,8 @@ public class CamelTraceAction extends ActionBaseCommand {
private int nameMaxWidth;
private boolean prefixShown;
+ private MessageTableHelper tableHelper;
+
private final Map<String, Ansi.Color> nameColors = new HashMap<>();
private final Map<String, Ansi.Color> exchangeIdColors = new HashMap<>();
private int exchangeIdColorsIndex = 1;
@@ -162,6 +157,25 @@ public class CamelTraceAction extends ActionBaseCommand {
@Override
public Integer doCall() throws Exception {
+ // setup table helper
+ tableHelper = new MessageTableHelper();
+ tableHelper.setPretty(pretty);
+ tableHelper.setLoggingColor(loggingColor);
+ tableHelper.setShowExchangeProperties(showExchangeProperties);
+ 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;
+ });
+
Map<Long, Pid> pids = new LinkedHashMap<>();
if (latest) {
@@ -675,130 +689,7 @@ public class CamelTraceAction extends ActionBaseCommand {
}
private String getDataAsTable(Row r) {
- List<TableRow> rows = new ArrayList<>();
-
- TableRow eRow = new TableRow("Exchange", r.message.getString("exchangeType"), r.exchangePattern, r.exchangeId);
- String tab1 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
- new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAsString)));
- String tab1b = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.CENTER)
- .minWidth(18).maxWidth(18).with(TableRow::mepAsKey),
- new Column().dataAlign(HorizontalAlign.RIGHT)
- .maxWidth(80).with(TableRow::exchangeIdAsValue)));
- // exchange properties
- JsonArray arr = r.message.getCollection("exchangeProperties");
- if (arr != null) {
- for (Object o : arr) {
- JsonObject jo = (JsonObject) o;
- rows.add(new TableRow("Property", jo.getString("type"), jo.getString("key"), jo.get("value")));
- }
- }
- // internal exchange properties
- arr = r.message.getCollection("internalExchangeProperties");
- if (arr != null) {
- for (Object o : arr) {
- JsonObject jo = (JsonObject) o;
- rows.add(new TableRow("Property", jo.getString("type"), jo.getString("key"), jo.get("value")));
- }
- }
- String tab2 = AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(25).maxWidth(50, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
- new Column().dataAlign(HorizontalAlign.RIGHT)
- .minWidth(25).maxWidth(40, OverflowBehaviour.NEWLINE).with(TableRow::keyAsString),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsString)));
- rows.clear();
-
- // message type before headers
- TableRow msgRow = new TableRow("Message", r.message.getString("messageType"), null, null);
- String tab3 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(msgRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
- new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAsString)));
- arr = r.message.getCollection("headers");
- if (arr != null) {
- for (Object o : arr) {
- JsonObject jo = (JsonObject) o;
- rows.add(new TableRow("Header", jo.getString("type"), jo.getString("key"), jo.get("value")));
- }
- }
- // headers
- String tab4 = AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(25).maxWidth(50, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
- new Column().dataAlign(HorizontalAlign.RIGHT)
- .minWidth(25).maxWidth(40, OverflowBehaviour.NEWLINE).with(TableRow::keyAsString),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsString)));
-
- // body and type
- JsonObject jo = r.message.getMap("body");
- TableRow bodyRow = new TableRow("Body", jo.getString("type"), null, jo.get("value"), jo.getLong("position"));
- String tab5 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(bodyRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
- new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAndLengthAsString)));
- // body value only (span)
- String tab6 = null;
- if (bodyRow.value != null) {
- tab6 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(bodyRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT).maxWidth(160, OverflowBehaviour.NEWLINE)
- .with(b -> pretty ? bodyRow.valueAsStringPretty() : bodyRow.valueAsString())));
- }
- String tab7 = null;
- jo = r.exception;
- if (jo != null) {
- eRow = new TableRow("Exception", jo.getString("type"), null, jo.get("message"));
- tab7 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT)
- .minWidth(showExchangeProperties ? 12 : 10)
- .with(TableRow::kindAsStringRed),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .maxWidth(40, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
- new Column().dataAlign(HorizontalAlign.LEFT)
- .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsStringRed)));
- }
- // stacktrace only (span)
- String tab8 = null;
- if (jo != null) {
- eRow = new TableRow("Stacktrace", null, null, jo.get("stackTrace"));
- tab8 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
- new Column().dataAlign(HorizontalAlign.LEFT).maxWidth(160, OverflowBehaviour.NEWLINE)
- .with(TableRow::valueAsStringRed)));
- }
- String answer = "";
- if (tab1 != null && tab1b != null && !tab1.isEmpty()) {
- answer = answer + tab1 + tab1b + System.lineSeparator();
- }
- if (tab2 != null && !tab2.isEmpty()) {
- answer = answer + tab2 + System.lineSeparator();
- }
- if (tab3 != null && !tab3.isEmpty()) {
- answer = answer + tab3 + System.lineSeparator();
- }
- if (tab4 != null && !tab4.isEmpty()) {
- answer = answer + tab4 + System.lineSeparator();
- }
- if (tab5 != null && !tab5.isEmpty()) {
- answer = answer + tab5 + System.lineSeparator();
- }
- if (tab6 != null && !tab6.isEmpty()) {
- answer = answer + tab6 + System.lineSeparator();
- }
- if (tab7 != null && !tab7.isEmpty()) {
- answer = answer + tab7 + System.lineSeparator();
- }
- if (tab8 != null && !tab8.isEmpty()) {
- answer = answer + tab8 + System.lineSeparator();
- }
- return answer;
+ return tableHelper.getDataAsTable(r.exchangeId, r.exchangePattern, null, r.message, r.exception);
}
private String getElapsed(Row r) {
@@ -891,192 +782,4 @@ public class CamelTraceAction extends ActionBaseCommand {
}
- private class TableRow {
- String kind;
- String type;
- String key;
- Object value;
- Long position;
-
- TableRow(String kind, String type, String key, Object value) {
- this(kind, type, key, value, null);
- }
-
- TableRow(String kind, String type, String key, Object value, Long position) {
- this.kind = kind;
- this.type = type;
- this.key = key;
- this.value = value;
- this.position = position;
- }
-
- String valueAsString() {
- return value != null ? value.toString() : "null";
- }
-
- String valueAsStringPretty() {
- if (value == null) {
- return "null";
- }
- boolean json = false;
- String s = value.toString();
- if (!s.isEmpty()) {
- try {
- s = Jsoner.unescape(s);
- if (loggingColor) {
- s = JSonHelper.colorPrint(s, 2, true);
- } else {
- s = JSonHelper.prettyPrint(s, 2);
- }
- if (s != null && !s.isEmpty()) {
- json = true;
- }
- } catch (Throwable e) {
- // ignore as not json
- }
- if (s == null || s.isEmpty()) {
- s = value.toString();
- }
- if (!json) {
- // try with xml
- try {
- s = Jsoner.unescape(s);
- if (loggingColor) {
- s = XmlHelper.colorPrint(s, 2, true);
- } else {
- s = XmlHelper.prettyPrint(s, 2);
- }
- } catch (Throwable e) {
- // ignore as not xml
- }
- }
- if (s == null || s.isEmpty()) {
- s = value.toString();
- }
- }
- if (s == null) {
- return "null";
- }
- return s;
- }
-
- String valueAsStringRed() {
- if (value != null) {
- if (loggingColor) {
- return Ansi.ansi().fgRed().a(value).reset().toString();
- } else {
- return value.toString();
- }
- }
- return "";
- }
-
- String keyAsString() {
- if (key == null) {
- return "";
- }
- return key;
- }
-
- String kindAsString() {
- return kind;
- }
-
- String kindAsStringRed() {
- if (loggingColor) {
- return Ansi.ansi().fgRed().a(kind).reset().toString();
- } else {
- return kind;
- }
- }
-
- String typeAsString() {
- String s;
- if (type == null) {
- s = "null";
- } else if (type.startsWith("java.util.concurrent")) {
- s = type.substring(21);
- } else if (type.startsWith("java.lang.") || type.startsWith("java.util.")) {
- s = type.substring(10);
- } else if (type.startsWith("org.apache.camel.support.")) {
- s = type.substring(25);
- } else if (type.startsWith("org.apache.camel.converter.stream.")) {
- s = type.substring(34);
- } else {
- s = type;
- }
- s = "(" + s + ")";
- if (loggingColor) {
- s = Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
- }
- return s;
- }
-
- String typeAndLengthAsString() {
- String s;
- if (type == null) {
- s = "null";
- } else if (type.startsWith("java.util.concurrent")) {
- s = type.substring(21);
- } else if (type.startsWith("java.lang.") || type.startsWith("java.util.")) {
- s = type.substring(10);
- } else if (type.startsWith("org.apache.camel.support.")) {
- s = type.substring(25);
- } else if (type.startsWith("org.apache.camel.converter.stream.")) {
- s = type.substring(34);
- } else {
- s = type;
- }
- s = "(" + s + ")";
- int l = valueLength();
- long p = position != null ? position : -1;
- if (l != -1 & p != -1) {
- s = s + " (pos: " + p + " length: " + l + ")";
- } else if (l != -1) {
- s = s + " (length: " + l + ")";
- } else if (p != -1) {
- s = s + " (pos: " + p + ")";
- }
- if (loggingColor) {
- s = Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
- }
- return s;
- }
-
- String mepAsKey() {
- String s = key;
- if (loggingColor) {
- s = Ansi.ansi().fgBrightMagenta().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
- }
- return s;
- }
-
- String exchangeIdAsValue() {
- String s = value.toString();
- if (loggingColor) {
- Ansi.Color color = exchangeIdColors.get(s);
- if (color == null) {
- // grab a new color
- exchangeIdColorsIndex++;
- if (exchangeIdColorsIndex > 6) {
- exchangeIdColorsIndex = 2;
- }
- color = Ansi.Color.values()[exchangeIdColorsIndex];
- exchangeIdColors.put(s, color);
- }
- s = Ansi.ansi().fg(color).a(s).reset().toString();
- }
- return s;
- }
-
- int valueLength() {
- if (value == null) {
- return -1;
- } else {
- return valueAsString().length();
- }
- }
-
- }
-
}
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
new file mode 100644
index 00000000000..cf7282e899a
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java
@@ -0,0 +1,408 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+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.common.JSonHelper;
+import org.apache.camel.dsl.jbang.core.common.XmlHelper;
+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;
+
+/**
+ * Helper to output message details (headers, body) in a table like structure with pretty and color supported.
+ */
+public class MessageTableHelper {
+
+ @FunctionalInterface
+ interface ColorChooser {
+ Ansi.Color color(String value);
+ }
+
+ private boolean loggingColor;
+ private boolean pretty;
+ private boolean showExchangeProperties;
+ private ColorChooser exchangeIdColorChooser;
+
+ public boolean isLoggingColor() {
+ return loggingColor;
+ }
+
+ public void setLoggingColor(boolean loggingColor) {
+ this.loggingColor = loggingColor;
+ }
+
+ public boolean isPretty() {
+ return pretty;
+ }
+
+ public void setPretty(boolean pretty) {
+ this.pretty = pretty;
+ }
+
+ public boolean isShowExchangeProperties() {
+ return showExchangeProperties;
+ }
+
+ public void setShowExchangeProperties(boolean showExchangeProperties) {
+ this.showExchangeProperties = showExchangeProperties;
+ }
+
+ public ColorChooser getExchangeIdColorChooser() {
+ return exchangeIdColorChooser;
+ }
+
+ public void setExchangeIdColorChooser(ColorChooser exchangeIdColorChooser) {
+ this.exchangeIdColorChooser = exchangeIdColorChooser;
+ }
+
+ public String getDataAsTable(
+ String exchangeId, String exchangePattern,
+ JsonObject endpoint, JsonObject root, JsonObject cause) {
+
+ List<TableRow> rows = new ArrayList<>();
+ TableRow eRow;
+ String tab0 = null, tab1 = null, tab1b = null, tab2 = null, tab3 = null, tab4 = null, tab5 = null, tab6 = null;
+
+ if (endpoint != null) {
+ eRow = new TableRow("Endpoint", endpoint.getString("endpoint"), null, null);
+ tab0 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAsString)));
+ }
+
+ if (root != null) {
+ eRow = new TableRow("Exchange", root.getString("exchangeType"), exchangePattern, exchangeId);
+ tab1 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAsString)));
+ tab1b = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.CENTER)
+ .minWidth(18).maxWidth(18).with(TableRow::mepAsKey),
+ new Column().dataAlign(HorizontalAlign.RIGHT)
+ .maxWidth(80).with(TableRow::exchangeIdAsValue)));
+ // exchange properties
+ JsonArray arr = root.getCollection("exchangeProperties");
+ if (arr != null) {
+ for (Object o : arr) {
+ JsonObject jo = (JsonObject) o;
+ rows.add(new TableRow("Property", jo.getString("type"), jo.getString("key"), jo.get("value")));
+ }
+ }
+ // internal exchange properties
+ arr = root.getCollection("internalExchangeProperties");
+ if (arr != null) {
+ for (Object o : arr) {
+ JsonObject jo = (JsonObject) o;
+ rows.add(new TableRow("Property", jo.getString("type"), jo.getString("key"), jo.get("value")));
+ }
+ }
+ tab2 = AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(25).maxWidth(50, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
+ new Column().dataAlign(HorizontalAlign.RIGHT)
+ .minWidth(25).maxWidth(40, OverflowBehaviour.NEWLINE).with(TableRow::keyAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsString)));
+ rows.clear();
+
+ // message type before headers
+ TableRow msgRow = new TableRow("Message", root.getString("messageType"), null, null);
+ tab3 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(msgRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAsString)));
+ arr = root.getCollection("headers");
+ if (arr != null) {
+ for (Object o : arr) {
+ JsonObject jo = (JsonObject) o;
+ rows.add(new TableRow("Header", jo.getString("type"), jo.getString("key"), jo.get("value")));
+ }
+ }
+ // headers
+ tab4 = AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(25).maxWidth(50, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
+ new Column().dataAlign(HorizontalAlign.RIGHT)
+ .minWidth(25).maxWidth(40, OverflowBehaviour.NEWLINE).with(TableRow::keyAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsString)));
+
+ // body and type
+ JsonObject jo = root.getMap("body");
+ if (jo != null) {
+ TableRow bodyRow = new TableRow("Body", jo.getString("type"), null, jo.get("value"), jo.getLong("position"));
+ tab5 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(bodyRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10).with(TableRow::kindAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT).with(TableRow::typeAndLengthAsString)));
+ // body value only (span)
+ if (bodyRow.value != null) {
+ tab6 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(bodyRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT).maxWidth(160, OverflowBehaviour.NEWLINE)
+ .with(b -> pretty ? bodyRow.valueAsStringPretty() : bodyRow.valueAsString())));
+ }
+ }
+ }
+
+ String tab7 = null;
+ if (cause != null) {
+ eRow = new TableRow("Exception", cause.getString("type"), null, cause.get("message"));
+ tab7 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .minWidth(showExchangeProperties ? 12 : 10)
+ .with(TableRow::kindAsStringRed),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(40, OverflowBehaviour.CLIP_LEFT).with(TableRow::typeAsString),
+ new Column().dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(80, OverflowBehaviour.NEWLINE).with(TableRow::valueAsStringRed)));
+ }
+ // stacktrace only (span)
+ String tab8 = null;
+ if (cause != null) {
+ String value = cause.getString("stackTrace");
+ value = Jsoner.unescape(value);
+ eRow = new TableRow("Stacktrace", null, null, value);
+ tab8 = AsciiTable.getTable(AsciiTable.NO_BORDERS, List.of(eRow), Arrays.asList(
+ new Column().dataAlign(HorizontalAlign.LEFT).maxWidth(160, OverflowBehaviour.NEWLINE)
+ .with(TableRow::valueAsStringRed)));
+ }
+ String answer = "";
+ if (tab0 != null && !tab0.isEmpty()) {
+ answer = answer + tab0 + System.lineSeparator();
+ }
+ if (tab1 != null && tab1b != null && !tab1.isEmpty()) {
+ answer = answer + tab1 + tab1b + System.lineSeparator();
+ }
+ if (tab2 != null && !tab2.isEmpty()) {
+ answer = answer + tab2 + System.lineSeparator();
+ }
+ if (tab3 != null && !tab3.isEmpty()) {
+ answer = answer + tab3 + System.lineSeparator();
+ }
+ if (tab4 != null && !tab4.isEmpty()) {
+ answer = answer + tab4 + System.lineSeparator();
+ }
+ if (tab5 != null && !tab5.isEmpty()) {
+ answer = answer + tab5 + System.lineSeparator();
+ }
+ if (tab6 != null && !tab6.isEmpty()) {
+ answer = answer + tab6 + System.lineSeparator();
+ }
+ if (tab7 != null && !tab7.isEmpty()) {
+ answer = answer + tab7 + System.lineSeparator();
+ }
+ if (tab8 != null && !tab8.isEmpty()) {
+ answer = answer + tab8 + System.lineSeparator();
+ }
+ return answer;
+ }
+
+ private class TableRow {
+ String kind;
+ String type;
+ String key;
+ Object value;
+ Long position;
+
+ TableRow(String kind, String type, String key, Object value) {
+ this(kind, type, key, value, null);
+ }
+
+ TableRow(String kind, String type, String key, Object value, Long position) {
+ this.kind = kind;
+ this.type = type;
+ this.key = key;
+ this.value = value;
+ this.position = position;
+ }
+
+ String valueAsString() {
+ return value != null ? value.toString() : "null";
+ }
+
+ String valueAsStringPretty() {
+ if (value == null) {
+ return "null";
+ }
+ boolean json = false;
+ String s = value.toString();
+ if (!s.isEmpty()) {
+ try {
+ s = Jsoner.unescape(s);
+ if (loggingColor) {
+ s = JSonHelper.colorPrint(s, 2, true);
+ } else {
+ s = JSonHelper.prettyPrint(s, 2);
+ }
+ if (s != null && !s.isEmpty()) {
+ json = true;
+ }
+ } catch (Throwable e) {
+ // ignore as not json
+ }
+ if (s == null || s.isEmpty()) {
+ s = value.toString();
+ }
+ if (!json) {
+ // try with xml
+ try {
+ s = Jsoner.unescape(s);
+ if (loggingColor) {
+ s = XmlHelper.colorPrint(s, 2, true);
+ } else {
+ s = XmlHelper.prettyPrint(s, 2);
+ }
+ } catch (Throwable e) {
+ // ignore as not xml
+ }
+ }
+ if (s == null || s.isEmpty()) {
+ s = value.toString();
+ }
+ }
+ if (s == null) {
+ return "null";
+ }
+ return s;
+ }
+
+ String valueAsStringRed() {
+ if (value != null) {
+ if (loggingColor) {
+ return Ansi.ansi().fgRed().a(value).reset().toString();
+ } else {
+ return value.toString();
+ }
+ }
+ return "";
+ }
+
+ String keyAsString() {
+ if (key == null) {
+ return "";
+ }
+ return key;
+ }
+
+ String kindAsString() {
+ return kind;
+ }
+
+ String kindAsStringRed() {
+ if (loggingColor) {
+ return Ansi.ansi().fgRed().a(kind).reset().toString();
+ } else {
+ return kind;
+ }
+ }
+
+ String typeAsString() {
+ String s;
+ if (type == null) {
+ s = "null";
+ } else if (type.startsWith("java.util.concurrent")) {
+ s = type.substring(21);
+ } else if (type.startsWith("java.lang.") || type.startsWith("java.util.")) {
+ s = type.substring(10);
+ } else if (type.startsWith("org.apache.camel.support.")) {
+ s = type.substring(25);
+ } else if (type.startsWith("org.apache.camel.converter.stream.")) {
+ s = type.substring(34);
+ } else {
+ s = type;
+ }
+ s = "(" + s + ")";
+ if (loggingColor) {
+ s = Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
+ }
+ return s;
+ }
+
+ String typeAndLengthAsString() {
+ String s;
+ if (type == null) {
+ s = "null";
+ } else if (type.startsWith("java.util.concurrent")) {
+ s = type.substring(21);
+ } else if (type.startsWith("java.lang.") || type.startsWith("java.util.")) {
+ s = type.substring(10);
+ } else if (type.startsWith("org.apache.camel.support.")) {
+ s = type.substring(25);
+ } else if (type.startsWith("org.apache.camel.converter.stream.")) {
+ s = type.substring(34);
+ } else {
+ s = type;
+ }
+ s = "(" + s + ")";
+ int l = valueLength();
+ long p = position != null ? position : -1;
+ if (l != -1 & p != -1) {
+ s = s + " (pos: " + p + " length: " + l + ")";
+ } else if (l != -1) {
+ s = s + " (length: " + l + ")";
+ } else if (p != -1) {
+ s = s + " (pos: " + p + ")";
+ }
+ if (loggingColor) {
+ s = Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
+ }
+ return s;
+ }
+
+ String mepAsKey() {
+ String s = key;
+ if (loggingColor) {
+ s = Ansi.ansi().fgBrightMagenta().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
+ }
+ return s;
+ }
+
+ String exchangeIdAsValue() {
+ String s = value.toString();
+ if (loggingColor) {
+ Ansi.Color color = exchangeIdColorChooser != null ? exchangeIdColorChooser.color(s) : Ansi.Color.DEFAULT;
+ s = Ansi.ansi().fg(color).a(s).reset().toString();
+ }
+ return s;
+ }
+
+ int valueLength() {
+ if (value == null) {
+ return -1;
+ } else {
+ return valueAsString().length();
+ }
+ }
+
+ }
+
+}