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 6d02bb76f153 Rebrand Camel CLI
6d02bb76f153 is described below
commit 6d02bb76f153cdfd53579431535de5f727980d10
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jun 5 19:00:32 2026 +0200
Rebrand Camel CLI
---
.../camel/cli/connector/LocalCliConnector.java | 8 +-
.../dsl/jbang/core/commands/tui/HistoryTab.java | 218 +++++++++++++++++----
.../dsl/jbang/core/commands/tui/TraceEntry.java | 1 +
3 files changed, 184 insertions(+), 43 deletions(-)
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 dfe522aa3af9..ae024871ebd4 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
@@ -98,7 +98,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * CLI Connector for local management of Camel integrations from Camel JBang.
+ * CLI Connector for local management of Camel integrations from Camel CLI.
*/
public class LocalCliConnector extends ServiceSupport implements CliConnector,
CamelContextAware {
@@ -203,9 +203,9 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
debugFile = createLockFile(lockFile.getName() + "-debug.json");
receiveFile = createLockFile(lockFile.getName() + "-receive.json");
scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0,
delay, TimeUnit.MILLISECONDS);
- LOG.info("Camel JBang CLI enabled");
+ LOG.info("Camel CLI connector enabled");
} else {
- LOG.warn("Cannot create PID file: {}. This integration cannot be
managed by Camel JBang CLI.", getPid());
+ LOG.warn("Cannot create PID file: {}. This integration cannot be
managed by Camel CLI connector.", getPid());
}
}
@@ -241,7 +241,7 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
terminateExecutor.submit(new Runnable() {
@Override
public void run() {
- LOG.info("Camel JBang terminating JVM");
+ LOG.info("Camel CLI connector terminating JVM");
try {
// if we are debugging then detach before stopping camel
BacklogDebugger debugger =
camelContext.hasService(BacklogDebugger.class);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
index 3a9c5361eaf5..ba2d905209bc 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
@@ -18,10 +18,14 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import dev.tamboui.layout.Alignment;
@@ -59,6 +63,16 @@ class HistoryTab implements MonitorTab {
private static final String[] TRACE_SORT_COLUMNS = { "time", "route",
"elapsed", "exchange" };
+ private static final Comparator<TraceEntry> UID_COMPARATOR = (a, b) -> {
+ String ua = a.uid != null ? a.uid : "";
+ String ub = b.uid != null ? b.uid : "";
+ try {
+ return Long.compare(Long.parseLong(ua), Long.parseLong(ub));
+ } catch (NumberFormatException e) {
+ return ua.compareTo(ub);
+ }
+ };
+
private final MonitorContext ctx;
private final AtomicReference<List<TraceEntry>> traces;
private final Map<String, Long> traceFilePositions;
@@ -204,7 +218,7 @@ class HistoryTab implements MonitorTab {
if (ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)) {
if (tracerActive && traceDetailView) {
if (showWaterfall) {
- List<TraceEntry> steps =
getTraceSteps(traceSelectedExchangeId);
+ List<TraceEntry> steps =
getTraceStepsDepthFirst(traceSelectedExchangeId);
for (int i = 0; i < 10; i++) {
traceStepTableState.selectNext(steps.size());
}
@@ -397,7 +411,7 @@ class HistoryTab implements MonitorTab {
public void navigateDown() {
if (!traces.get().isEmpty()) {
if (traceDetailView) {
- List<TraceEntry> steps =
getTraceSteps(traceSelectedExchangeId);
+ List<TraceEntry> steps =
getTraceStepsDepthFirst(traceSelectedExchangeId);
traceStepTableState.selectNext(steps.size());
traceDetailScroll = 0;
} else {
@@ -592,7 +606,7 @@ class HistoryTab implements MonitorTab {
return;
}
}
- List<TraceEntry> steps = getTraceSteps(exchangeId);
+ List<TraceEntry> steps = getTraceStepsDepthFirst(exchangeId);
if (steps.isEmpty()) {
diagram.endLoad();
return;
@@ -655,26 +669,29 @@ class HistoryTab implements MonitorTab {
private void renderTraceExchangeList(Frame frame, Rect area) {
List<String> exchangeIds = getTraceExchangeIds();
- List<TraceEntry> current = traces.get();
+ // Compute child exchange IDs that will be inlined into a parent's
depth-first view
+ Set<String> childExchangeIds = computeAllChildExchangeIds(exchangeIds);
+
record ExchangeSummary(String exchangeId, String timestamp, long
epochMs, String routeId,
String status, long elapsed, int steps) {
}
List<ExchangeSummary> summaries = new ArrayList<>();
for (String exchangeId : exchangeIds) {
+ if (childExchangeIds.contains(exchangeId)) {
+ continue;
+ }
+ List<TraceEntry> depthFirstSteps =
getTraceStepsDepthFirst(exchangeId);
TraceEntry first = null;
TraceEntry lastEntry = null;
TraceEntry latestEntry = null;
- int stepCount = 0;
- for (TraceEntry e : current) {
- if (exchangeId.equals(e.exchangeId)) {
- if (first == null) {
- first = e;
- }
- latestEntry = e;
- if (e.last) {
- lastEntry = e;
- }
- stepCount++;
+ int stepCount = depthFirstSteps.size();
+ for (TraceEntry e : depthFirstSteps) {
+ if (first == null) {
+ first = e;
+ }
+ latestEntry = e;
+ if (e.last && exchangeId.equals(e.exchangeId)) {
+ lastEntry = e;
}
}
if (first != null) {
@@ -752,7 +769,7 @@ class HistoryTab implements MonitorTab {
}
private void renderTraceExchangeDetail(Frame frame, Rect area) {
- List<TraceEntry> steps = getTraceSteps(traceSelectedExchangeId);
+ List<TraceEntry> steps =
getTraceStepsDepthFirst(traceSelectedExchangeId);
List<Rect> chunks = Layout.vertical()
.constraints(Constraint.length(10), Constraint.length(1),
Constraint.fill())
@@ -763,7 +780,7 @@ class HistoryTab implements MonitorTab {
for (int i = 0; i < steps.size(); i++) {
TraceEntry entry = steps.get(i);
String desc = showDescription ? descMap.get(entry.routeId) : null;
- rows.add(buildStepRow(i + 1,
+ rows.add(buildStepRow(i + 1, entry.inlineDepth,
entry.direction, entry.first, entry.last, entry.failed,
entry.timestamp, entry.routeId, entry.nodeId,
entry.processor, desc, entry.elapsed));
}
@@ -1017,7 +1034,7 @@ class HistoryTab implements MonitorTab {
for (int i = 0; i < current.size(); i++) {
HistoryEntry entry = current.get(i);
String desc = showDescription ? descMap.get(entry.routeId) : null;
- rows.add(buildStepRow(i + 1,
+ rows.add(buildStepRow(i + 1, 0,
entry.direction, entry.first, entry.last, entry.failed,
entry.timestamp, entry.routeId, entry.nodeId,
entry.processor, desc, entry.elapsed));
}
@@ -1104,24 +1121,146 @@ class HistoryTab implements MonitorTab {
return new ArrayList<>(seen);
}
- private List<TraceEntry> getTraceSteps(String exchangeId) {
- List<TraceEntry> current = traces.get();
- List<TraceEntry> steps = new ArrayList<>();
- for (TraceEntry e : current) {
- if (exchangeId != null && exchangeId.equals(e.exchangeId)) {
- steps.add(e);
+ private List<TraceEntry> getTraceStepsDepthFirst(String exchangeId) {
+ List<TraceEntry> allTraces = traces.get();
+ if (allTraces.isEmpty() || exchangeId == null) {
+ return Collections.emptyList();
+ }
+
+ // Group by exchangeId, each sorted by uid
+ Map<String, List<TraceEntry>> byExchange = new LinkedHashMap<>();
+ for (TraceEntry e : allTraces) {
+ if (e.exchangeId != null) {
+ byExchange.computeIfAbsent(e.exchangeId, k -> new
ArrayList<>()).add(e);
}
}
- steps.sort((a, b) -> {
- String ua = a.uid != null ? a.uid : "";
- String ub = b.uid != null ? b.uid : "";
- try {
- return Long.compare(Long.parseLong(ua), Long.parseLong(ub));
- } catch (NumberFormatException e) {
- return ua.compareTo(ub);
+ byExchange.values().forEach(list -> list.sort(UID_COMPARATOR));
+
+ // Build from-endpoint → exchangeIds index (consumer endpoint of each
exchange)
+ Map<String, List<String>> fromIndex = new LinkedHashMap<>();
+ for (var entry : byExchange.entrySet()) {
+ List<TraceEntry> steps = entry.getValue();
+ if (!steps.isEmpty() && steps.get(0).first) {
+ String ep = extractFromEndpoint(steps.get(0));
+ if (ep != null) {
+ fromIndex.computeIfAbsent(ep, k -> new
ArrayList<>()).add(entry.getKey());
+ }
}
- });
- return steps;
+ }
+
+ // Build multicast/splitter child index: routeId → [child exchangeIds]
+ // A child exchange has no first=true entry and its first step is a
to[...] node
+ Map<String, List<String>> branchChildren = new LinkedHashMap<>();
+ for (var entry : byExchange.entrySet()) {
+ List<TraceEntry> steps = entry.getValue();
+ if (!steps.isEmpty() && !steps.get(0).first) {
+ TraceEntry firstStep = steps.get(0);
+ if (firstStep.routeId != null && extractToEndpoint(firstStep)
!= null) {
+ branchChildren.computeIfAbsent(firstStep.routeId, k -> new
ArrayList<>()).add(entry.getKey());
+ }
+ }
+ }
+
+ // Walk depth-first
+ List<TraceEntry> result = new ArrayList<>();
+ Set<String> visited = new HashSet<>();
+ inlineExchange(exchangeId, byExchange, fromIndex, branchChildren,
visited, result, 0);
+ return result;
+ }
+
+ private void inlineExchange(
+ String exchangeId, Map<String, List<TraceEntry>> byExchange,
+ Map<String, List<String>> fromIndex, Map<String, List<String>>
branchChildren,
+ Set<String> visited, List<TraceEntry> result, int depth) {
+ if (!visited.add(exchangeId)) {
+ return;
+ }
+ List<TraceEntry> steps = byExchange.get(exchangeId);
+ if (steps == null) {
+ return;
+ }
+ for (TraceEntry step : steps) {
+ step.inlineDepth = depth;
+ result.add(step);
+
+ // After a multicast/splitter step, inline branch child exchanges
+ if (isMulticastNode(step)) {
+ List<String> children = branchChildren.get(step.routeId);
+ if (children != null) {
+ for (String childId : children) {
+ if (!visited.contains(childId)) {
+ inlineExchange(childId, byExchange, fromIndex,
branchChildren, visited, result, depth + 1);
+ }
+ }
+ }
+ }
+
+ // After a to[X] step, inline consumer exchanges matching endpoint
X
+ String targetEndpoint = extractToEndpoint(step);
+ if (targetEndpoint != null) {
+ List<String> children = fromIndex.get(targetEndpoint);
+ if (children != null) {
+ for (String childId : children) {
+ if (!visited.contains(childId)) {
+ inlineExchange(childId, byExchange, fromIndex,
branchChildren, visited, result, depth + 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isMulticastNode(TraceEntry entry) {
+ if (entry.nodeShortName != null) {
+ return switch (entry.nodeShortName) {
+ case "multicast", "recipientList", "split", "routingSlip",
"enrich", "pollEnrich",
+ "wireTap", "dynamicRouter" ->
+ true;
+ default -> false;
+ };
+ }
+ return false;
+ }
+
+ private static String extractFromEndpoint(TraceEntry entry) {
+ if (entry.nodeLabel != null) {
+ return normalizeEndpoint(entry.nodeLabel);
+ }
+ return null;
+ }
+
+ private static String extractToEndpoint(TraceEntry entry) {
+ if (entry.nodeLabel != null && entry.nodeLabel.startsWith("to[")) {
+ int end = entry.nodeLabel.indexOf(']');
+ if (end > 3) {
+ return normalizeEndpoint(entry.nodeLabel.substring(3, end));
+ }
+ }
+ return null;
+ }
+
+ private static String normalizeEndpoint(String endpoint) {
+ int q = endpoint.indexOf('?');
+ if (q > 0) {
+ endpoint = endpoint.substring(0, q);
+ }
+ return endpoint.replace("://", ":");
+ }
+
+ private Set<String> computeAllChildExchangeIds(List<String> exchangeIds) {
+ Set<String> allChildren = new LinkedHashSet<>();
+ for (String exchangeId : exchangeIds) {
+ if (allChildren.contains(exchangeId)) {
+ continue;
+ }
+ List<TraceEntry> depthFirst = getTraceStepsDepthFirst(exchangeId);
+ for (TraceEntry e : depthFirst) {
+ if (!exchangeId.equals(e.exchangeId)) {
+ allChildren.add(e.exchangeId);
+ }
+ }
+ }
+ return allChildren;
}
private String traceSortLabel(String label, String column) {
@@ -1133,7 +1272,7 @@ class HistoryTab implements MonitorTab {
}
private static Row buildStepRow(
- int stepNumber,
+ int stepNumber, int inlineDepth,
String direction, boolean first, boolean last, boolean failed,
String timestamp, String routeId, String nodeId, String processor,
String description, long elapsed) {
@@ -1145,13 +1284,14 @@ class HistoryTab implements MonitorTab {
}
String elapsedStr = elapsed >= 0 ? elapsed + "ms" : "";
String display = description != null ? description : (processor !=
null ? processor : "");
+ String indent = inlineDepth > 0 ? " ".repeat(inlineDepth) : "";
return Row.from(
rightCell(String.valueOf(stepNumber), 3),
Cell.from(Span.styled(direction, dirStyle)),
Cell.from(timestamp != null ? truncate(timestamp, 12) : ""),
- Cell.from(Span.styled(routeId != null ? truncate(routeId, 25)
: "", Style.EMPTY.fg(Color.CYAN))),
- Cell.from(nodeId != null ? truncate(nodeId, 15) : ""),
- Cell.from(display),
+ Cell.from(Span.styled(indent + (routeId != null ?
truncate(routeId, 25) : ""), Style.EMPTY.fg(Color.CYAN))),
+ Cell.from(indent + (nodeId != null ? truncate(nodeId, 15) :
"")),
+ Cell.from(indent + display),
rightCell(elapsedStr, 10));
}
@@ -1438,7 +1578,7 @@ class HistoryTab implements MonitorTab {
boolean tracerActive = !traces.get().isEmpty();
if (tracerActive) {
if (traceDetailView) {
- List<TraceEntry> steps =
getTraceSteps(traceSelectedExchangeId);
+ List<TraceEntry> steps =
getTraceStepsDepthFirst(traceSelectedExchangeId);
if (steps.isEmpty()) {
return null;
}
@@ -1671,7 +1811,7 @@ class HistoryTab implements MonitorTab {
if (tracerActive) {
if (traceDetailView && traceSelectedExchangeId != null) {
result.put("tab", "Trace Steps");
- List<TraceEntry> steps =
getTraceSteps(traceSelectedExchangeId);
+ List<TraceEntry> steps =
getTraceStepsDepthFirst(traceSelectedExchangeId);
JsonArray rows = new JsonArray();
for (TraceEntry t : steps) {
JsonObject row = new JsonObject();
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
index 3c88ec2a3950..a566178d6b44 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
@@ -50,4 +50,5 @@ class TraceEntry {
Map<String, Object> exchangeVariables;
Map<String, String> exchangeVariableTypes;
String exception;
+ int inlineDepth;
}