This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-23802 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 3241898f8675cd1759c399dbfadcdf0c766003a9 Author: Claus Ibsen <[email protected]> AuthorDate: Fri Jun 19 10:09:55 2026 +0200 CAMEL-23802: Remove TamboUI hacks when upgrading to 0.4 Upgrade TamboUI from 0.3.0 to 0.4.0 and remove workarounds that are no longer needed: - Delete custom MirroredSparkline (456 lines) and use upstream DualSparkline - Remove reflection hack in TuiBackendHelper for JLineBackend(Terminal) constructor - Remove class preloading workaround for JVM shutdown ClassNotFoundException Signed-off-by: Claus Ibsen <[email protected]> Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../jbang/core/commands/tui/CamelCatalogTui.java | 1 - .../dsl/jbang/core/commands/tui/CamelMonitor.java | 1 - .../jbang/core/commands/tui/CircuitBreakerTab.java | 4 +- .../dsl/jbang/core/commands/tui/EndpointsTab.java | 26 +- .../jbang/core/commands/tui/MirroredSparkline.java | 456 --------------------- .../jbang/core/commands/tui/TuiBackendHelper.java | 21 +- .../dsl/jbang/core/commands/tui/TuiHelper.java | 13 - parent/pom.xml | 2 +- 8 files changed, 13 insertions(+), 511 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java index 40e20580ae6c..9710a6506445 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java @@ -92,7 +92,6 @@ public class CamelCatalogTui extends CamelCommand { public Integer doCall() throws Exception { // to make ServiceLoader work with tamboui for downloaded JARs Thread.currentThread().setContextClassLoader(classLoader); - TuiHelper.preloadClasses(classLoader); loadCatalog(); 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 452c15c3324b..d1595e561505 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 @@ -265,7 +265,6 @@ public class CamelMonitor extends CamelCommand { // to make ServiceLoader work with tamboui for downloaded JARs Thread.currentThread().setContextClassLoader(classLoader); - TuiHelper.preloadClasses(classLoader); // Create shared context and tab instances ctx = new MonitorContext(data, infraData); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java index ac683ae3563c..af0da28d17a2 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java @@ -35,6 +35,7 @@ import dev.tamboui.widgets.block.Block; import dev.tamboui.widgets.block.BorderType; import dev.tamboui.widgets.block.Title; import dev.tamboui.widgets.paragraph.Paragraph; +import dev.tamboui.widgets.sparkline.DualSparkline; import dev.tamboui.widgets.table.Cell; import dev.tamboui.widgets.table.Row; import dev.tamboui.widgets.table.Table; @@ -376,11 +377,12 @@ class CircuitBreakerTab implements MonitorTab { Span.raw(String.format(" ok:%-4d ", curSuccess)), Span.styled("▬", Style.EMPTY.fg(Color.LIGHT_RED)), Span.raw(String.format(" fail:%-4d msg/s", curFail))); - frame.renderWidget(MirroredSparkline.builder() + frame.renderWidget(DualSparkline.builder() .topData(successArr) .bottomData(failArr) .topStyle(Style.EMPTY.fg(Color.GREEN)) .bottomStyle(Style.EMPTY.fg(Color.LIGHT_RED)) + .showYAxis(true) .xLabels("-60s", "-45s", "-30s", "-15s", "now") .block(Block.builder().borderType(BorderType.ROUNDED) .title(Title.from(chartTitle)).build()) diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java index 3d97c4f23bab..14b08694517c 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java @@ -37,6 +37,7 @@ import dev.tamboui.widgets.block.Block; import dev.tamboui.widgets.block.BorderType; import dev.tamboui.widgets.block.Title; import dev.tamboui.widgets.paragraph.Paragraph; +import dev.tamboui.widgets.sparkline.DualSparkline; import dev.tamboui.widgets.table.Cell; import dev.tamboui.widgets.table.Row; import dev.tamboui.widgets.table.Table; @@ -447,11 +448,12 @@ class EndpointsTab implements MonitorTab { Span.raw(String.format(" out:%-4d msg/s", curOut))); Rect rightArea = hSplit.get(1); - frame.renderWidget(MirroredSparkline.builder() + frame.renderWidget(DualSparkline.builder() .topData(inArr) .bottomData(outArr) .topStyle(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))) .bottomStyle(Style.EMPTY.fg(Color.CYAN)) + .showYAxis(true) .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 4) + "s", "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 4) + "s", "now") .block(Block.builder().borderType(BorderType.ROUNDED) @@ -556,11 +558,12 @@ class EndpointsTab implements MonitorTab { Span.styled("▬", Style.EMPTY.fg(Color.CYAN)), Span.raw(String.format(" out:%-4d msg/s", curOut))); - frame.renderWidget(MirroredSparkline.builder() + frame.renderWidget(DualSparkline.builder() .topData(inArr) .bottomData(outArr) .topStyle(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))) .bottomStyle(Style.EMPTY.fg(Color.CYAN)) + .showYAxis(true) .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 4) + "s", "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 4) + "s", "now") .block(Block.builder().borderType(BorderType.ROUNDED) @@ -594,12 +597,12 @@ class EndpointsTab implements MonitorTab { Span.styled("▬", Style.EMPTY.fg(Color.MAGENTA)), Span.raw(String.format(" out:%-8s avg body", sizeToString(curOut)))); - frame.renderWidget(MirroredSparkline.builder() + frame.renderWidget(DualSparkline.builder() .topData(inArr) .bottomData(outArr) .topStyle(Style.EMPTY.fg(Color.YELLOW)) .bottomStyle(Style.EMPTY.fg(Color.MAGENTA)) - .yLabelFormatter(EndpointsTab::sizeToYLabel) + .showYAxis(true) .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 4) + "s", "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 4) + "s", "now") .block(Block.builder().borderType(BorderType.ROUNDED) @@ -611,21 +614,6 @@ class EndpointsTab implements MonitorTab { return value != null ? value : 0L; } - private static String sizeToYLabel(long size) { - if (size <= 0) { - return "0 B"; - } - if (size < 1024) { - return size + "B"; - } else if (size < 1024 * 1024) { - long kb = size / 1024; - return kb + "KB"; - } else { - long mb = size / (1024 * 1024); - return mb + "MB"; - } - } - static String sizeToString(long size) { if (size < 0) { return "-"; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java deleted file mode 100644 index 30b8ad52e573..000000000000 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.camel.dsl.jbang.core.commands.tui; - -import java.util.List; -import java.util.function.LongFunction; - -import dev.tamboui.buffer.Buffer; -import dev.tamboui.layout.Rect; -import dev.tamboui.style.Style; -import dev.tamboui.widget.Widget; -import dev.tamboui.widgets.block.Block; -import dev.tamboui.widgets.sparkline.Sparkline; - -/** - * A mirrored sparkline widget that displays two time-series datasets as vertical bars growing in opposite directions - * from a shared centre axis. - * <p> - * The top series renders as bars growing <em>upward</em> from the centre; the bottom series renders as bars growing - * <em>downward</em> from the centre. Sub-pixel resolution is achieved using Unicode block characters (▁▂▃▄▅▆▇█), giving - * smooth visual gradation within a single character row. This layout matches the style of macOS Activity Monitor's - * network and disk activity graphs. - * <p> - * Example usage: - * - * <pre>{@code - * MirroredSparkline chart = MirroredSparkline.builder() - * .topData(inRates) - * .bottomData(outRates) - * .topStyle(Style.EMPTY.fg(Color.GREEN)) - * .bottomStyle(Style.EMPTY.fg(Color.BLUE)) - * .xLabels("-60s", "-45s", "-30s", "-15s", "now") - * .block(Block.builder().borderType(BorderType.ROUNDED) - * .title(Title.from("In / Out msg/s")).build()) - * .build(); - * }</pre> - * - * <h2>Differences from {@link Sparkline}</h2> - * <table> - * <caption>Feature comparison between Sparkline and MirroredSparkline</caption> - * <tr> - * <th></th> - * <th>{@code Sparkline}</th> - * <th>{@code MirroredSparkline}</th> - * </tr> - * <tr> - * <td>Series</td> - * <td>1</td> - * <td>2 (top + bottom)</td> - * </tr> - * <tr> - * <td>Growth direction</td> - * <td>always upward from bottom row</td> - * <td>top grows up, bottom grows down, from a shared centre separator row</td> - * </tr> - * <tr> - * <td>Height</td> - * <td>1 row (fixed at bottom of area)</td> - * <td>fills the full area height</td> - * </tr> - * <tr> - * <td>Y-axis labels</td> - * <td>none</td> - * <td>optional: max / 0 / max at top, centre, and bottom rows</td> - * </tr> - * <tr> - * <td>X-axis labels</td> - * <td>none</td> - * <td>optional label row rendered below the chart body</td> - * </tr> - * <tr> - * <td>BarSet</td> - * <td>yes ({@link Sparkline.BarSet})</td> - * <td>yes (reuses {@link Sparkline.BarSet})</td> - * </tr> - * </table> - * - * <p> - * This class is intended for contribution to the TamboUI project as a first-class widget under - * {@code dev.tamboui.widgets.sparkline}. The package and license header would change accordingly upon contribution. - */ -public final class MirroredSparkline implements Widget { - - private static final int Y_LABEL_WIDTH = 4; - private static final Style DIM = Style.EMPTY.dim(); - private static final String CENTRE_SEPARATOR = "─"; - - private final long[] topData; - private final long[] bottomData; - private final Style topStyle; - private final Style bottomStyle; - private final Long max; - private final Block block; - private final Sparkline.BarSet barSet; - private final boolean showYAxis; - private final String[] xLabels; - private final LongFunction<String> yLabelFormatter; - - private MirroredSparkline(Builder builder) { - this.topData = builder.topData; - this.bottomData = builder.bottomData; - this.topStyle = builder.topStyle; - this.bottomStyle = builder.bottomStyle; - this.max = builder.max; - this.block = builder.block; - this.barSet = builder.barSet; - this.showYAxis = builder.showYAxis; - this.xLabels = builder.xLabels; - this.yLabelFormatter = builder.yLabelFormatter; - } - - /** - * Creates a new builder. - * - * @return a new Builder - */ - public static Builder builder() { - return new Builder(); - } - - @Override - public void render(Rect area, Buffer buffer) { - if (area.isEmpty()) { - return; - } - - Rect inner = area; - if (block != null) { - block.render(area, buffer); - inner = block.inner(area); - } - - if (inner.isEmpty()) { - return; - } - - int innerH = inner.height(); - int innerW = inner.width(); - - boolean hasXAxis = xLabels != null && xLabels.length > 0; - // Reserve one row at the bottom for x-axis labels when configured - int chartBodyRows = hasXAxis ? Math.max(2, innerH - 1) : innerH; - int halfH = Math.max(1, (chartBodyRows - 1) / 2); - int centerRow = halfH; - - int yLabelW = showYAxis ? Y_LABEL_WIDTH : 0; - int chartW = Math.max(1, innerW - yLabelW); - - int dataLen = Math.max(topData.length, bottomData.length); - int ticks = Math.min(dataLen, chartW); - - long effectiveMax = computeMax(); - - // --- Bar rows --- - for (int r = 0; r < chartBodyRows; r++) { - int y = inner.y() + r; - - if (showYAxis) { - String label; - if (r == 0 || r == chartBodyRows - 1) { - label = formatYLabel(effectiveMax); - } else if (r == centerRow) { - label = " 0"; - } else { - label = " "; - } - buffer.setString(inner.x(), y, label, DIM); - } - - for (int t = 0; t < ticks; t++) { - int x = inner.x() + yLabelW + t; - int dataIdx = dataLen - ticks + t; - long topVal = dataIdx >= 0 && dataIdx < topData.length ? topData[dataIdx] : 0; - long botVal = dataIdx >= 0 && dataIdx < bottomData.length ? bottomData[dataIdx] : 0; - - String ch; - Style style; - - if (r < centerRow) { - // Top series: bars grow upward from the centre - int rowOffset = centerRow - 1 - r; // 0 at the row nearest the centre - long barPx = topVal * halfH * 8 / effectiveMax; - long threshold = (long) rowOffset * 8; - if (barPx >= threshold + 8) { - ch = barSet.full(); - } else if (barPx > threshold) { - ch = barSet.symbolForLevel((double) (barPx - threshold) / 8.0); - } else { - ch = barSet.empty(); - } - style = topStyle; - } else if (r == centerRow) { - ch = CENTRE_SEPARATOR; - style = DIM; - } else { - // Bottom series: bars grow downward from the centre - int rowOffset = r - centerRow - 1; // 0 at the row nearest the centre - long barPx = botVal * halfH * 8 / effectiveMax; - long threshold = (long) rowOffset * 8; - if (barPx >= threshold + 8) { - ch = barSet.full(); - } else if (barPx > threshold) { - ch = barSet.symbolForLevel((double) (barPx - threshold) / 8.0); - } else { - ch = barSet.empty(); - } - style = bottomStyle; - } - - buffer.setString(x, y, ch, style); - } - } - - // --- X-axis label row --- - if (hasXAxis) { - int xAxisY = inner.y() + chartBodyRows; - char[] xChars = new char[chartW]; - for (int i = 0; i < chartW; i++) { - xChars[i] = ' '; - } - // Distribute labels evenly across the tick range - for (int li = 0; li < xLabels.length; li++) { - String lbl = xLabels[li]; - double fraction = xLabels.length > 1 ? (double) li / (xLabels.length - 1) : 0; - int col = (int) Math.round(fraction * (ticks - 1)); - // Right-align the last label so it doesn't run past the right edge - int start = li == xLabels.length - 1 - ? Math.max(0, col - lbl.length() + 1) - : col; - for (int k = 0; k < lbl.length() && start + k < chartW; k++) { - xChars[start + k] = lbl.charAt(k); - } - } - if (showYAxis) { - buffer.setString(inner.x(), xAxisY, " ".repeat(yLabelW), DIM); - } - buffer.setString(inner.x() + yLabelW, xAxisY, new String(xChars), DIM); - } - } - - private String formatYLabel(long value) { - if (yLabelFormatter != null) { - String s = yLabelFormatter.apply(value); - if (s.length() >= Y_LABEL_WIDTH) { - return s.substring(0, Y_LABEL_WIDTH); - } - return " ".repeat(Y_LABEL_WIDTH - s.length()) + s; - } - return value > 9999 ? "999+" : String.format("%4d", value); - } - - private long computeMax() { - if (max != null) { - return Math.max(1, max); - } - long m = 1; - for (long v : topData) { - m = Math.max(m, v); - } - for (long v : bottomData) { - m = Math.max(m, v); - } - return m; - } - - /** - * Builder for {@link MirroredSparkline}. - */ - public static final class Builder { - private long[] topData = new long[0]; - private long[] bottomData = new long[0]; - private Style topStyle = Style.EMPTY; - private Style bottomStyle = Style.EMPTY; - private Long max; - private Block block; - private Sparkline.BarSet barSet = Sparkline.BarSet.NINE_LEVELS; - private boolean showYAxis = true; - private String[] xLabels; - private LongFunction<String> yLabelFormatter; - - private Builder() { - } - - /** - * Sets the top series data (bars grow upward from centre). - * - * @param data the data values - * @return this builder - */ - public Builder topData(long... data) { - this.topData = data != null ? data.clone() : new long[0]; - return this; - } - - /** - * Sets the top series data from a list (bars grow upward from centre). - * - * @param data the data values - * @return this builder - */ - public Builder topData(List<Long> data) { - this.topData = data == null ? new long[0] : data.stream().mapToLong(Long::longValue).toArray(); - return this; - } - - /** - * Sets the bottom series data (bars grow downward from centre). - * - * @param data the data values - * @return this builder - */ - public Builder bottomData(long... data) { - this.bottomData = data != null ? data.clone() : new long[0]; - return this; - } - - /** - * Sets the bottom series data from a list (bars grow downward from centre). - * - * @param data the data values - * @return this builder - */ - public Builder bottomData(List<Long> data) { - this.bottomData = data == null ? new long[0] : data.stream().mapToLong(Long::longValue).toArray(); - return this; - } - - /** - * Sets the style for the top series bars. - * - * @param style the style - * @return this builder - */ - public Builder topStyle(Style style) { - this.topStyle = style != null ? style : Style.EMPTY; - return this; - } - - /** - * Sets the style for the bottom series bars. - * - * @param style the style - * @return this builder - */ - public Builder bottomStyle(Style style) { - this.bottomStyle = style != null ? style : Style.EMPTY; - return this; - } - - /** - * Sets an explicit maximum value for scaling both series. When not set the maximum value across both datasets - * is used. - * - * @param max the maximum value - * @return this builder - */ - public Builder max(long max) { - this.max = max; - return this; - } - - /** - * Clears an explicit maximum, reverting to auto-scaling from the data. - * - * @return this builder - */ - public Builder autoMax() { - this.max = null; - return this; - } - - /** - * Wraps the chart in a block (border + optional title). - * - * @param block the block - * @return this builder - */ - public Builder block(Block block) { - this.block = block; - return this; - } - - /** - * Sets the bar symbol set used for sub-pixel rendering. - * - * @param barSet the bar set - * @return this builder - */ - public Builder barSet(Sparkline.BarSet barSet) { - this.barSet = barSet != null ? barSet : Sparkline.BarSet.NINE_LEVELS; - return this; - } - - /** - * Controls whether a Y-axis label column is rendered on the left. Shows the shared maximum at the top and - * bottom rows and {@code 0} at the centre row. Defaults to {@code true}. - * - * @param show whether to show the y-axis labels - * @return this builder - */ - public Builder showYAxis(boolean show) { - this.showYAxis = show; - return this; - } - - /** - * Sets the x-axis labels rendered as a single row below the chart body. Labels are distributed evenly across - * the data range. The last label is right-aligned at its position so it does not overflow the right edge. - * <p> - * Example: {@code xLabels("-60s", "-45s", "-30s", "-15s", "now")} - * - * @param labels the labels, distributed left-to-right - * @return this builder - */ - public Builder xLabels(String... labels) { - this.xLabels = labels != null ? labels.clone() : null; - return this; - } - - /** - * Sets a custom formatter for Y-axis max labels. The function receives the max value and should return a short - * string (up to 4 chars). When not set, values are formatted as integers with {@code 999+} for values above - * 9999. - * - * @param formatter the formatter function - * @return this builder - */ - public Builder yLabelFormatter(LongFunction<String> formatter) { - this.yLabelFormatter = formatter; - return this; - } - - /** - * Builds the widget. - * - * @return a new MirroredSparkline - */ - public MirroredSparkline build() { - return new MirroredSparkline(this); - } - } -} diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java index b8f38e7a358f..748ff2980acd 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java @@ -16,10 +16,7 @@ */ package org.apache.camel.dsl.jbang.core.commands.tui; -import java.lang.reflect.Constructor; - import dev.tamboui.backend.jline3.JLineBackend; -import dev.tamboui.terminal.Backend; import dev.tamboui.tui.TuiConfig; import dev.tamboui.tui.TuiRunner; import org.apache.camel.dsl.jbang.core.common.EnvironmentHelper; @@ -33,23 +30,9 @@ final class TuiBackendHelper { static TuiRunner createTuiRunner() throws Exception { Terminal activeTerminal = EnvironmentHelper.getActiveTerminal(); if (activeTerminal != null) { - Backend backend = createBackendForTerminal(activeTerminal); - if (backend != null) { - return TuiRunner.create(TuiConfig.builder().backend(backend).mouseCapture(true).build()); - } + JLineBackend backend = new JLineBackend(activeTerminal); + return TuiRunner.create(TuiConfig.builder().backend(backend).mouseCapture(true).build()); } return TuiRunner.create(TuiConfig.builder().mouseCapture(true).build()); } - - private static Backend createBackendForTerminal(Terminal terminal) { - try { - Constructor<JLineBackend> ctor = JLineBackend.class.getDeclaredConstructor(Terminal.class); - return ctor.newInstance(terminal); - } catch (NoSuchMethodException e) { - // JLineBackend(Terminal) not available in this version, fall back - return null; - } catch (Exception e) { - return null; - } - } } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java index 3fe39d4bb64c..d7335aab1f9e 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java @@ -31,7 +31,6 @@ import dev.tamboui.text.Span; import org.apache.camel.dsl.jbang.core.common.ProcessHelper; import org.apache.camel.support.PatternHelper; import org.apache.camel.util.FileUtil; -import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.json.JsonObject; import org.apache.camel.util.json.Jsoner; @@ -43,18 +42,6 @@ final class TuiHelper { private TuiHelper() { } - /** - * Eagerly load classes used by the TUI input reader daemon thread and picocli post-processing. Without this, during - * JVM shutdown the classloader may already be closing while the input reader thread is still trying to load these - * classes lazily — causing ClassNotFoundException stack traces on exit. - */ - static void preloadClasses(ClassLoader cl) { - ObjectHelper.loadClass("dev.tamboui.tui.event.KeyModifiers", cl); - ObjectHelper.loadClass("dev.tamboui.tui.event.KeyEvent", cl); - ObjectHelper.loadClass("dev.tamboui.tui.event.KeyCode", cl); - ObjectHelper.loadClass("picocli.CommandLine$IExitCodeGenerator", cl); - } - /** * Find PIDs of running Camel integrations matching the given name pattern. */ diff --git a/parent/pom.xml b/parent/pom.xml index d651ee9c7c0b..b2e7388b6d13 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -504,7 +504,7 @@ <swagger-request-validator-version>2.46.1</swagger-request-validator-version> <stringtemplate-version>4.3.4</stringtemplate-version> <tahu-version>1.0.19</tahu-version> - <tamboui-version>0.3.0</tamboui-version> + <tamboui-version>0.4.0</tamboui-version> <testcontainers-version>2.0.5</testcontainers-version> <thymeleaf-version>3.1.5.RELEASE</thymeleaf-version> <tika-version>3.3.1</tika-version>
