Skip to content

CAMEL-25897: camel-jbang - the source view shows the body types each step has been reached with at the end of its line - #28234

Merged
davsclaus merged 6 commits into
mainfrom
feature/CAMEL-25897-source-body-types
Oct 24, 2026
Merged

davsclaus merged 6 commits into
mainfrom
feature/CAMEL-25897-source-body-types

Conversation

@davsclaus

@davsclaus davsclaus commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #28220. CAMEL-25892 brought the body types each step of a route has been reached with to the Routes and Diagram tabs of the Camel TUI. This is its third view, the source view of the Source tab (CAMEL-25897):

  • At the end of the line of each step: its body types, dimmed and lined up, cut to the room there is (ByteArrayInputStreamCache, ⚠ LinkedHashMap | InputStreamCache).
  • A ⚠ in the gutter of a step reached with bodies of more than one kind (null, payload, data or collection), whose types are in the warning color: its body depends on the path a message takes.
  • The quick documentation of the line lists all the types, and for a mix of kinds says why it needs attention.

Source

Only for the version of the file that runs. The facts of a line hold only while the view has the file as the integration runs it. #28220 added the run state of the open file. The live run data is dimmed, and the body types are left out:

  • while the integration does not run the file as saved: saved after it loaded the file, reloading, or the reload failed;
  • until the run state is known for the file shown: after another file is opened, or the file is saved in the editor. Both compute the state at once, in the background.

A file whose routes are all stopped (or suspended) shows ○ the routes of this file are stopped. Its live run data and body types still show when the integration loaded the file as saved. Without a reload that is known, as the routes were loaded at the start. After a reload, when a stopped route was loaded is not known, so they are left out.

