This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch ws in repository https://gitbox.apache.org/repos/asf/camel.git
commit 350fa11f9c8bdd0a23d8656df4a6185d0927c16f Author: Claus Ibsen <[email protected]> AuthorDate: Tue Jan 21 10:56:48 2025 +0100 CAMEL-21637: camel-jbang - Commands in watch mode should wait for user input to exit which makes them also work in shell mode. --- .../dsl/jbang/core/commands/CamelCommand.java | 2 +- .../dsl/jbang/core/commands/CommandHelper.java | 25 +++++++++++++++++++--- .../camel/dsl/jbang/core/commands/Shell.java | 15 ++++++++----- .../core/commands/action/ActionWatchCommand.java | 23 ++++++++++++++++++-- .../jbang/core/commands/action/CamelLogAction.java | 14 ++++++++++-- .../core/commands/action/CamelReceiveAction.java | 16 +++++++++++--- .../core/commands/action/CamelTraceAction.java | 14 ++++++++++-- .../core/commands/process/ProcessWatchCommand.java | 23 ++++++++++++++++++-- 8 files changed, 112 insertions(+), 20 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java index 7667dbefc4b..188c9abd295 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java @@ -123,7 +123,7 @@ public abstract class CamelCommand implements Callable<Integer> { protected Printer printer() { var out = getMain().getOut(); - CommandHelper.SetPrinter(out); + CommandHelper.setPrinter(out); return out; } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java index 464e2e9019a..c14cf3bfc24 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java @@ -23,16 +23,16 @@ import org.apache.camel.util.FileUtil; public final class CommandHelper { - private static ThreadLocal<Printer> printerAssociation = new ThreadLocal<>(); + private static final ThreadLocal<Printer> printerAssociation = new ThreadLocal<>(); private CommandHelper() { } - public static Printer GetPrinter() { + public static Printer getPrinter() { return printerAssociation.get(); } - public static void SetPrinter(Printer out) { + public static void setPrinter(Printer out) { printerAssociation.set(out); } @@ -53,4 +53,23 @@ public final class CommandHelper { } } } + + /** + * A background task that reads from console, and can be used to signal when user has entered or pressed ctrl + c / + * ctrl + d + */ + public static class ReadConsoleTask implements Runnable { + + private final Runnable listener; + + public ReadConsoleTask(Runnable listener) { + this.listener = listener; + } + + @Override + public void run() { + System.console().readLine(); + listener.run(); + } + } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java index 9add8063153..f3c95a1563b 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java @@ -42,7 +42,7 @@ import picocli.shell.jline3.PicocliCommands; @CommandLine.Command(name = "shell", description = "Interactive Camel JBang shell. Hit @|magenta <TAB>|@ to see available commands.", - footer = "Press Ctrl-D to exit.") + footer = "Press Ctrl-C to exit.") public class Shell extends CamelCommand { public Shell(CamelJBangMain main) { @@ -86,17 +86,22 @@ public class Shell extends CamelCommand { String prompt = "camel> "; String rightPrompt = null; - // start the shell and process input until the user quits with Ctrl-D + // start the shell and process input until the user quits with Ctrl-C or Ctrl-D String line; - while (true) { + boolean run = true; + while (run) { try { systemRegistry.cleanUp(); line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null); systemRegistry.execute(line); + } catch (SystemRegistryImpl.UnknownCommandException e) { + // ignore } catch (UserInterruptException e) { - // Ignore + // ctrl + c is pressed so exit + run = false; } catch (EndOfFileException e) { - break; + // ctrl + d is pressed so exit + run = false; } catch (Exception e) { systemRegistry.trace(e); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java index 0e979c4df96..ae1baebd033 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/ActionWatchCommand.java @@ -16,7 +16,11 @@ */ package org.apache.camel.dsl.jbang.core.commands.action; +import java.util.concurrent.atomic.AtomicBoolean; + import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.CommandHelper; +import org.apache.camel.util.StopWatch; import org.fusesource.jansi.Ansi; import org.fusesource.jansi.AnsiConsole; import picocli.CommandLine; @@ -27,6 +31,8 @@ abstract class ActionWatchCommand extends ActionBaseCommand { description = "Execute periodically and showing output fullscreen") boolean watch; + private CommandHelper.ReadConsoleTask waitUserTask; + public ActionWatchCommand(CamelJBangMain main) { super(main); } @@ -34,14 +40,27 @@ abstract class ActionWatchCommand extends ActionBaseCommand { @Override public Integer doCall() throws Exception { int exit; + final AtomicBoolean running = new AtomicBoolean(true); if (watch) { + Thread t = new Thread(() -> { + waitUserTask = new CommandHelper.ReadConsoleTask(() -> running.set(false)); + waitUserTask.run(); + }, "WaitForUser"); + t.start(); do { exit = doWatchCall(); if (exit == 0) { // use 2-sec delay in watch mode - Thread.sleep(2000); + try { + StopWatch watch = new StopWatch(); + while (running.get() && watch.taken() < 2000) { + Thread.sleep(100); + } + } catch (Exception e) { + running.set(false); + } } - } while (exit == 0); + } while (exit == 0 && running.get()); } else { exit = doWatchCall(); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java index 31b282c6eef..e390af4cf48 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java @@ -34,10 +34,12 @@ import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import org.apache.camel.catalog.impl.TimePatternConverter; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.CommandHelper; import org.apache.camel.dsl.jbang.core.common.CommandLineHelper; import org.apache.camel.dsl.jbang.core.common.ProcessHelper; import org.apache.camel.util.StopWatch; @@ -56,6 +58,8 @@ public class CamelLogAction extends ActionBaseCommand { private static final String TIMESTAMP_MAIN = "yyyy-MM-dd HH:mm:ss.SSS"; + private CommandHelper.ReadConsoleTask waitUserTask; + public static class PrefixCompletionCandidates implements Iterable<String> { public PrefixCompletionCandidates() { @@ -78,7 +82,7 @@ public class CamelLogAction extends ActionBaseCommand { boolean timestamp = true; @CommandLine.Option(names = { "--follow" }, defaultValue = "true", - description = "Keep following and outputting new log lines (use ctrl + c to exit).") + description = "Keep following and outputting new log lines (press enter to exit).") boolean follow = true; @CommandLine.Option(names = { "--startup" }, defaultValue = "false", @@ -165,6 +169,12 @@ public class CamelLogAction extends ActionBaseCommand { if (follow) { boolean waitMessage = true; + final AtomicBoolean running = new AtomicBoolean(true); + Thread t = new Thread(() -> { + waitUserTask = new CommandHelper.ReadConsoleTask(() -> running.set(false)); + waitUserTask.run(); + }, "WaitForUser"); + t.start(); StopWatch watch = new StopWatch(); do { if (rows.isEmpty()) { @@ -188,7 +198,7 @@ public class CamelLogAction extends ActionBaseCommand { Thread.sleep(100); } } - } while (true); + } while (running.get()); } return 0; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java index 237555fc1e2..3e1cb212840 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import com.github.freva.asciitable.AsciiTable; @@ -41,6 +42,7 @@ import com.github.freva.asciitable.HorizontalAlign; import com.github.freva.asciitable.OverflowBehaviour; import org.apache.camel.catalog.impl.TimePatternConverter; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.CommandHelper; import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates; import org.apache.camel.dsl.jbang.core.common.ProcessHelper; import org.apache.camel.util.FileUtil; @@ -63,6 +65,8 @@ public class CamelReceiveAction extends ActionBaseCommand { private static final int NAME_MAX_WIDTH = 25; private static final int NAME_MIN_WIDTH = 10; + private CommandHelper.ReadConsoleTask waitUserTask; + public static class PrefixCompletionCandidates implements Iterable<String> { public PrefixCompletionCandidates() { @@ -102,7 +106,7 @@ public class CamelReceiveAction extends ActionBaseCommand { String sort; @CommandLine.Option(names = { "--follow" }, defaultValue = "true", - description = "Keep following and outputting new messages (use ctrl + c to exit).") + description = "Keep following and outputting new messages (press enter to exit).") boolean follow = true; @CommandLine.Option(names = { "--prefix" }, defaultValue = "auto", @@ -393,9 +397,15 @@ public class CamelReceiveAction extends ActionBaseCommand { if (follow) { boolean waitMessage = true; - StopWatch watch = new StopWatch(); + final AtomicBoolean running = new AtomicBoolean(true); + Thread t = new Thread(() -> { + waitUserTask = new CommandHelper.ReadConsoleTask(() -> running.set(false)); + waitUserTask.run(); + }, "WaitForUser"); + t.start(); boolean more = true; boolean init = true; + StopWatch watch = new StopWatch(); do { if (pids.isEmpty()) { if (waitMessage) { @@ -425,7 +435,7 @@ public class CamelReceiveAction extends ActionBaseCommand { break; } } - } while (more); + } while (more && running.get()); } return 0; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java index 61a08e4ef78..859efc1ce49 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import com.github.freva.asciitable.AsciiTable; @@ -42,6 +43,7 @@ import com.github.freva.asciitable.HorizontalAlign; import com.github.freva.asciitable.OverflowBehaviour; import org.apache.camel.catalog.impl.TimePatternConverter; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.CommandHelper; import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates; import org.apache.camel.dsl.jbang.core.common.ProcessHelper; import org.apache.camel.util.IOHelper; @@ -64,6 +66,8 @@ public class CamelTraceAction extends ActionBaseCommand { private static final int NAME_MAX_WIDTH = 25; private static final int NAME_MIN_WIDTH = 10; + private CommandHelper.ReadConsoleTask waitUserTask; + public static class PrefixCompletionCandidates implements Iterable<String> { public PrefixCompletionCandidates() { @@ -107,7 +111,7 @@ public class CamelTraceAction extends ActionBaseCommand { boolean ago; @CommandLine.Option(names = { "--follow" }, defaultValue = "true", - description = "Keep following and outputting new traces (use ctrl + c to exit).") + description = "Keep following and outputting new traces (press enter to exit).") boolean follow = true; @CommandLine.Option(names = { "--prefix" }, defaultValue = "auto", completionCandidates = PrefixCompletionCandidates.class, @@ -376,7 +380,13 @@ public class CamelTraceAction extends ActionBaseCommand { if (follow) { boolean waitMessage = true; + final AtomicBoolean running = new AtomicBoolean(true); StopWatch watch = new StopWatch(); + Thread t = new Thread(() -> { + waitUserTask = new CommandHelper.ReadConsoleTask(() -> running.set(false)); + waitUserTask.run(); + }, "WaitForUser"); + t.start(); boolean more = true; do { if (pids.isEmpty()) { @@ -402,7 +412,7 @@ public class CamelTraceAction extends ActionBaseCommand { break; } } - } while (more); + } while (more && running.get()); } return 0; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessWatchCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessWatchCommand.java index 5ac45dabdfd..d9559a9f5a1 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessWatchCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessWatchCommand.java @@ -16,7 +16,11 @@ */ package org.apache.camel.dsl.jbang.core.commands.process; +import java.util.concurrent.atomic.AtomicBoolean; + import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.CommandHelper; +import org.apache.camel.util.StopWatch; import org.fusesource.jansi.Ansi; import org.fusesource.jansi.AnsiConsole; import picocli.CommandLine; @@ -30,6 +34,8 @@ abstract class ProcessWatchCommand extends ProcessBaseCommand { description = "Execute periodically and showing output fullscreen") boolean watch; + private CommandHelper.ReadConsoleTask waitUserTask; + public ProcessWatchCommand(CamelJBangMain main) { super(main); } @@ -37,15 +43,28 @@ abstract class ProcessWatchCommand extends ProcessBaseCommand { @Override public Integer doCall() throws Exception { int exit; + final AtomicBoolean running = new AtomicBoolean(true); if (watch) { + Thread t = new Thread(() -> { + waitUserTask = new CommandHelper.ReadConsoleTask(() -> running.set(false)); + waitUserTask.run(); + }, "WaitForUser"); + t.start(); do { autoClearScreen(); exit = doProcessWatchCall(); if (exit == 0) { // use 2-sec delay in watch mode - Thread.sleep(2000); + try { + StopWatch watch = new StopWatch(); + while (running.get() && watch.taken() < 2000) { + Thread.sleep(100); + } + } catch (Exception e) { + running.set(false); + } } - } while (exit == 0); + } while (exit == 0 && running.get()); } else { exit = doProcessWatchCall(); }
