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 914537bf7524f2647be0836b7deaa6b9315a53b0 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Jul 28 07:52:18 2026 +0200 camel-tui: Use compact duration format in Inflight tab camel-tui: Improve table tabs and Internal Tasks/CVE Audit display - BackgroundTask: add attempting flag, fix nextAttemptTime calculation, persist error across retry attempts - Internal Tasks tab: show Waiting/Attempting status, use compact duration format, fix NEXT showing 0ms - Enable table row selection with highlight on Consumers, Producers, Inflight, Health, and Internal Tasks tabs - CVE Audit tab: extract (via xxx) into its own sortable VIA column, reduce CVE ID column width camel-tui: Update TUI documentation to match current state - Mention 40+ screens in intro - Update tab overview: Source is now Tab 2, HTTP moved to More - List all 31 More tabs organized by category groups - Add Source Code Browser section with inline doc and format cycling - Add Highlighted Features section: CVE Audit, Kafka, SQL, Memory Leak, Catalog - Update Theme section: 15 themes instead of 2 - Add Source Tab keyboard shortcuts - Fix tab number references throughout camel-tui: Document runtime selector (Main, Spring Boot, Quarkus) Run an example and Run from folder let you choose between Camel Main, Spring Boot, and Quarkus runtimes before launching. When a pom.xml is present the runtime is auto-detected. camel-tui: Document camel-cli-connector for Spring Boot and Quarkus apps camel-tui: Rename Internal Tasks tab to Recovery Tasks The tab tracks reconnection and retry operations, so "Recovery Tasks" better communicates its purpose to users and AI agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/impl/console/TaskRegistryDevConsole.java | 1 + .../apache/camel/support/task/BackgroundTask.java | 13 +- .../java/org/apache/camel/support/task/Task.java | 8 + .../modules/ROOT/pages/camel-jbang-tui.adoc | 192 ++++++++++++++++++--- .../dsl/jbang/core/commands/tui/ConsumersTab.java | 22 +-- .../dsl/jbang/core/commands/tui/CveAuditTab.java | 26 ++- .../dsl/jbang/core/commands/tui/HealthTab.java | 21 +-- .../dsl/jbang/core/commands/tui/InflightTab.java | 24 +-- .../jbang/core/commands/tui/InternalTaskInfo.java | 1 + .../jbang/core/commands/tui/InternalTasksTab.java | 49 +++--- .../dsl/jbang/core/commands/tui/ProducersTab.java | 22 +-- .../dsl/jbang/core/commands/tui/StatusParser.java | 1 + .../dsl/jbang/core/commands/tui/TabRegistry.java | 2 +- .../dsl/jbang/core/commands/tui/TuiIcons.java | 2 +- 14 files changed, 238 insertions(+), 146 deletions(-) diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/TaskRegistryDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/TaskRegistryDevConsole.java index e92c19c64df5..1bab1684eb8a 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/TaskRegistryDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/TaskRegistryDevConsole.java @@ -63,6 +63,7 @@ public class TaskRegistryDevConsole extends AbstractDevConsole { JsonObject jo = new JsonObject(); jo.put("name", task.getName()); jo.put("status", task.getStatus().name()); + jo.put("attempting", task.isAttempting()); jo.put("attempts", task.iteration()); jo.put("delay", task.getCurrentDelay()); jo.put("elapsed", task.getCurrentElapsedTime()); diff --git a/core/camel-support/src/main/java/org/apache/camel/support/task/BackgroundTask.java b/core/camel-support/src/main/java/org/apache/camel/support/task/BackgroundTask.java index f0f9a51bef81..5065391ca38e 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/task/BackgroundTask.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/task/BackgroundTask.java @@ -82,6 +82,7 @@ public class BackgroundTask extends AbstractTask implements BlockingTask { private final AtomicBoolean running = new AtomicBoolean(); private final AtomicBoolean completed = new AtomicBoolean(); private volatile boolean registeredByRun; + private volatile boolean attempting; BackgroundTask(TimeBudget budget, ScheduledExecutorService service, String name) { super(name); @@ -132,7 +133,8 @@ public class BackgroundTask extends AbstractTask implements BlockingTask { cause = e; throw e; } - nextAttemptTime = lastAttemptTime + budget.interval(); + // scheduleWithFixedDelay waits interval after this run finishes, so compute from now + nextAttemptTime = System.currentTimeMillis() + budget.interval(); } /** @@ -162,15 +164,22 @@ public class BackgroundTask extends AbstractTask implements BlockingTask { return completed.get(); } + @Override + public boolean isAttempting() { + return attempting; + } + protected boolean doRun(BooleanSupplier supplier) { try { - cause = null; + attempting = true; return supplier.getAsBoolean(); } catch (TaskRunFailureException e) { LOG.debug("Task {} failed at {} iterations and will attempt again on next interval: {}", getName(), budget.iteration(), e.getMessage()); cause = e; return false; + } finally { + attempting = false; } } diff --git a/core/camel-support/src/main/java/org/apache/camel/support/task/Task.java b/core/camel-support/src/main/java/org/apache/camel/support/task/Task.java index 5f74bf5b0f4d..72cfa586bdf4 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/task/Task.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/task/Task.java @@ -81,6 +81,14 @@ public interface Task { */ long getNextAttemptTime(); + /** + * Whether the task is currently attempting to run its supplier (true), or waiting for the next scheduled tick + * (false). + */ + default boolean isAttempting() { + return false; + } + /** * The task failed for some un-expected exception */ diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc index 74e0eee86f09..c487a80122b5 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc @@ -3,9 +3,11 @@ *Available as of Camel 4.21* Camel TUI is a terminal dashboard for developing, prototyping, and understanding Camel integrations. -It makes your integration visible -- you can see your route topology, watch messages flow through -processors, step through exchanges like scrubbing through a video timeline, and understand what Camel actually does -with your routes. No more black box. +With over 40 screens organized across tabs, it makes your entire integration visible -- you can browse +your project source code with inline documentation, see your route topology, watch messages flow through +processors, step through exchanges like scrubbing through a video timeline, inspect Kafka topics, +run SQL queries against your DataSources, audit CVE vulnerabilities, and understand what Camel actually +does with your routes. No more black box. image::jbang/camel-tui-overview.png[TUI Overview showing multiple routes] @@ -46,6 +48,11 @@ camel tui . Browse the example catalog -- type to filter by name . Press *Enter* to launch the selected example +Before launching, a run options form lets you choose the *runtime*: Camel Main (standalone), +Spring Boot, or Quarkus. This makes it easy to try any example on all three runtimes without +changing a single line of code. You can also set the integration name, toggle dev mode, and +add extra dependencies. + The example starts running in the background. The TUI auto-selects it as soon as it appears. From there you can explore tabs, watch messages flow, inspect the route diagram, and experiment. @@ -53,9 +60,46 @@ If an example requires infrastructure (like Kafka or a database), the TUI automa required Docker containers before launching the example. A notification in the footer shows the progress. +The same runtime selector is available in *Run from folder...* (F2 menu), which lets you point +the TUI at a local directory containing your routes. When a `pom.xml` is present, the runtime +is auto-detected and locked to match your project. + TIP: Press *F1* or *?* on any screen for context-sensitive help. Keyboard shortcuts are always shown in the footer bar. +=== Connecting Existing Spring Boot and Quarkus Applications + +The TUI auto-discovers integrations started with `camel run`. To monitor and control +existing Spring Boot or Quarkus applications, add the `camel-cli-connector` dependency +to your project. This is a lightweight runtime adapter that lets the TUI (and the Camel CLI) +communicate with your application -- all TUI features work the same way regardless of runtime. + +Spring Boot: + +[source,xml] +---- +<dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-cli-connector-starter</artifactId> +</dependency> +---- + +Quarkus: + +[source,xml] +---- +<dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-cli-connector</artifactId> +</dependency> +---- + +Once added, start your application normally and the TUI will discover it automatically. +No additional configuration is needed -- the connector auto-detects on the classpath and +registers the application with the local Camel CLI. + +See xref:camel-jbang-managing.adoc[Managing Integrations] for more details. + == Tabs Overview The TUI organizes information into tabs. Press number keys *1* through *0* to jump directly @@ -66,27 +110,55 @@ to any tab, or use *Tab* / *Shift+Tab* to cycle. | Key | Tab | What It Shows | 1 | Overview | All running integrations and infrastructure services. Start here. -| 2 | Log | Real-time application logs with search and filtering. -| 3 | Activity | Live exchange activity with elapsed times, endpoint sends, and failure tracking. -| 4 | Diagram | Visual route topology with drill-down into individual routes. -| 5 | Routes | Route list with message counts, throughput, and processing times. -| 6 | Endpoints | All registered endpoints with usage statistics. -| 7 | HTTP | HTTP endpoints with details. +| 2 | Source | File explorer for your project code with syntax highlighting and inline Camel documentation. +| 3 | Log | Real-time application logs with search and filtering. +| 4 | Activity | Live exchange activity with elapsed times, endpoint sends, and failure tracking. +| 5 | Diagram | Visual route topology with drill-down into individual routes. +| 6 | Routes | Route list with message counts, throughput, and processing times. +| 7 | Endpoints | All registered endpoints with usage statistics. | 8 | Inspect | Message history and tracing -- step through exchanges processor by processor. | 9 | Errors | Failures with stack traces and exchange context. -| 0 | More | Additional tabs: Beans, Browse, Circuit Breaker, Classpath, Configuration, Consumers, CVE Audit, Health, Inflight, Memory, Metrics, Spans, Process, Startup, and more. +| 0 | More | 30+ additional tabs organized by category (see below). |=== -Tabs appear dynamically based on what the integration uses. For example, the SQL tab appears -when a DataSource is present, Circuit Breaker when resilience4j is in use, and Spans when -OpenTelemetry is enabled. The TUI adapts to show only what's relevant to your integration. +The *More* menu (key *0*) opens a popup with tabs organized into groups: + +* *Routing* -- Browse Endpoints, Consumers, HTTP, Inflight, Producers, Route Controller +* *Observability* -- Circuit Breaker, Health, Metrics, Network Services, Exchange Events, Recovery Tasks, OpenTelemetry Spans +* *Data* -- JDBC DataSource, Kafka, SQL Query, SQL Trace +* *JVM* -- Classpath, Heap Memory Histogram, Memory Usage, Memory Leak, Process, Startup, Threads +* *Project* -- Beans, Catalog, Configuration, CVE Audit, Maven Dependencies, Type Converters, Data Type Transformers + +Tabs appear dynamically based on what the integration uses. For example, the Kafka tab appears +when a Kafka component is in use, SQL tabs when a DataSource is present, Circuit Breaker when +resilience4j is in use, and Spans when OpenTelemetry is enabled. The TUI adapts to show only +what's relevant to your integration. Tab badges show live counts -- the Errors tab shows a red badge when errors exist and Routes shows the route count. +== Source Code Browser + +The Source tab (Tab 2) gives you a file explorer into your project code. The left panel shows +a navigable file tree of the integration's source directory. Select any file to view it in the +right panel with full syntax highlighting. + +For Camel source files (YAML, XML, Java routes, and `application.properties`), press *i* to toggle +inline documentation. The TUI uses the Camel catalog to look up every component, EIP, language, +and data format used in your route and shows their documentation right next to the source code. +For properties files, it resolves `camel.component.*` and `camel.main.*` keys to their catalog +descriptions. This makes it easy to understand unfamiliar routes without leaving the terminal. + +Additional features: + +* *Format cycling* -- press *Space* to convert Camel routes between YAML, Java, and XML DSL formats +* *Search* -- press */* to search within the source code, *n*/*N* to jump between matches +* *Highlight* -- press *h* to highlight text occurrences +* *Resizable panels* -- drag the split border with the mouse to resize the file list and viewer + == Activity -The Activity tab (Tab 3) shows a live feed of exchange activity -- every exchange that flows through +The Activity tab shows a live feed of exchange activity -- every exchange that flows through the system is captured with its route, status (OK or Failed), elapsed time, endpoint sends, and how long ago it completed. @@ -105,7 +177,7 @@ Use *s* to cycle sort order, *S* to reverse, and *F5* to clear the activity list == Route Topology Diagram -The Diagram tab (Tab 4) renders the route topology as interactive ASCII art. It shows how routes +The Diagram tab renders the route topology as interactive ASCII art. It shows how routes connect to each other through shared endpoints (direct, seda, kafka, etc.). image::jbang/camel-tui-diagram.png[Diagram topology view] @@ -322,26 +394,28 @@ The Doctor checks your development environment and reports issues: == Theme -The TUI ships with two color themes, *dark* (the default) and *light*, defined as -CSS stylesheets. Open the *F2* actions menu and choose *Light Theme* or *Dark Theme* -to switch at runtime. +The TUI ships with 15 color themes defined as CSS stylesheets: + +* *Dark themes* -- Dark (default), Dracula, Nord, Solarized Dark, Gruvbox Dark, + Catppuccin Mocha, Tokyo Night, Rosé Pine, Kanagawa, Everforest, Monochrome, CRT +* *Light themes* -- Light, Solarized Light, Catppuccin Latte -Pass `--theme=dark` or `--theme=light` on the command line to pick the palette for -the current session. The CLI value overrides the persisted preference from -`.camel-cli.properties`; runtime toggles and the config file still apply on -later launches when `--theme` is omitted. +Open the *F2* actions menu and choose *Settings...* to switch themes, or pass +`--theme=<name>` on the command line (e.g., `--theme=tokyo-night`). The CLI value +overrides the persisted preference from `.camel-cli.properties`; runtime toggles and +the config file still apply on later launches when `--theme` is omitted. -The brand orange accent is identical in both themes; status colors (success, -warning, error) and borders adapt for readability on dark and light terminals. +The brand orange accent is consistent across all themes; status colors (success, +warning, error) and borders adapt for readability on each palette. -Your choice is remembered: it is saved as `camel.tui.theme` (`dark` or `light`) -in `.camel-cli.properties` and restored the next time you open the TUI. +Your choice is remembered: it is saved as `camel.tui.theme` in `.camel-cli.properties` +and restored the next time you open the TUI. == Settings Open the *F2* actions menu and choose *Settings...* to change TUI preferences in one place: -* *Theme* -- cycle between *dark* and *light* (applied immediately on save). +* *Theme* -- cycle through 15 available themes (applied immediately on save). * *Starting Tab* -- the tab shown when the TUI launches; any tab (primary or under *More*) can be chosen. Defaults to *Overview*. * *Select Tab* -- the tab to switch to when selecting an integration from the Overview tab. @@ -389,6 +463,25 @@ respectively. Use *↑*/*↓* on the input line to walk previous entries. Limits | *Esc* | Close popup / go back / return to Overview |=== +=== Source Tab + +[cols="2,5",options="header"] +|=== +| Key | Action + +| *Up/Down* | Navigate files (left panel) or scroll source (right panel) +| *Enter* | Open file or directory +| *Backspace* | Go to parent directory +| *Tab* | Toggle focus between file list and source viewer +| *Space* | Cycle format (YAML/Java/XML) for Camel routes +| *i* | Toggle inline Camel documentation +| */* | Search in source +| *h* | Highlight text +| *n* / *N* | Next / previous search match +| *w* | Toggle word wrap +| *Esc* / *c* | Close source viewer +|=== + === Diagram Tab [cols="2,5",options="header"] @@ -420,6 +513,49 @@ respectively. Use *↑*/*↓* on the input line to walk previous entries. Limits | *F5* | Refresh / clear traces |=== +== Highlighted Features + +Beyond the core tabs described above, the TUI includes several specialized screens worth highlighting. + +=== CVE Audit + +The CVE Audit tab (under More > Project) scans all Maven dependencies on your integration's classpath +against the https://osv.dev[OSV.dev] vulnerability database. It queries every JAR using the OSV batch +API and displays known vulnerabilities grouped by severity (Critical, High, Medium, Low). + +Each vulnerability shows the CVE ID, affected artifact, the direct dependency that pulls it in (VIA column), +and a summary. Select a CVE to see full details including CVSS vector, published date, fixed versions, +and aliases. Results are cached globally -- switching integrations that share JARs is instant. + +=== Kafka + +The Kafka tab (under More > Data) provides a dedicated view for Kafka consumers and their connectivity +status. It shows consumer group details, topic assignments, partition offsets, and reconnection state. +When a broker connection drops, the Recovery Tasks tab shows the reconnection task with its +Waiting/Attempting status, retry count, and error details. + +=== SQL Query and SQL Trace + +When your integration includes a DataSource, two SQL tabs appear under More > Data: + +* *SQL Query* -- an interactive SQL console where you can write and execute queries against any + DataSource in your integration. Results are displayed as a formatted table with column types. +* *SQL Trace* -- captures and displays SQL statements executed by your routes in real-time, + showing query text, execution time, and the route that triggered them. + +=== Memory Leak Detection + +The Memory Leak tab (under More > JVM) uses Java Flight Recorder (JFR) to diagnose memory leaks +in your running integration. It runs two sequential recordings and compares object retention trends, +classifying each class as growing, stable, shrinking, new, or gone. This is lightweight and safe +for production use. + +=== Catalog + +The Catalog tab (under More > Project) lets you browse the full Camel component catalog from within +the TUI. Search for components by name, view their documentation, options, and supported headers -- +useful when building routes and you need to check what options a component supports. + == AI Integration (MCP) The TUI includes an embedded https://modelcontextprotocol.io/[MCP] (Model Context Protocol) @@ -592,7 +728,7 @@ vhs demo.tape # .tape -> .gif | `100` | `--theme` -| Color theme for this session: `dark` or `light`. Overrides the persisted `camel.tui.theme` preference when set. +| Color theme for this session (e.g., `dark`, `tokyo-night`, `dracula`). See <<Theme>> for the full list of 15 themes. Overrides the persisted `camel.tui.theme` preference when set. | | `--record` diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java index 6942081fdd18..ee8997034b60 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java @@ -26,8 +26,6 @@ import dev.tamboui.layout.Rect; import dev.tamboui.style.Style; import dev.tamboui.terminal.Frame; import dev.tamboui.text.Span; -import dev.tamboui.tui.event.KeyCode; -import dev.tamboui.tui.event.KeyEvent; import dev.tamboui.tui.event.MouseEvent; import dev.tamboui.widgets.block.Block; import dev.tamboui.widgets.block.BorderType; @@ -46,24 +44,6 @@ class ConsumersTab extends AbstractTableTab { super(ctx, "id", "status", "type", "remote", "inflight", "polls", "uri"); } - @Override - public void navigateUp() { - } - - @Override - public void navigateDown() { - } - - @Override - public boolean handleKeyEvent(KeyEvent ke) { - if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP) - || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN) - || ke.isHome() || ke.isEnd()) { - return false; - } - return super.handleKeyEvent(ke); - } - @Override public boolean handleMouseEvent(MouseEvent me, Rect area) { return false; @@ -136,6 +116,8 @@ class ConsumersTab extends AbstractTableTab { Constraint.length(8), Constraint.length(22), Constraint.fill()) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL) .title(" Consumers ").build()) .build(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java index 9e4bc931196e..a158921ef7d4 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CveAuditTab.java @@ -66,7 +66,7 @@ class CveAuditTab extends AbstractTableTab { private int scannedCount; CveAuditTab(MonitorContext ctx) { - super(ctx, "severity", "id", "artifact"); + super(ctx, "severity", "id", "artifact", "via"); } @Override @@ -209,22 +209,21 @@ class CveAuditTab extends AbstractTableTab { String firstParent = group.affectedGavs.isEmpty() ? null : group.gavParents.get(firstArtifact); int artCount = group.affectedGavs.size(); String artDisplay = firstArtifact; - if (firstParent != null) { - artDisplay += " (via " + firstParent + ")"; - } if (artCount > 1) { artDisplay += " (+" + (artCount - 1) + ")"; } + String viaDisplay = firstParent != null ? firstParent : ""; rows.add(Row.from( Cell.from(Span.styled(group.severity != null ? group.severity : "", severityStyle(group.severity))), Cell.from(Span.styled(group.canonicalId != null ? group.canonicalId : "", Style.EMPTY.bold())), Cell.from(Span.styled(artDisplay, Style.EMPTY.fg(Theme.accent()))), + Cell.from(Span.styled(viaDisplay, Style.EMPTY.dim())), Cell.from(Span.styled(group.summary != null ? group.summary : "", Style.EMPTY.dim())))); } if (rows.isEmpty() && dataLoaded) { - rows.add(emptyRow("No matching vulnerabilities", 4)); + rows.add(emptyRow("No matching vulnerabilities", 5)); } String title = buildTitle(); @@ -235,11 +234,13 @@ class CveAuditTab extends AbstractTableTab { Cell.from(Span.styled(sortLabel("SEVERITY", "severity"), sortStyle("severity"))), Cell.from(Span.styled(sortLabel("CVE ID", "id"), sortStyle("id"))), Cell.from(Span.styled(sortLabel("ARTIFACT", "artifact"), sortStyle("artifact"))), + Cell.from(Span.styled(sortLabel("VIA", "via"), sortStyle("via"))), Cell.from(Span.styled("SUMMARY", Style.EMPTY.dim())))) .widths( Constraint.length(10), - Constraint.length(20), - Constraint.percentage(30), + Constraint.length(16), + Constraint.percentage(25), + Constraint.percentage(15), Constraint.fill()) .highlightStyle(Theme.selectionBg()) .highlightSpacing(Table.HighlightSpacing.ALWAYS) @@ -448,6 +449,7 @@ class CveAuditTab extends AbstractTableTab { case "artifact" -> compareStr( a.affectedGavs.isEmpty() ? "" : a.affectedGavs.get(0), b.affectedGavs.isEmpty() ? "" : b.affectedGavs.get(0)); + case "via" -> compareStr(firstParent(a), firstParent(b)); default -> Integer.compare(severityIndex(a.severity), severityIndex(b.severity)); }; return sortReversed ? -cmp : cmp; @@ -534,6 +536,14 @@ class CveAuditTab extends AbstractTableTab { }; } + private static String firstParent(VulnGroup g) { + if (g.affectedGavs.isEmpty()) { + return ""; + } + String parent = g.gavParents.get(g.affectedGavs.get(0)); + return parent != null ? parent : ""; + } + private static int severityIndex(String severity) { int idx = SEVERITY_ORDER.indexOf(severity); return idx >= 0 ? idx : SEVERITY_ORDER.size(); @@ -574,6 +584,7 @@ class CveAuditTab extends AbstractTableTab { - **SEVERITY** — CRITICAL (red), HIGH (red), MEDIUM (yellow), LOW (dim) - **CVE ID** — the canonical vulnerability identifier (prefers CVE- over GHSA-) - **ARTIFACT** — the affected Maven artifact (groupId:artifactId:version) + - **VIA** — the direct dependency that pulls in the affected artifact (transitive dependency chain) - **SUMMARY** — brief description of the vulnerability ## Detail View @@ -612,6 +623,7 @@ class CveAuditTab extends AbstractTableTab { JsonObject row = new JsonObject(); row.put("cveId", g.canonicalId); row.put("severity", g.severity); + row.put("via", firstParent(g)); row.put("summary", g.summary); row.put("published", g.published); JsonArray arts = new JsonArray(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java index 3fe734b5bc08..5f431b57049b 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java @@ -24,7 +24,6 @@ import dev.tamboui.layout.Rect; import dev.tamboui.style.Style; import dev.tamboui.terminal.Frame; import dev.tamboui.text.Span; -import dev.tamboui.tui.event.KeyCode; import dev.tamboui.tui.event.KeyEvent; import dev.tamboui.tui.event.MouseEvent; import dev.tamboui.widgets.block.Block; @@ -48,24 +47,6 @@ class HealthTab extends AbstractTableTab { sort = "name"; } - @Override - public void navigateUp() { - } - - @Override - public void navigateDown() { - } - - @Override - public boolean handleKeyEvent(KeyEvent ke) { - if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP) - || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN) - || ke.isHome() || ke.isEnd()) { - return false; - } - return super.handleKeyEvent(ke); - } - @Override public boolean handleMouseEvent(MouseEvent me, Rect area) { return false; @@ -150,6 +131,8 @@ class HealthTab extends AbstractTableTab { Constraint.length(12), Constraint.length(6), Constraint.fill()) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build()) .build(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java index e38f1da51b30..5c0a5d2cf13c 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java @@ -26,8 +26,6 @@ import dev.tamboui.terminal.Frame; import dev.tamboui.text.Line; import dev.tamboui.text.Span; import dev.tamboui.text.Text; -import dev.tamboui.tui.event.KeyCode; -import dev.tamboui.tui.event.KeyEvent; import dev.tamboui.tui.event.MouseEvent; import dev.tamboui.widgets.block.Block; import dev.tamboui.widgets.block.BorderType; @@ -57,24 +55,6 @@ class InflightTab extends AbstractTableTab { sort = "duration"; } - @Override - public void navigateUp() { - } - - @Override - public void navigateDown() { - } - - @Override - public boolean handleKeyEvent(KeyEvent ke) { - if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP) - || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN) - || ke.isHome() || ke.isEnd()) { - return false; - } - return super.handleKeyEvent(ke); - } - @Override public boolean handleMouseEvent(MouseEvent me, Rect area) { return false; @@ -105,7 +85,7 @@ class InflightTab extends AbstractTableTab { ? Theme.error().bold() : Theme.success(); - String duration = TimeUtils.printDuration(ii.duration, true); + String duration = TimeUtils.printDuration(ii.duration, false); Style durationStyle = durationColor(ii.duration); String route = ii.atRouteId != null ? ii.atRouteId : ""; @@ -142,6 +122,8 @@ class InflightTab extends AbstractTableTab { Constraint.fill(), Constraint.length(14), Constraint.length(23)) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build()) .build(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTaskInfo.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTaskInfo.java index 23ee67a0615d..11eb5a21c18b 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTaskInfo.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTaskInfo.java @@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui; class InternalTaskInfo { String name; String status; + boolean attempting; long attempts; long delay; long elapsed; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTasksTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTasksTab.java index 61c7b476c8f8..7a7b57be39b6 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTasksTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InternalTasksTab.java @@ -55,16 +55,18 @@ class InternalTasksTab extends AbstractTableTab { List<Row> rows = new ArrayList<>(); for (InternalTaskInfo ti : sorted) { - Style statusStyle = statusStyle(ti.status); + String statusText = ti.attempting + ? "Attempting" : ("Active".equals(ti.status) ? "Waiting" : (ti.status != null ? ti.status : "")); + Style statusStyle = ti.attempting ? Theme.warning() : statusStyle(ti.status); String elapsed = TimeUtils.printAge(ti.elapsed); String first = ti.firstTime > 0 ? TimeUtils.printSince(ti.firstTime) : ""; - String last = ti.lastTime > 0 ? TimeUtils.printSince(ti.lastTime, true) : ""; + String last = ti.lastTime > 0 ? TimeUtils.printSince(ti.lastTime) : ""; String next = formatNext(ti.nextTime); String error = ti.error != null ? ti.error : ""; rows.add(Row.from( Cell.from(Span.styled(" " + (ti.name != null ? ti.name : ""), Style.EMPTY.fg(Theme.accent()))), - Cell.from(Span.styled(ti.status != null ? ti.status : "", statusStyle)), + Cell.from(Span.styled(statusText, statusStyle)), rightCell(String.valueOf(ti.attempts), 8), rightCell(String.valueOf(ti.delay), 8), Cell.from(elapsed), @@ -75,7 +77,7 @@ class InternalTasksTab extends AbstractTableTab { } if (rows.isEmpty()) { - rows.add(emptyRow("No internal tasks", 9)); + rows.add(emptyRow("No recovery tasks", 9)); } Table table = Table.builder() @@ -100,8 +102,10 @@ class InternalTasksTab extends AbstractTableTab { Constraint.length(10), Constraint.length(10), Constraint.fill()) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL) - .title(" Internal Tasks ").build()) + .title(" Recovery Tasks ").build()) .build(); lastTableArea = area; @@ -144,36 +148,26 @@ class InternalTasksTab extends AbstractTableTab { return ""; } long age = nextTime - System.currentTimeMillis(); - return TimeUtils.printDuration(age, true); - } - - @Override - public SelectionContext getSelectionContext() { - IntegrationInfo info = ctx.findSelectedIntegration(); - if (info == null || info.internalTasks.isEmpty()) { - return null; + if (age <= 0) { + return ""; } - List<InternalTaskInfo> sorted = new ArrayList<>(info.internalTasks); - sorted.sort(this::sortTask); - List<String> items = sorted.stream().map(t -> t.name != null ? t.name : "").toList(); - Integer sel = tableState.selected(); - return new SelectionContext("table", items, sel != null ? sel : -1, items.size(), "Internal Tasks"); + return TimeUtils.printDuration(age); } @Override public String description() { - return "Background task registry (reconnections, retries)"; + return "Recovery and reconnection tasks (retries, leader election)"; } @Override public String getHelpText() { return """ - # Internal Tasks + # Recovery Tasks - Internal tasks are background operations that Camel components schedule - for retry and reconnection purposes. For example, when a JMS or SMPP - connection drops, the component registers an internal task that periodically - attempts to reconnect. + Recovery tasks are background operations that Camel components schedule + for reconnection and retry purposes. For example, when a Kafka broker or + JMS connection drops, the component registers a recovery task that + periodically attempts to reconnect. Tasks self-register when they start running and are removed when they complete, fail permanently, or exhaust their retry budget. The table @@ -182,7 +176,7 @@ class InternalTasksTab extends AbstractTableTab { ## Table Columns - **NAME** — Descriptive name of the task (e.g., connection target or component) - - **STATUS** — Current state: `Active` (running), `Completed` (finished successfully), `Failed` (gave up), `Exhausted` (retry budget spent) + - **STATUS** — Current state: `Waiting` (waiting for next attempt), `Attempting` (actively trying now), `Completed` (finished successfully), `Failed` (gave up), `Exhausted` (retry budget spent) - **ATTEMPTS** — Number of retry attempts made so far - **DELAY** — Current delay between attempts in milliseconds - **ELAPSED** — Total time since the task started @@ -193,7 +187,7 @@ class InternalTasksTab extends AbstractTableTab { ## When Tasks Appear - You will typically see internal tasks when: + You will typically see recovery tasks when: - A messaging broker connection is lost and the consumer is reconnecting - A leader election is in progress (e.g., camel-master) - A component is retrying a failed initialization @@ -215,7 +209,7 @@ class InternalTasksTab extends AbstractTableTab { return null; } JsonObject result = new JsonObject(); - result.put("tab", "Internal Tasks"); + result.put("tab", "Recovery Tasks"); JsonArray rows = new JsonArray(); List<InternalTaskInfo> sorted = new ArrayList<>(info.internalTasks); sorted.sort(this::sortTask); @@ -223,6 +217,7 @@ class InternalTasksTab extends AbstractTableTab { JsonObject row = new JsonObject(); row.put("name", ti.name); row.put("status", ti.status); + row.put("attempting", ti.attempting); row.put("attempts", ti.attempts); row.put("delay", ti.delay); row.put("elapsed", ti.elapsed); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProducersTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProducersTab.java index dec2525b51d3..c337ff0fbf72 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProducersTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProducersTab.java @@ -24,8 +24,6 @@ import dev.tamboui.layout.Rect; import dev.tamboui.style.Style; import dev.tamboui.terminal.Frame; import dev.tamboui.text.Span; -import dev.tamboui.tui.event.KeyCode; -import dev.tamboui.tui.event.KeyEvent; import dev.tamboui.tui.event.MouseEvent; import dev.tamboui.widgets.block.Block; import dev.tamboui.widgets.block.BorderType; @@ -44,24 +42,6 @@ class ProducersTab extends AbstractTableTab { super(ctx, "route", "status", "type", "uri"); } - @Override - public void navigateUp() { - } - - @Override - public void navigateDown() { - } - - @Override - public boolean handleKeyEvent(KeyEvent ke) { - if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP) - || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN) - || ke.isHome() || ke.isEnd()) { - return false; - } - return super.handleKeyEvent(ke); - } - @Override public boolean handleMouseEvent(MouseEvent me, Rect area) { return false; @@ -109,6 +89,8 @@ class ProducersTab extends AbstractTableTab { Constraint.length(20), Constraint.length(8), Constraint.fill()) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL) .title(" Producers ").build()) .build(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java index 014bc2f245b1..493a8f556f58 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java @@ -471,6 +471,7 @@ final class StatusParser { InternalTaskInfo ti = new InternalTaskInfo(); ti.name = tj.getString("name"); ti.status = tj.getString("status"); + ti.attempting = tj.getBooleanOrDefault("attempting", false); ti.attempts = tj.getLongOrDefault("attempts", 0); ti.delay = tj.getLongOrDefault("delay", 0); ti.elapsed = tj.getLongOrDefault("elapsed", 0); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java index 9bd91d6def4b..0e930a47ef7a 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java @@ -202,7 +202,7 @@ class TabRegistry { List.of(), info -> !info.services.isEmpty()), new MoreTab(TuiIcons.TAB_EVENTS, "Events", "&Exchange Events", eventTab, "Observability"), new MoreTab( - TuiIcons.TAB_INTERNAL_TASKS, "Internal Tasks", "In&ternal Tasks", internalTasksTab, + TuiIcons.TAB_RECOVERY_TASKS, "Recovery Tasks", "&Recovery Tasks", internalTasksTab, "Observability", List.of(), info -> !info.internalTasks.isEmpty()), new MoreTab( diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java index 9858818d9a76..4da9429eebb7 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java @@ -150,7 +150,7 @@ final class TuiIcons { static final String TAB_THREADS = "🧵"; static final String TAB_PRODUCERS = "📤"; static final String TAB_EVENTS = "📣"; - static final String TAB_INTERNAL_TASKS = "🔩"; + static final String TAB_RECOVERY_TASKS = "🔁"; static final String TAB_ROUTE_CONTROLLER = "🚦"; static final String TAB_TYPE_CONVERTERS = "🔄"; static final String TAB_TRANSFORMERS = "🔀";
