This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch tui-infra-CAMEL-23569 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8cf60713390b834ad6be9714f30bc803c2fcb0b8 Author: Claus Ibsen <[email protected]> AuthorDate: Wed May 20 16:31:28 2026 +0200 CAMEL-23569: camel-tui: Add support for camel-test-infra services Add infrastructure service management to the TUI monitor including: - Discover running infra services by scanning ~/.camel/infra-*.json files - Show infra services in a separate table below integrations in the overview - Display connection details (host, port, credentials) in the info panel - Support infra service logs in the Log tab - Add stop/kill (x/X keys) for both infra services and integrations Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../dsl/jbang/core/commands/tui/CamelMonitor.java | 440 +++++++++++++++++++-- 1 file changed, 401 insertions(+), 39 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java index 805ee1327737..c5f084fad0f5 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java @@ -36,6 +36,7 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -163,8 +164,12 @@ public class CamelMonitor extends CamelCommand { // State private final AtomicReference<List<IntegrationInfo>> data = new AtomicReference<>(Collections.emptyList()); + private final AtomicReference<List<InfraInfo>> infraData = new AtomicReference<>(Collections.emptyList()); private final Map<String, VanishingInfo> vanishing = new ConcurrentHashMap<>(); + private final Map<String, VanishingInfraInfo> vanishingInfra = new ConcurrentHashMap<>(); private final TableState overviewTableState = new TableState(); + private final TableState infraTableState = new TableState(); + private boolean infraTableFocused; private final TableState routeTableState = new TableState(); private final TableState consumerTableState = new TableState(); private final TableState healthTableState = new TableState(); @@ -439,6 +444,11 @@ public class CamelMonitor extends CamelCommand { tabsState.select(TAB_OVERVIEW); return true; } + if (infraTableFocused) { + infraTableFocused = false; + syncSelectedPidFromOverview(); + return true; + } if (selectedPid != null) { selectedPid = null; return true; @@ -625,8 +635,8 @@ public class CamelMonitor extends CamelCommand { chartMode = (chartMode + 1) % 3; return true; } - // Overview tab: start/stop all routes for selected integration - if (tab == TAB_OVERVIEW && ke.isChar('p') && selectedPid != null) { + // Overview tab: start/stop all routes for selected integration (not infra) + if (tab == TAB_OVERVIEW && ke.isChar('p') && selectedPid != null && !infraTableFocused) { IntegrationInfo selInfo = findSelectedIntegration(); if (selInfo != null) { String cmd = selInfo.routeStarted > 0 ? "stop" : "start"; @@ -634,6 +644,16 @@ public class CamelMonitor extends CamelCommand { } return true; } + // Overview tab: stop process (SIGTERM) for selected integration or infra + if (tab == TAB_OVERVIEW && ke.isChar('x') && selectedPid != null) { + stopSelectedProcess(false); + return true; + } + // Overview tab: kill process (SIGKILL) for selected integration or infra + if (tab == TAB_OVERVIEW && ke.isChar('X') && selectedPid != null) { + stopSelectedProcess(true); + return true; + } // Consumers tab: sort if (tab == TAB_CONSUMERS && ke.isChar('s')) { @@ -1038,12 +1058,20 @@ public class CamelMonitor extends CamelCommand { if (selectedPid != null) { return; } - List<IntegrationInfo> infos = sortedOverviewInfos(); - Integer sel = overviewTableState.selected(); - if (sel != null && sel >= 0 && sel < infos.size()) { - selectedPid = infos.get(sel).pid; - } else if (infos.size() == 1) { - selectedPid = infos.get(0).pid; + if (infraTableFocused) { + List<InfraInfo> infras = infraData.get(); + Integer sel = infraTableState.selected(); + if (sel != null && sel >= 0 && sel < infras.size()) { + selectedPid = infras.get(sel).pid; + } + } else { + List<IntegrationInfo> infos = sortedOverviewInfos(); + Integer sel = overviewTableState.selected(); + if (sel != null && sel >= 0 && sel < infos.size()) { + selectedPid = infos.get(sel).pid; + } else if (infos.size() == 1) { + selectedPid = infos.get(0).pid; + } } } @@ -1062,6 +1090,19 @@ public class CamelMonitor extends CamelCommand { } } + private void syncSelectedPidFromInfra() { + List<InfraInfo> infras = infraData.get(); + Integer sel = infraTableState.selected(); + String newPid = null; + if (sel != null && sel >= 0 && sel < infras.size()) { + newPid = infras.get(sel).pid; + } + if (newPid != null && !newPid.equals(selectedPid)) { + selectedPid = newPid; + resetIntegrationTabState(); + } + } + // NOTE: When adding a new tab, reset its view state here too so switching integrations // on the Overview always shows a clean slate for the newly selected integration. private void resetIntegrationTabState() { @@ -1105,8 +1146,23 @@ public class CamelMonitor extends CamelCommand { private void navigateUp() { switch (tabsState.selected()) { case TAB_OVERVIEW -> { - overviewTableState.selectPrevious(); - syncSelectedPidFromOverview(); + if (infraTableFocused) { + Integer sel = infraTableState.selected(); + if (sel != null && sel <= 0) { + infraTableFocused = false; + List<IntegrationInfo> intInfos = sortedOverviewInfos(); + if (!intInfos.isEmpty()) { + overviewTableState.select(intInfos.size() - 1); + } + syncSelectedPidFromOverview(); + } else { + infraTableState.selectPrevious(); + syncSelectedPidFromInfra(); + } + } else { + overviewTableState.selectPrevious(); + syncSelectedPidFromOverview(); + } } case TAB_ROUTES -> routeTableState.selectPrevious(); case TAB_CIRCUIT_BREAKER -> cbTableState.selectPrevious(); @@ -1132,11 +1188,24 @@ public class CamelMonitor extends CamelCommand { } private void navigateDown() { - List<IntegrationInfo> infos = data.get().stream().filter(i -> !i.vanishing).toList(); switch (tabsState.selected()) { case TAB_OVERVIEW -> { - overviewTableState.selectNext(sortedOverviewInfos().size()); - syncSelectedPidFromOverview(); + if (infraTableFocused) { + List<InfraInfo> infraInfos = infraData.get(); + infraTableState.selectNext(infraInfos.size()); + syncSelectedPidFromInfra(); + } else { + List<IntegrationInfo> overviewInfos = sortedOverviewInfos(); + Integer sel = overviewTableState.selected(); + if (sel != null && sel >= overviewInfos.size() - 1 && !infraData.get().isEmpty()) { + infraTableFocused = true; + infraTableState.select(0); + syncSelectedPidFromInfra(); + } else { + overviewTableState.selectNext(overviewInfos.size()); + syncSelectedPidFromOverview(); + } + } } case TAB_ROUTES -> { IntegrationInfo info = findSelectedIntegration(); @@ -1204,6 +1273,11 @@ public class CamelMonitor extends CamelCommand { titleSpans.add(Span.styled(camelVersion != null ? "v" + camelVersion : "", Style.EMPTY.fg(Color.GREEN))); titleSpans.add(Span.raw(" ")); titleSpans.add(Span.styled(activeCount + " integration(s)", Style.EMPTY.fg(Color.CYAN))); + long activeInfra = infraData.get().stream().filter(i -> !i.vanishing).count(); + if (activeInfra > 0) { + titleSpans.add(Span.raw(" ")); + titleSpans.add(Span.styled(activeInfra + " infra", Style.EMPTY.fg(Color.MAGENTA))); + } if (selectedPid != null) { titleSpans.add(Span.raw(" ")); titleSpans.add(Span.styled("selected: " + selectedName(), Style.EMPTY.fg(Color.YELLOW))); @@ -1349,9 +1423,10 @@ public class CamelMonitor extends CamelCommand { private void renderOverview(Frame frame, Rect area) { List<IntegrationInfo> infos = sortedOverviewInfos(); + List<InfraInfo> infraInfos = infraData.get(); // Keep the table selection index tracking the same PID across sort changes and data refreshes - if (selectedPid != null) { + if (selectedPid != null && !infraTableFocused) { for (int i = 0; i < infos.size(); i++) { if (selectedPid.equals(infos.get(i).pid)) { overviewTableState.select(i); @@ -1359,17 +1434,31 @@ public class CamelMonitor extends CamelCommand { } } } + if (selectedPid != null && infraTableFocused) { + for (int i = 0; i < infraInfos.size(); i++) { + if (selectedPid.equals(infraInfos.get(i).pid)) { + infraTableState.select(i); + break; + } + } + } - // Split: table (fill) + chart (14 rows: 13 chart + 1 x-axis) if we have data and chart is on + // Split: integrations (fill) + infra table (if present) + chart (14 rows) boolean hasSparkline = chartMode != CHART_OFF && !throughputHistory.isEmpty(); - List<Rect> chunks; + boolean hasInfra = !infraInfos.isEmpty(); + // infra table height: header(1) + borders(2) + rows (capped at 6) + int infraHeight = hasInfra ? Math.min(infraInfos.size(), 6) + 3 : 0; + List<Constraint> constraints = new ArrayList<>(); + constraints.add(Constraint.fill()); + if (hasInfra) { + constraints.add(Constraint.length(infraHeight)); + } if (hasSparkline) { - chunks = Layout.vertical() - .constraints(Constraint.fill(), Constraint.length(14)) - .split(area); - } else { - chunks = List.of(area); + constraints.add(Constraint.length(14)); } + List<Rect> chunks = Layout.vertical() + .constraints(constraints) + .split(area); // Integration table List<Row> rows = new ArrayList<>(); @@ -1439,6 +1528,9 @@ public class CamelMonitor extends CamelCommand { rightCell("INFLIGHT", 8, Style.EMPTY.bold()), Cell.from(Span.styled("SINCE-LAST", Style.EMPTY.bold()))); + Style integrationHighlight = infraTableFocused + ? Style.EMPTY.fg(Color.WHITE).dim() + : Style.EMPTY.fg(Color.WHITE).bold().onBlue(); Table table = Table.builder() .rows(rows) .header(header) @@ -1455,16 +1547,21 @@ public class CamelMonitor extends CamelCommand { Constraint.length(6), Constraint.length(8), Constraint.length(12)) - .highlightStyle(Style.EMPTY.fg(Color.WHITE).bold().onBlue()) + .highlightStyle(integrationHighlight) .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).title(" Integrations ").build()) .build(); frame.renderStatefulWidget(table, chunks.get(0), overviewTableState); + // Infrastructure services table + if (hasInfra) { + renderInfraTable(frame, chunks.get(1), infraInfos); + } + // Split green/red throughput bar chart with Y and X axes if (hasSparkline && chunks.size() > 1) { - Rect chartTotalArea = chunks.get(1); + Rect chartTotalArea = chunks.get(chunks.size() - 1); // Split chart area horizontally: bar chart (fill) + info panel (30 cols) List<Rect> chartHSplit = Layout.horizontal() @@ -1611,6 +1708,13 @@ public class CamelMonitor extends CamelCommand { } private void renderOverviewInfoPanel(Frame frame, Rect area) { + // Check if an infra service is selected — show connection details instead + InfraInfo infraSel = infraTableFocused ? findSelectedInfra() : null; + if (infraSel != null) { + renderInfraInfoPanel(frame, area, infraSel); + return; + } + IntegrationInfo sel = findSelectedIntegration(); // Fall back to the single active integration when nothing is explicitly selected if (sel == null) { @@ -1713,6 +1817,91 @@ public class CamelMonitor extends CamelCommand { frame.renderWidget(Paragraph.builder().text(Text.from(lines)).build(), inner); } + // ---- Infra table (overview sub-section) ---- + + private void renderInfraTable(Frame frame, Rect area, List<InfraInfo> infraInfos) { + List<Row> infraRows = new ArrayList<>(); + for (InfraInfo info : infraInfos) { + if (info.vanishing) { + long elapsed = System.currentTimeMillis() - info.vanishStart; + float fade = 1.0f - Math.min(1.0f, (float) elapsed / VANISH_DURATION_MS); + int gray = (int) (100 * fade); + Style dimStyle = Style.EMPTY.fg(Color.indexed(232 + Math.min(gray / 4, 23))); + infraRows.add(Row.from( + Cell.from(Span.styled(info.pid, dimStyle)), + Cell.from(Span.styled(info.alias, dimStyle)), + Cell.from(Span.styled("✖ Stopped", Style.EMPTY.fg(Color.LIGHT_RED).dim())), + Cell.from(Span.styled("", dimStyle)), + Cell.from(Span.styled("", dimStyle)))); + } else { + Style statusStyle = info.alive ? Style.EMPTY.fg(Color.GREEN) : Style.EMPTY.fg(Color.LIGHT_RED); + String statusText = info.alive ? "Running" : "Stopped"; + String port = objToString(info.properties.get("getPort")); + String host = objToString(info.properties.get("getHost")); + if (host.isEmpty()) { + host = objToString(info.properties.get("getHostname")); + } + infraRows.add(Row.from( + Cell.from(info.pid), + Cell.from(Span.styled(info.alias, Style.EMPTY.fg(Color.MAGENTA))), + Cell.from(Span.styled(statusText, statusStyle)), + Cell.from(port), + Cell.from(host))); + } + } + + Row infraHeader = Row.from( + Cell.from(Span.styled("PID", Style.EMPTY.bold())), + Cell.from(Span.styled("SERVICE", Style.EMPTY.bold())), + Cell.from(Span.styled("STATUS", Style.EMPTY.bold())), + Cell.from(Span.styled("PORT", Style.EMPTY.bold())), + Cell.from(Span.styled("HOST", Style.EMPTY.bold()))); + + Style infraHighlight = infraTableFocused + ? Style.EMPTY.fg(Color.WHITE).bold().onBlue() + : Style.EMPTY.fg(Color.WHITE).dim(); + Table infraTable = Table.builder() + .rows(infraRows) + .header(infraHeader) + .widths( + Constraint.length(8), + Constraint.fill(), + Constraint.length(10), + Constraint.length(8), + Constraint.length(20)) + .highlightStyle(infraHighlight) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) + .block(Block.builder().borderType(BorderType.ROUNDED).title(" Infrastructure ").build()) + .build(); + + frame.renderStatefulWidget(infraTable, area, infraTableState); + } + + private void renderInfraInfoPanel(Frame frame, Rect area, InfraInfo infra) { + Block infoBlock = Block.builder().borderType(BorderType.ROUNDED).build(); + frame.renderWidget(infoBlock, area); + Rect inner = infoBlock.inner(area); + List<Line> lines = new ArrayList<>(); + Style dim = Style.EMPTY.dim(); + lines.add(Line.from( + Span.styled("Service: ", dim), + Span.styled(infra.alias, Style.EMPTY.fg(Color.MAGENTA)))); + lines.add(Line.from(Span.raw(""))); + // Show connection properties with cleaned-up key names + for (Map.Entry<String, Object> e : infra.properties.entrySet()) { + String key = e.getKey(); + // Strip "get" prefix and capitalize + if (key.startsWith("get") && key.length() > 3) { + key = key.substring(3); + } + String value = String.valueOf(e.getValue()); + lines.add(Line.from( + Span.styled(key + ": ", dim), + Span.raw(TuiHelper.truncate(value, inner.width() - key.length() - 2)))); + } + frame.renderWidget(Paragraph.builder().text(Text.from(lines)).build(), inner); + } + // ---- Tab 2: Routes ---- private void renderRoutes(Frame frame, Rect area) { @@ -2855,6 +3044,39 @@ public class CamelMonitor extends CamelCommand { sendRouteCommand(selectedPid, route.routeId, command); } + private void stopSelectedProcess(boolean forceKill) { + if (selectedPid == null) { + return; + } + long pid; + try { + pid = Long.parseLong(selectedPid); + } catch (NumberFormatException e) { + return; + } + if (infraTableFocused) { + // For infra services: delete the JSON file to trigger graceful shutdown + InfraInfo infra = findSelectedInfra(); + if (infra != null) { + Path jsonFile = CommandLineHelper.getCamelDir() + .resolve("infra-" + infra.alias + "-" + infra.pid + ".json"); + PathUtils.deleteFile(jsonFile); + } + if (forceKill) { + ProcessHandle.of(pid).ifPresent(ProcessHandle::destroyForcibly); + } + } else { + // For integrations: signal the process directly + ProcessHandle.of(pid).ifPresent(ph -> { + if (forceKill) { + ph.destroyForcibly(); + } else { + ph.destroy(); + } + }); + } + } + private void sendRouteCommand(String pid, String routeId, String command) { JsonObject root = new JsonObject(); root.put("action", "route"); @@ -3961,7 +4183,11 @@ public class CamelMonitor extends CamelCommand { } private void readNewLogLines(String pid, List<String> newLines) { - Path logFile = CommandLineHelper.getCamelDir().resolve(pid + ".log"); + readNewLogLinesFromFile(pid, pid + ".log", newLines); + } + + private void readNewLogLinesFromFile(String pid, String fileName, List<String> newLines) { + Path logFile = CommandLineHelper.getCamelDir().resolve(fileName); if (!Files.exists(logFile)) { logFilePid = pid; logFilePos = -1; @@ -5099,22 +5325,28 @@ public class CamelMonitor extends CamelCommand { if (tab == TAB_OVERVIEW) { hint(spans, "q", "quit"); if (selectedPid != null) { - hint(spans, "Esc", "unselect"); + hint(spans, "Esc", infraTableFocused ? "integrations" : "unselect"); } hint(spans, "\u2191\u2193", "navigate"); - hint(spans, "s", "sort"); - hint(spans, "a", "chart " + switch (chartMode) { - case CHART_ALL -> "[all]"; - case CHART_SINGLE -> "[single]"; - default -> "[off]"; - }); + if (!infraTableFocused) { + hint(spans, "s", "sort"); + hint(spans, "a", "chart " + switch (chartMode) { + case CHART_ALL -> "[all]"; + case CHART_SINGLE -> "[single]"; + default -> "[off]"; + }); + } hint(spans, "Enter", "details"); - if (selectedPid != null) { + if (selectedPid != null && !infraTableFocused) { IntegrationInfo selInfo = findSelectedIntegration(); if (selInfo != null) { - hint(spans, "p", selInfo.routeStarted > 0 ? "stop" : "start"); + hint(spans, "p", selInfo.routeStarted > 0 ? "stop routes" : "start routes"); } } + if (selectedPid != null) { + hint(spans, "x", "stop"); + hint(spans, "X", "kill"); + } hint(spans, "1-9", "tabs"); } else if (tab == TAB_ROUTES && showSource) { hint(spans, "c/Esc", "close"); @@ -5394,12 +5626,27 @@ public class CamelMonitor extends CamelCommand { data.set(infos); + // Discover running infra services + refreshInfraData(); + // Refresh log data only when the Log tab is visible if (tabsState.selected() == TAB_LOG) { - IntegrationInfo selected = findSelectedIntegration(); - if (selected != null) { - if (!selected.pid.equals(logFilePid)) { - // Integration changed: reset all incremental log state + // Determine which log file to tail: infra or integration + String logPid = null; + String logFileName = null; + InfraInfo selInfra = findSelectedInfra(); + if (selInfra != null) { + logPid = selInfra.pid; + logFileName = "infra-" + selInfra.alias + "-" + selInfra.pid + ".log"; + } else { + IntegrationInfo selected = findSelectedIntegration(); + if (selected != null) { + logPid = selected.pid; + logFileName = selected.pid + ".log"; + } + } + if (logPid != null) { + if (!logPid.equals(logFilePid)) { mutableFilteredEntries.clear(); logFilePos = -1; logTotalLinesRead = 0; @@ -5407,7 +5654,7 @@ public class CamelMonitor extends CamelCommand { logLineBuffer.setLength(0); } List<String> newRawLines = new ArrayList<>(); - readNewLogLines(selected.pid, newRawLines); + readNewLogLinesFromFile(logPid, logFileName, newRawLines); if (!newRawLines.isEmpty()) { logTotalLinesRead += newRawLines.size(); for (String line : newRawLines) { @@ -5430,6 +5677,88 @@ public class CamelMonitor extends CamelCommand { } } + @SuppressWarnings("unchecked") + private void refreshInfraData() { + List<InfraInfo> infraInfos = new ArrayList<>(); + try { + Path camelDir = CommandLineHelper.getCamelDir(); + if (Files.isDirectory(camelDir)) { + try (var files = Files.list(camelDir)) { + List<Path> jsonFiles = files + .filter(p -> { + String n = p.getFileName().toString(); + return n.startsWith("infra-") && n.endsWith(".json"); + }) + .toList(); + for (Path jsonFile : jsonFiles) { + String fn = jsonFile.getFileName().toString(); + // Format: infra-{alias}-{pid}.json + String withoutExt = fn.substring(0, fn.lastIndexOf('.')); + int lastDash = withoutExt.lastIndexOf('-'); + if (lastDash <= 6) { + continue; + } + String alias = withoutExt.substring(6, lastDash); + String pidStr = withoutExt.substring(lastDash + 1); + long pid; + try { + pid = Long.parseLong(pidStr); + } catch (NumberFormatException e) { + continue; + } + boolean alive = ProcessHandle.of(pid).map(ProcessHandle::isAlive).orElse(false); + + InfraInfo info = new InfraInfo(); + info.pid = pidStr; + info.alias = alias; + info.alive = alive; + try { + String json = Files.readString(jsonFile); + Object parsed = Jsoner.deserialize(json); + if (parsed instanceof Map<?, ?> map) { + for (Map.Entry<?, ?> e : map.entrySet()) { + info.properties.put(String.valueOf(e.getKey()), e.getValue()); + } + } + } catch (Exception e) { + // ignore parse errors + } + infraInfos.add(info); + } + } + } + } catch (IOException e) { + // ignore + } + + // Handle vanishing infra services + Set<String> liveInfraPids = infraInfos.stream().map(i -> i.pid).collect(Collectors.toSet()); + List<InfraInfo> previousInfra = infraData.get(); + for (InfraInfo prev : previousInfra) { + if (!prev.vanishing && !liveInfraPids.contains(prev.pid) && !vanishingInfra.containsKey(prev.pid)) { + vanishingInfra.put(prev.pid, new VanishingInfraInfo(prev, System.currentTimeMillis())); + } + } + long now = System.currentTimeMillis(); + Iterator<Map.Entry<String, VanishingInfraInfo>> infraIt = vanishingInfra.entrySet().iterator(); + while (infraIt.hasNext()) { + Map.Entry<String, VanishingInfraInfo> entry = infraIt.next(); + if (now - entry.getValue().startTime > VANISH_DURATION_MS) { + infraIt.remove(); + } else if (!liveInfraPids.contains(entry.getKey())) { + InfraInfo ghost = entry.getValue().info; + ghost.vanishing = true; + ghost.vanishStart = entry.getValue().startTime; + infraInfos.add(ghost); + } else { + infraIt.remove(); + } + } + + infraInfos.sort((a, b) -> a.alias.compareToIgnoreCase(b.alias)); + infraData.set(infraInfos); + } + private void updateThroughputHistory(IntegrationInfo info) { // Track exchangesTotal and exchangesFailed over a 1-second sliding window long currentTotal = info.exchangesTotal; @@ -6408,9 +6737,29 @@ public class CamelMonitor extends CamelCommand { .findFirst().orElse(null); } + private InfraInfo findSelectedInfra() { + if (selectedPid == null) { + return null; + } + return infraData.get().stream() + .filter(i -> selectedPid.equals(i.pid) && !i.vanishing) + .findFirst().orElse(null); + } + + private boolean isInfraSelected() { + return infraTableFocused && findSelectedInfra() != null; + } + private String selectedName() { IntegrationInfo info = findSelectedIntegration(); - return info != null ? truncate(info.name, 20) : "?"; + if (info != null) { + return truncate(info.name, 20); + } + InfraInfo infra = findSelectedInfra(); + if (infra != null) { + return truncate(infra.alias, 20); + } + return "?"; } private List<Long> findPids(String name) { @@ -6754,6 +7103,19 @@ public class CamelMonitor extends CamelCommand { Map<String, String> exchangeVariableTypes; } + static class InfraInfo { + String pid; + String alias; + String description; + Map<String, Object> properties = new TreeMap<>(); + boolean alive; + boolean vanishing; + long vanishStart; + } + record VanishingInfo(IntegrationInfo info, long startTime) { } + + record VanishingInfraInfo(InfraInfo info, long startTime) { + } }