The issue asked to keep the body types of the routes an edit did not change, by their checksums. The TUI cannot compute the checksums of an edited YAML or XML file without loading it with Camel, which runs parts of it and needs the components (see #28220). So the view leaves them all out until the integration runs the file. In dev mode that is about a second after a save, and they come back as messages reach the new steps.

How

  • LiveRunLines collects the body types of the steps on each line with the live run data. A line is mixed when one of its steps was reached with more than one kind. Two steps on one line that each have a kind of their own do not make it mixed.
  • SourceLiveColumn draws the gutter mark and the types after the code, and gives the quick documentation entry. The types are cut one column before the scrollbar's. SourceViewer only calls it, and stays within its size limit.
  • The live run data is read again as soon as another file is shown, not up to a second later.

Tests

  • SourceBodyTypesTest:
    • the types at the end of the step lines, lined up, and the ⚠ in the gutter only on the mixed step;
    • cut to a narrow view;
    • none in plain mode;
    • none, with the live data dimmed and no runtime failure offered to fix, while the integration runs another version;
    • the quick documentation;
    • the steps of one line together;
    • a save in the editor hides them until the next refresh;
    • a cut name keeps its … and a count its digit, from 44 to 72 columns;
    • another file has its own live run data at once, and is stale until its state is known.
  • SourceRunStateTest: a file whose routes are all stopped, before and after a reload.
  • The camel-jbang-plugin-tui suite: 2228 tests pass.
  • Checked live with an integration in dev mode whose choice marshals in one branch:
    • the four steps after the choice have the ⚠ and their two kinds;
    • after an edit, the body types go away while the integration reloads, and come back with the new steps.

Docs: the Source help page, and the Source Editor and Diagram pages of the manual, with the screenshot.

Claude Code on behalf of davsclaus

🤖 Generated with Claude Code

@davsclaus davsclaus added this to the 4.24.0 milestone Oct 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clean, well-tested feature. The 7 test cases cover the full feature lifecycle: alignment and gutter marks, narrow-viewport truncation, plain mode, stale/dirty state dimming, quick-doc content, multi-processor-per-line aggregation with correct mixed semantics, and the save-stale-refresh state machine.

A few design notes worth having on record:

BodyTypes.mixed(null) is null-safe. The call in LiveRunLines.addBodyTypes() passes p.bodyKinds which can be null, but BodyTypes.mixed() has an explicit null guard (return kinds != null && kinds.size() > 1) — null input returns false, which is correct (no kinds = not mixed).

Threading is correct. bodyTypesAlign is written in refreshLiveLines() and read in addBodyTypes() — both on the render thread, following the same single-render-thread pattern as the existing liveTotalWidth/liveFailedWidth/liveMeanWidth fields. No volatile needed. Similarly, wasDirty in SourceRunState.Tracker is read and written exclusively from the UI thread in shown() — refresh() on the background thread never touches it.

gutter() / addBodyTypes() consistency. Both call current() which reads volatile saves/computedSaves. A background refresh could flip current() between the two calls, but this produces at most a one-frame visual glitch (gutter mark with no type list, or vice versa) — harmless in a TUI, not a correctness issue.

Progressive degradation in BodyTypes.fit(). The fallback chain (full label → short label → first N types + count → truncated first type + count → just the mark → empty) handles all viewport widths gracefully, which is validated by the narrow-view test (theTypesAreCutToTheRoomThereIs at 56 cols).

Save-stale semantics. The aSaveInTheEditorLeavesThemOutUntilTheIntegrationIsSeenToLoadIt test correctly verifies that after refresh(), stale() still returns true because state.kind() != Kind.RUNNING — body types remain hidden until the integration actually loads the new file version, not just until the next refresh cycle.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@oscerd oscerd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR

A careful third view of the body types: they come only from the integration's status, nothing loads the route file, and the run state from #28220 hides them while another version runs. No code blocker. Two display gaps and the CI gap are worth closing first.

Blockers

  • None in the code. Merge precondition: this PR is stacked (base feature/CAMEL-25771-route-checksum-views, on #28220 → #28213), so Build and test, doc validation and the license check never ran on 8a4c917d6339. Only Dependency Review and PR id uploader did. Let's get a completed build once it targets main.

Other findings: 4 (2 non-blocking, 1 suggestion, 1 question)

  1. Cut types lose their last column (SourceViewer.java:2952). The right edge is viewportWidth + hSkip, and viewportWidth is contentArea.width() (:1878). But the paragraph is drawn in hChunks.get(0), which is one column narrower because the last column is kept for the scrollbar (:1883-1888). When BodyTypes.fit cuts a name, truncateWithEllipsis fills the room exactly (BodyTypes.java:114). The … or the last digit of +N then lands in that column and is clipped: ByteArra… shows as ByteArra, which reads like a whole name. Passing viewportWidth - 1 + hSkip fixes it and keeps wrapRowCount consistent. The 56-column test stops at ⚠ byte[] +1, well before the edge. A narrower render that reaches the cut, asserting the … and the +1, would cover it.
  2. An unknown run state counts as current (SourceRunState.java:242). stale() is false whenever current() is null for a reason other than a save. One case is a state computed for another file: this lasts until the next refresh after switching files, while liveLines() can still serve the previous file's map for up to a second (SourceLiveColumn.java:257). The other is of() finding no Started route from the file (:95, all stopped or suspended). In that case, with a plain camel run (its default dev profile turns the message history on, and nothing reloads), a saved edit shows the old types and ⚠ on the new lines again after the next refresh, undimmed. What do you think about these three changes? Treat "not computed for the file shown" as stale (keep Computed(shown, null) so it differs from "no started route"). Re-read liveLines() when the file changes. And decide what a file whose routes are all stopped should show.
  3. [Suggestion] Jira scope. CAMEL-25897 still says the routes an edit did not change keep their hints through the CAMEL-25771 checksum, and the PR explains why it leaves that out. Could we update the issue text, or open a follow-up, before resolving it?
  4. [Question] Gutter ⚠. The mark for a mixed body kind is the same glyph and style as the deprecated-line mark (SourceViewer.java:2935, " ⚠ " in Theme.warning()), and a deprecated line shows only that one. Is sharing it intended, or would a distinct style help tell them apart?

Reviewed with Claude Code on behalf of oscerd. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.

@davsclaus

Copy link
Copy Markdown
Contributor Author

Thanks. These are addressed in 3101f32, after merging the updated #28220 branch in.

CI: as for #28220, Build and test runs once this PR targets main, after #28213 and #28220.

  1. Fixed. The right edge is now viewportWidth - 1 + hSkip, as the last column is the scrollbar's. aCutNameKeepsItsEllipsisAndTheCountItsDigits renders 44 to 72 columns and checks that every cut name keeps its … and every count its digit. Without the fix it fails at 45 columns.
  2. Done, all three:
    • A state not computed for the file shown counts as stale. For a file the integration has no route from, the tracker keeps Computed(file, null), which is not stale. Showing another file, or saving it, computes the state at once in the background, so the gap is a frame rather than a second. A refresh that started later wins over one that ends after it.
    • liveLines() reads the data again as soon as the file shown changes. anotherFileHasItsOwnLiveRunDataAtOnce fails without it.
    • A file whose routes are all stopped (or suspended) now has a state of its own: ○ the routes of this file are stopped.
      • Its live run data and body types still show when the integration loaded the file as saved. Without a reload that is known, as the routes were loaded at the start.
      • After a reload, when a stopped route was loaded is not known, so they are left out.
      • A file saved since the integration loaded it says not running yet, stopped or not.
  3. Done. The description of CAMEL-25897 now says the checksum part is out of scope, and why, and a comment points to this PR.
  4. Intended. ⚠ is the mark of a mixed body kind in every view (Routes, Diagram and the Camel CLI). The types at the end of the line carry it too, in the warning color. A line with a deprecated option takes the gutter as before, and its body types still show the ⚠. The quick documentation of the line says which one it is. A glyph of its own in the gutter would make the mark differ between the views.

Claude Code on behalf of davsclaus

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on my earlier review — all 4 of oscerd's findings have been addressed in 3101f32.

[1] Right-edge off-by-one — ✅ Addressed. viewportWidth - 1 + hSkip at SourceViewer.java:2952 is correct; the new aCutNameKeepsItsEllipsisAndTheCountItsDigits test validates the fix across 44–72 column widths, asserting the … and the +N digit are preserved at every width.

[2] Unknown run state — ✅ Addressed. Three separate fixes:

  • Computed(file, null) is now stored for the file shown but with no started route, so stale() correctly returns true until the next refresh() computes it — the gap is a single frame.
  • liveLines() re-reads immediately when liveLinesFile differs from loadedFilePath, eliminating the up-to-one-second lag on file switch.
  • Kind.STOPPED handles the all-stopped-routes case; state.stale() is 0 when routes were loaded as saved (body types visible), and is incremented for routes stopped after a reload (body types hidden). All three sub-cases have test coverage in SourceBodyTypesTest.

[3] JIRA scope — ✅ Confirmed by author.

[4] Gutter ⚠ glyph — ✅ Confirmed intentional; consistent with Routes and Diagram tabs.

Pre-fix validation (static trace): anotherFileHasItsOwnLiveRunDataAtOnce asserts immediate data switch after viewer.loadFile(other), which exercises the liveLinesFile check added in refreshLiveLines(). aSaveInTheEditorLeavesThemOutUntilTheIntegrationIsSeenToLoadIt verifies the saves/computedSaves tracking. Both tests would pass without the fix only if the data happened to load within the assert's frame — not the case given the synchronous refreshLiveLinesForTesting() helper.

The Tracker concurrency model is sound: shown() and stale() are UI-thread-only; saves is volatile and written only on the UI thread; the synchronized block in refresh() guards computed/written/computedSaves; and the AtomicLong refreshes ordering ensures a later refresh always wins over an earlier one that ends after it.

LGTM.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

davsclaus and others added 5 commits October 22, 2026 22:22
…sion and checksum of a route, and the Source view whether the file runs as saved

The route console has the checksum and the revision of each route, so the
views show them:

- Routes: a route added again changed in this run, such as by a reload, has
  its revision after its id, muted ("orders rev 3"). tui_get_table has the
  checksum and the revision of each route.
- Diagram: the Info panel of a route in the topology has its checksum, and
  its revision with the changes in this run ("3 · 2 changes").
- Source: the bottom border says whether the integration runs the file as it
  is saved: running as saved, reloading, not running yet (the integration
  runs without --dev, or did not reload the file), or the reload failed.
  tui_get_table has it as runState, so an agent can tell its edit runs.

The Source view compares when the file was saved with when the integration
loaded the routes it has from the file, not the checksums of the file:
- loading a YAML or XML file with Camel runs parts of it, such as creating
  its beans, which a tool that reads route sources must not do;
- an endpoint written with parameters is built by its component, which the
  TUI does not have, so the checksum would not be the one the route runs
  with.
An integration that has not reloaded loaded its routes when it started; after
a reload, a route that started after the save runs what is saved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…e is in parentheses and dimmed

The Routes table shows the revision of a changed route as "orders (rev 3)",
with the revision dimmed like the time since the last exchange beside the totals.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
… tui_get_table reads it or the tab is selected (address review)

- tui_get_table may read the Source tab while another tab is on screen,
  which the data refresh does not refresh, so its run state is computed when
  the table is read.
- Selecting the tab computes it in the background at once, so the border
  does not show the state of before for a refresh.
- The file shown comes from SourceViewer.getCurrentFilePath(), which already
  gave it.
- The docs say that an integration without camel-xml-io has no checksum and
  revision either: camel run always has it, a Spring Boot or Quarkus
  application may not.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…step has been reached with at the end of its line

With the message history on, such as with camel run --dev, the body types
each step of a running route has been reached with go at the end of its line
in the source view of the Source tab, dimmed, lined up, and cut to the room
there is. A step reached with bodies of more than one kind (null, payload,
data or collection) has a ⚠ in the gutter, and its types in the warning
color. The quick documentation of the line lists all the types, and for a mix
of kinds says that the body there depends on the path a message takes.

The facts of a line only hold while the view has the version of the file
that runs. The live run data is dimmed, and the body types are left out:
- while the integration does not run the file as saved (saved after it
  loaded it, reloading, or the reload failed);
- after a save in the editor, until the next refresh knows the state again.
Keeping the body types of the routes an edit did not change would need the
checksums of the routes of the edited file, which the TUI cannot compute
without loading the file with Camel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…re the scrollbar, and stay out until the run state of the file shown is known (address review)

- The last column of the source view is the scrollbar's, so the body types
  are cut one column before it: a cut name keeps its ellipsis, and "+1" its
  digit.
- The live run data is read again as soon as another file is shown, not up to
  a second later with the numbers of the previous file.
- Until the run state is known for the file shown, the integration may run
  another version of it, so the live run data is dimmed and the body types
  are left out. Showing another file, or saving it, computes the state at
  once in the background; a refresh that started later wins over one that
  ends after it.
- A file whose routes are all stopped (or suspended) shows "the routes of
  this file are stopped". Its live run data and body types still show when
  the integration loaded the file as saved, which is known without a reload,
  and not for a route stopped after a reload.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@davsclaus
davsclaus force-pushed the feature/CAMEL-25771-route-checksum-views branch from 18e2b26 to 484ea29 Compare October 22, 2026 20:29
@davsclaus
davsclaus force-pushed the feature/CAMEL-25897-source-body-types branch from 0b57a47 to 91c5380 Compare October 22, 2026 20:29
@github-actions
github-actions Bot deleted the branch feature/CAMEL-25771-route-checksum-views October 22, 2026 20:29
@davsclaus
davsclaus changed the base branch from feature/CAMEL-25771-route-checksum-views to main October 23, 2026 08:45
…ource-body-types

Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • docs
  • dsl/camel-jbang/camel-jbang-plugin-tui

🔬 Scalpel shadow comparison — Scalpel: 2 of 707 tested, 1 compile-only — current: 2 all tested

Maveniverse Scalpel detected 2 affected modules (current approach: 2).

Skip-tests mode would test 2 modules (2 direct + 1 downstream), skip tests for 1 (generated code, meta-modules)

Modules Scalpel would test (2)
  • camel-jbang-plugin-tui ← dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/LiveRunLines.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceLiveColumn.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceRunState.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/source.md, dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceBodyTypesTest.java, dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceRunStateTest.java
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
Modules with tests skipped (1)
  • camel-launcher

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (4 modules, 40.5s total)

Total reactor time: 40.5s

Module Duration Status
Camel :: Launcher 26.8s SUCCESS
Camel :: Docs 13.0s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Plugin :: TUI n/a

Top 20 slowest modules:

  • Camel :: Launcher (26.8s)
  • Camel :: Docs (13.0s)
  • Camel :: Launcher :: Container (0.7s)

⚙️ View full build and test results

@davsclaus davsclaus added the enhancement New feature or request label Oct 24, 2026
@davsclaus davsclaus self-assigned this Oct 24, 2026
@davsclaus
davsclaus merged commit 9692de5 into main Oct 24, 2026
7 checks passed
@davsclaus
davsclaus deleted the feature/CAMEL-25897-source-body-types branch October 24, 2026 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs dsl enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants