This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch it
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 37872879bb7635794c5b562dde50b5a90c09fa17
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Dec 7 08:37:10 2025 +0100

    CAMEL-22703: camel-jbang - history to have it mode to show message data 
like debugger
---
 .../impl/console/MessageHistoryDevConsole.java     |  73 ++-
 .../camel/dsl/jbang/core/commands/Debug.java       |   5 +-
 .../core/commands/action/CamelHistoryAction.java   | 507 ++++++++++++++++++++-
 3 files changed, 580 insertions(+), 5 deletions(-)

diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/MessageHistoryDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/MessageHistoryDevConsole.java
index f2d0c6656190..86cbad472945 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/MessageHistoryDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/MessageHistoryDevConsole.java
@@ -16,21 +16,31 @@
  */
 package org.apache.camel.impl.console;
 
+import java.io.LineNumberReader;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
+import org.apache.camel.Route;
 import org.apache.camel.spi.BacklogTracer;
 import org.apache.camel.spi.BacklogTracerEventMessage;
 import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 
 @DevConsole(name = "message-history", displayName = "Message History", 
description = "History of latest completed exchange")
 @Configurer(extended = true)
 public class MessageHistoryDevConsole extends AbstractDevConsole {
 
+    public static final String CODE_LIMIT = "codeLimit";
+
     public MessageHistoryDevConsole() {
         super("camel", "message-history", "Message History", "History of 
latest completed exchange");
     }
@@ -53,14 +63,30 @@ public class MessageHistoryDevConsole extends 
AbstractDevConsole {
     protected JsonObject doCallJson(Map<String, Object> options) {
         JsonObject root = new JsonObject();
 
+        String codeLimit = (String) options.getOrDefault(CODE_LIMIT, "5");
+
         BacklogTracer tracer = 
getCamelContext().getCamelContextExtension().getContextPlugin(BacklogTracer.class);
         if (tracer != null) {
             JsonArray arr = new JsonArray();
 
             Collection<BacklogTracerEventMessage> queue = 
tracer.getLatestMessageHistory();
             for (BacklogTracerEventMessage t : queue) {
-                JsonObject jo = (JsonObject) t.asJSon();
-                arr.add(jo);
+                JsonObject to = (JsonObject) t.asJSon();
+
+                // enrich with source code +/- lines around location
+                int limit = Integer.parseInt(codeLimit);
+                if (limit > 0) {
+                    String rid = to.getString("routeId");
+                    String loc = to.getString("location");
+                    if (rid != null) {
+                        List<JsonObject> code = enrichSourceCode(rid, loc, 
limit);
+                        if (code != null && !code.isEmpty()) {
+                            to.put("code", code);
+                        }
+                    }
+                }
+
+                arr.add(to);
             }
             root.put("name", getCamelContext().getName());
             root.put("traces", arr);
@@ -69,4 +95,47 @@ public class MessageHistoryDevConsole extends 
AbstractDevConsole {
         return root;
     }
 
+    private List<JsonObject> enrichSourceCode(String routeId, String location, 
int lines) {
+        Route route = getCamelContext().getRoute(routeId);
+        if (route == null) {
+            return null;
+        }
+        Resource resource = route.getSourceResource();
+        if (resource == null) {
+            return null;
+        }
+
+        List<JsonObject> code = new ArrayList<>();
+
+        location = StringHelper.afterLast(location, ":");
+        int line = 0;
+        try {
+            if (location != null) {
+                line = Integer.parseInt(location);
+            }
+            LineNumberReader reader = new 
LineNumberReader(resource.getReader());
+            for (int i = 1; i <= line + lines; i++) {
+                String t = reader.readLine();
+                if (t != null) {
+                    int low = line - lines + 2; // grab more of the following 
code than previous code (+2)
+                    int high = line + lines + 1 + 2;
+                    if (i >= low && i <= high) {
+                        JsonObject c = new JsonObject();
+                        c.put("line", i);
+                        if (line == i) {
+                            c.put("match", true);
+                        }
+                        c.put("code", Jsoner.escape(t));
+                        code.add(c);
+                    }
+                }
+            }
+            IOHelper.close(reader);
+        } catch (Exception e) {
+            // ignore
+        }
+
+        return code;
+    }
+
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
index c05836ee7457..45b3bed57c55 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
@@ -962,9 +962,12 @@ public class Debug extends Run {
                         c = Jsoner.unescape(h.code);
                         c = c.trim();
                     } else {
-                        c = Jsoner.unescape(h.nodeLabel);
+                        c = Jsoner.escape(h.nodeLabel);
                         c = c.trim();
                     }
+                    // pad with level
+                    String pad = StringHelper.padString(h.level);
+                    c = pad + c;
 
                     String fids = String.format("%-30.30s", ids);
                     String msg;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
index fc1eec3285f4..f7eea0727e29 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
@@ -16,15 +16,20 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
+import java.io.Console;
 import java.io.LineNumberReader;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import com.github.freva.asciitable.AsciiTable;
 import com.github.freva.asciitable.Column;
@@ -35,6 +40,7 @@ import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.TimeUtils;
@@ -43,6 +49,7 @@ 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 = "history",
@@ -52,6 +59,10 @@ public class CamelHistoryAction extends ActionWatchCommand {
     @CommandLine.Parameters(description = "Name or pid of running Camel 
integration", arity = "0..1")
     String name = "*";
 
+    @CommandLine.Option(names = { "--it" },
+                        description = "Interactive mode for enhanced history 
information")
+    boolean it;
+
     @CommandLine.Option(names = { "--source" },
                         description = "Prefer to display source filename/code 
instead of IDs")
     boolean source;
@@ -68,12 +79,59 @@ public class CamelHistoryAction extends ActionWatchCommand {
                         description = "Limit Split to a maximum number of 
entries to be displayed")
     int limitSplit;
 
+    @CommandLine.Option(names = { "--timestamp" }, defaultValue = "true",
+            description = "Print timestamp.")
+    boolean timestamp = true;
+
+    @CommandLine.Option(names = { "--ago" },
+            description = "Use ago instead of yyyy-MM-dd HH:mm:ss in 
timestamp.")
+    boolean ago;
+
+    @CommandLine.Option(names = { "--show-exchange-properties" }, defaultValue 
= "false",
+            description = "Show exchange properties in debug messages")
+    boolean showExchangeProperties;
+
+    @CommandLine.Option(names = { "--show-exchange-variables" }, defaultValue 
= "true",
+            description = "Show exchange variables in debug messages")
+    boolean showExchangeVariables = true;
+
+    @CommandLine.Option(names = { "--show-headers" }, defaultValue = "true",
+            description = "Show message headers in debug messages")
+    boolean showHeaders = true;
+
+    @CommandLine.Option(names = { "--show-body" }, defaultValue = "true",
+            description = "Show message body in debug 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 = { "--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 MessageTableHelper tableHelper;
+    private final AtomicBoolean quit = new AtomicBoolean();
+    private final AtomicBoolean waitForUser = new AtomicBoolean();
     private final CamelCatalog camelCatalog = new DefaultCamelCatalog(true);
 
     public CamelHistoryAction(CamelJBangMain main) {
         super(main);
     }
 
+    @Override
+    public Integer doCall() throws Exception {
+        if (it) {
+            // cannot run in watch mode for interactive
+            watch = false;
+        }
+        return super.doCall();
+    }
+
     @Override
     public Integer doWatchCall() throws Exception {
         if (name == null) {
@@ -83,6 +141,13 @@ public class CamelHistoryAction extends ActionWatchCommand {
         List<List<Row>> pids = loadRows();
 
         if (!pids.isEmpty()) {
+            if (it) {
+                if (pids.size() > 1) {
+                    printer().println("Interactive mode only operate on a 
single Camel application");
+                    return 0;
+                }
+                return doInteractiveCall(pids.get(0));
+            }
             if (watch) {
                 clearScreen();
             }
@@ -137,6 +202,345 @@ public class CamelHistoryAction extends 
ActionWatchCommand {
         return 0;
     }
 
+    private void doRead(Console c, AtomicBoolean quit, AtomicInteger index) {
+        do {
+            String line = c.readLine();
+            if (line != null) {
+                line = line.trim();
+                if ("q".equalsIgnoreCase(line) || 
"quit".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)) {
+                    quit.set(true);
+                }
+                // user have pressed ENTER so continue
+                index.incrementAndGet();
+                waitForUser.set(false);
+            }
+        } while (!quit.get());
+    }
+
+    private Integer doInteractiveCall(List<Row> rows) throws Exception {
+        // read CLI input from user
+        final AtomicInteger index = new AtomicInteger();
+        final Console c = System.console();
+        Thread t2 = new Thread(() -> doRead(c, quit, index), "ReadCommand");
+        t2.start();
+
+        tableHelper = new MessageTableHelper();
+        tableHelper.setPretty(pretty);
+        tableHelper.setLoggingColor(loggingColor);
+        tableHelper.setShowExchangeProperties(showExchangeProperties);
+        tableHelper.setShowExchangeVariables(showExchangeVariables);
+
+        do {
+            if (!waitForUser.get()) {
+                clearScreen();
+                Row first = rows.get(0);
+                String ago = TimeUtils.printSince(first.timestamp);
+                Row last = rows.get(rows.size() - 1);
+                String status = last.failed ? "failed" : "success";
+                String s = String.format("Message History of last completed 
(id:%s status:%s ago:%s pid:%d name:%s)",
+                        first.exchangeId, status, ago, first.pid, first.name);
+                printer().println(s);
+
+                int i = index.get();
+                if (i < rows.size()) {
+                    Row r = rows.get(i);
+                    printSourceAndHistory(r);
+                    printCurrentRow(r);
+                }
+                waitForUser.set(true);
+            }
+        } while (!quit.get() || waitForUser.get());
+
+        return 0;
+    }
+
+    private static int lineIsNumber(String line) {
+        try {
+            return Integer.parseInt(line);
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    private String getDataAsTable(Row r) {
+        return tableHelper.getDataAsTable(r.exchangeId, r.exchangePattern, 
null, r.endpoint, r.endpointService,
+                r.message,
+                r.exception);
+    }
+
+    private void printSourceAndHistory(Row row) {
+        List<Panel> panel = new ArrayList<>();
+        if (!row.code.isEmpty()) {
+            String loc = StringHelper.beforeLast(row.location, ":", 
row.location);
+            if (loc != null && loc.length() < 72) {
+                loc = loc + " ".repeat(72 - loc.length());
+            } else {
+                loc = "";
+            }
+            panel.add(Panel.withCode("Source: " + loc).andHistory("History"));
+            panel.add(Panel.withCode("-".repeat(80))
+                    .andHistory("-".repeat(90)));
+
+            for (int i = 0; i < row.code.size(); i++) {
+                Code code = row.code.get(i);
+                String c = Jsoner.unescape(code.code);
+                String arrow = "    ";
+                if (code.match) {
+                    if (row.first) {
+                        arrow = "*-->";
+                    } else if (row.last) {
+                        arrow = "<--*";
+                    } else {
+                        arrow = "--->";
+                    }
+                }
+                String msg = String.format("%4d: %s %s", code.line, arrow, c);
+                if (msg.length() > 80) {
+                    msg = msg.substring(0, 80);
+                }
+                int length = msg.length();
+                if (loggingColor && code.match) {
+                    Ansi.Color col = Ansi.Color.BLUE;
+                    Ansi.Attribute it = Ansi.Attribute.INTENSITY_BOLD;
+                    if (row.failed && row.last) {
+                        col = Ansi.Color.RED;
+                    } else if (row.last) {
+                        col = Ansi.Color.GREEN;
+                    }
+                    // need to fill out entire line, so fill in spaces
+                    if (length < 80) {
+                        String extra = " ".repeat(80 - length);
+                        msg = msg + extra;
+                        length = 80;
+                    }
+                    msg = Ansi.ansi().bg(col).a(it).a(msg).reset().toString();
+                } else {
+                    // need to fill out entire line, so fill in spaces
+                    if (length < 80) {
+                        String extra = " ".repeat(80 - length);
+                        msg = msg + extra;
+                        length = 80;
+                    }
+                }
+                panel.add(Panel.withCode(msg, length));
+            }
+            for (int i = row.code.size(); i < 11; i++) {
+                // empty lines so source code has same height
+                panel.add(Panel.withCode(" ".repeat(80)));
+            }
+        }
+
+        if (!row.history.isEmpty()) {
+            if (row.history.size() > (panel.size() - 4)) {
+                // cut to only what we can display
+                int pos = row.history.size() - (panel.size() - 4);
+                if (row.history.size() > pos) {
+                    row.history = row.history.subList(pos, row.history.size());
+                }
+            }
+            for (int i = 2; panel.size() > 2 && i < 11; i++) {
+                Panel p = panel.get(i);
+                if (row.history.size() > (i - 2)) {
+                    History h = row.history.get(i - 2);
+                    boolean top = h == row.history.get(row.history.size() - 1);
+
+                    String ids;
+                    if (source) {
+                        ids = locationAndLine(h.location, h.line);
+                    } else {
+                        ids = h.routeId + "/" + h.nodeId;
+                    }
+                    if (ids.length() > 30) {
+                        ids = ids.substring(ids.length() - 30);
+                    }
+
+                    ids = String.format("%-30.30s", ids);
+                    if (loggingColor) {
+                        ids = Ansi.ansi().fgCyan().a(ids).reset().toString();
+                    }
+                    long e = i == 2 ? 0 : h.elapsed; // the pseudo from should 
have 0 as elapsed
+                    String elapsed = "(" + e + "ms)";
+
+                    String c = "";
+                    if (source && h.code != null) {
+                        c = Jsoner.unescape(h.code);
+                        c = c.trim();
+                    } else if (h.nodeLabel != null) {
+                        c = Jsoner.escape(h.nodeLabel);
+                        c = c.trim();
+                    }
+                    // pad with level
+                    String pad = StringHelper.padString(h.level);
+                    c = pad + c;
+
+                    String fids = String.format("%-30.30s", ids);
+                    String msg;
+                    if (top && !row.last) {
+                        msg = String.format("%2d %10.10s %s %4d:   %s", 
h.index, "--->", fids, h.line, c);
+                    } else {
+                        msg = String.format("%2d %10.10s %s %4d:   %s", 
h.index, elapsed, fids, h.line, c);
+                    }
+                    int len = msg.length();
+                    if (loggingColor) {
+                        fids = String.format("%-30.30s", ids);
+                        fids = Ansi.ansi().fgCyan().a(fids).reset().toString();
+                        if (top && !row.last) {
+                            msg = String.format("%2d %10.10s %s %4d:   %s", 
h.index, "--->", fids, h.line, c);
+                        } else {
+                            msg = String.format("%2d %10.10s %s %4d:   %s", 
h.index, elapsed, fids, h.line, c);
+                        }
+                    }
+
+                    p.history = msg;
+                    p.historyLength = len;
+                }
+            }
+        }
+        // the ascii-table does not work well with color cells 
(https://github.com/freva/ascii-table/issues/26)
+        for (Panel p : panel) {
+            String c = p.code;
+            String h = p.history;
+            int len = p.historyLength;
+            if (len > 90) {
+                h = h.substring(0, 90);
+            }
+            String line = c + "    " + h;
+            printer().println(line);
+        }
+    }
+
+    private void printCurrentRow(Row row) {
+        if (timestamp) {
+            String ts;
+            if (ago) {
+                ts = String.format("%12s", TimeUtils.printSince(row.timestamp) 
+ " ago");
+            } else {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 
HH:mm:ss.SSS");
+                ts = sdf.format(new Date(row.timestamp));
+            }
+            if (loggingColor) {
+                
AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(ts).reset());
+            } else {
+                printer().print(ts);
+            }
+            printer().print("  ");
+        }
+        // pid
+        String p = String.format("%5.5s", row.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 {
+            printer().print(p);
+            printer().print(" --- ");
+        }
+        // thread name
+        String tn = row.threadName;
+        if (tn.length() > 25) {
+            tn = tn.substring(tn.length() - 25);
+        }
+        tn = String.format("[%25.25s]", tn);
+        if (loggingColor) {
+            
AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(tn).reset());
+        } else {
+            printer().print(tn);
+        }
+        printer().print(" ");
+        // node ids or source location
+        String ids;
+        if (source) {
+            ids = locationAndLine(row.location, -1);
+        } else {
+            ids = row.routeId + "/" + getId(row);
+        }
+        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 {
+            printer().print(ids);
+        }
+        printer().print(" : ");
+        // uuid
+        String u = String.format("%5.5s", row.uid);
+        if (loggingColor) {
+            AnsiConsole.out().print(Ansi.ansi().fgMagenta().a(u).reset());
+        } else {
+            printer().print(u);
+        }
+        printer().print(" - ");
+        // status
+        printer().print(getStatus(row));
+        // elapsed
+        String e = getElapsed(row);
+        if (e != null) {
+            if (loggingColor) {
+                AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(" (" + 
e + ")").reset());
+            } else {
+                printer().print("(" + e + ")");
+            }
+        }
+        printer().println();
+        printer().println(getDataAsTable(row));
+        printer().println();
+    }
+
+    private String getElapsed(Row r) {
+        if (!r.first) {
+            return TimeUtils.printDuration(r.elapsed, true);
+        }
+        return null;
+    }
+
+    private String getStatus(Row r) {
+        boolean remote = r.endpoint != null && 
r.endpoint.getBooleanOrDefault("remote", false);
+
+        if (r.first) {
+            String s = "Created";
+            if (loggingColor) {
+                return 
Ansi.ansi().fg(Ansi.Color.GREEN).a(s).reset().toString();
+            } else {
+                return s;
+            }
+        } else if (r.last) {
+            String done = r.exception != null ? "Completed (exception)" : 
"Completed (success)";
+            if (loggingColor) {
+                return Ansi.ansi().fg(r.failed ? Ansi.Color.RED : 
Ansi.Color.GREEN).a(done).reset().toString();
+            } else {
+                return done;
+            }
+        }
+        if (!r.done) {
+            if (loggingColor) {
+                return 
Ansi.ansi().fg(Ansi.Color.BLUE).a("Breakpoint").reset().toString();
+            } else {
+                return "Breakpoint";
+            }
+        } else if (r.failed) {
+            String fail = r.exception != null ? "Exception" : "Failed";
+            if (loggingColor) {
+                return 
Ansi.ansi().fg(Ansi.Color.RED).a(fail).reset().toString();
+            } else {
+                return fail;
+            }
+        } else {
+            String s = remote ? "Sent" : "Processed";
+            if (loggingColor) {
+                return 
Ansi.ansi().fg(Ansi.Color.GREEN).a(s).reset().toString();
+            } else {
+                return s;
+            }
+        }
+    }
+
+    private static String locationAndLine(String loc, int line) {
+        // shorten path as there is no much space
+        loc = FileUtil.stripPath(loc);
+        return line == -1 ? loc : loc + ":" + line;
+    }
+
     private boolean filterDepth(Row r) {
         if (depth >= 9) {
             return true;
@@ -241,8 +645,6 @@ public class CamelHistoryAction extends ActionWatchCommand {
                         answer.add(rows);
                     }
                 } catch (Exception e) {
-                    // TODO: remove me
-                    e.printStackTrace();
                     // ignore
                 } finally {
                     IOHelper.close(reader);
@@ -284,6 +686,10 @@ public class CamelHistoryAction extends ActionWatchCommand 
{
                         row.nodeLabel = URISupport.sanitizeUri(row.nodeLabel);
                     }
                     row.nodeLevel = jo.getIntegerOrDefault("nodeLevel", 0);
+                    if ("aggregate".equals(jo.getString("nodeShortName"))) {
+                        row.aggregate = new JsonObject();
+                        row.aggregate.put("nodeLabel", 
jo.getString("nodeLabel"));
+                    }
                     String uri = jo.getString("endpointUri");
                     if (uri != null) {
                         row.endpoint = new JsonObject();
@@ -313,6 +719,31 @@ public class CamelHistoryAction extends ActionWatchCommand 
{
                     // we should exchangeId/pattern elsewhere
                     row.message.remove("exchangeId");
                     row.message.remove("exchangePattern");
+                    if (!showExchangeVariables) {
+                        row.message.remove("exchangeVariables");
+                    }
+                    if (!showExchangeProperties) {
+                        row.message.remove("exchangeProperties");
+                    }
+                    if (!showHeaders) {
+                        row.message.remove("headers");
+                    }
+                    if (!showBody) {
+                        row.message.remove("body");
+                    }
+                    if (!showException) {
+                        row.exception = null;
+                    }
+                    List<JsonObject> codeLines = jo.getCollection("code");
+                    if (codeLines != null) {
+                        for (JsonObject cl : codeLines) {
+                            Code code = new Code();
+                            code.line = cl.getInteger("line");
+                            code.match = cl.getBooleanOrDefault("match", 
false);
+                            code.code = cl.getString("code");
+                            row.code.add(code);
+                        }
+                    }
                     rows.add(row);
                 }
 
@@ -385,6 +816,27 @@ public class CamelHistoryAction extends ActionWatchCommand 
{
                 }
             }
         }
+        for (int i = 1; i < rows.size() - 1; i++) {
+            Row cur = rows.get(i);
+
+            cur.history = new ArrayList<>();
+            for (int j = 0; j < i; j++) {
+                Row r = rows.get(j);
+                History h = new History();
+                h.index = j;
+                h.nodeId = r.nodeId;
+                h.routeId = r.routeId;
+                h.nodeShortName = r.nodeShortName;
+                h.nodeLabel = r.nodeLabel;
+                h.location = r.location;
+                h.elapsed = r.elapsed;
+                h.level = r.nodeLevel;
+                // TODO
+                h.code = "some code here";
+                h.line = 123;
+                cur.history.add(h);
+             }
+        }
     }
 
     private Map<String, String> extractComponentModel(String uri, Row r) {
@@ -470,11 +922,62 @@ public class CamelHistoryAction extends 
ActionWatchCommand {
         long elapsed;
         boolean done;
         boolean failed;
+        JsonObject aggregate;
         JsonObject endpoint;
         JsonObject endpointService;
         JsonObject message;
         JsonObject exception;
         String summary;
+        List<Code> code = new ArrayList<>();
+        List<History> history = new ArrayList<>();
+    }
+
+    private static class History {
+        int index;
+        String routeId;
+        String nodeId;
+        String nodeShortName;
+        String nodeLabel;
+        int level;
+        long elapsed;
+        String location;
+        int line;
+        String code;
+    }
+
+    private static class Code {
+        int line;
+        String code;
+        boolean match;
+    }
+
+    private static class Panel {
+        String code = "";
+        String history = "";
+        int codeLength;
+        int historyLength;
+
+        static Panel withCode(String code) {
+            return withCode(code, code.length());
+        }
+
+        static Panel withCode(String code, int length) {
+            Panel p = new Panel();
+            p.code = code;
+            p.codeLength = length;
+            return p;
+        }
+
+        Panel andHistory(String history) {
+            return andHistory(history, history.length());
+        }
+
+        Panel andHistory(String history, int length) {
+            this.history = history;
+            this.historyLength = length;
+            return this;
+        }
+
     }
 
 }

Reply via email to