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();
         }

Reply via email to