This is an automated email from the ASF dual-hosted git repository.

clebertsuconic pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/artemis.git


The following commit(s) were added to refs/heads/main by this push:
     new d0a41691d3 ARTEMIS-5951 Proper CTRL-C handling on 'artemis shell'
d0a41691d3 is described below

commit d0a41691d3fe28aa2224c178812144e51d022ae3
Author: Clebert Suconic <[email protected]>
AuthorDate: Thu Mar 12 11:03:54 2026 -0400

    ARTEMIS-5951 Proper CTRL-C handling on 'artemis shell'
---
 .../org/apache/activemq/artemis/cli/Shell.java     | 16 ++++-
 .../artemis/cli/commands/ActionContext.java        |  3 +
 .../artemis/cli/commands/InputAbstract.java        | 76 +++++++++++++++++++---
 .../artemis/cli/commands/messages/Consumer.java    | 29 ++++++---
 .../artemis/cli/commands/messages/Producer.java    | 14 ++--
 docs/user-manual/versions.adoc                     |  1 +
 6 files changed, 115 insertions(+), 24 deletions(-)

diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Shell.java 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Shell.java
index 7f5daa45da..1e06c93169 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Shell.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Shell.java
@@ -22,6 +22,7 @@ import java.nio.file.Paths;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
 
+import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.Connect;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
 import org.jline.console.SystemRegistry;
@@ -101,7 +102,17 @@ public class Shell implements Runnable {
          PicocliCommands picocliCommands = new PicocliCommands(commandLine);
 
          Parser parser = new DefaultParser();
-         try (Terminal terminal = TerminalBuilder.terminal()) {
+         try (Terminal terminal = TerminalBuilder.builder()
+               .nativeSignals(true)
+               .build()) {
+            // Capture the main shell thread for signal handling
+            Thread shellThread = Thread.currentThread();
+
+            // Handle CTRL-C by interrupting the shell thread
+            terminal.handle(Terminal.Signal.INT, signal -> {
+               System.out.println("\n....Interrupted by the user.");
+               shellThread.interrupt();
+            });
             SystemRegistry systemRegistry = new SystemRegistryImpl(parser, 
terminal, workDir, null);
             systemRegistry.setCommandRegistries(picocliCommands);
             systemRegistry.register("help", picocliCommands);
@@ -113,6 +124,9 @@ public class Shell implements Runnable {
                .variable(LineReader.LIST_MAX, 50)   // max tab completion 
candidates
                .build();
             factory.setTerminal(terminal);
+            if (ActionContext.system() != null) {
+               ActionContext.system().lineReader = reader;
+            }
 
             String rightPrompt = null;
 
diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionContext.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionContext.java
index 51b16d6b16..205006a937 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionContext.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionContext.java
@@ -19,6 +19,8 @@ package org.apache.activemq.artemis.cli.commands;
 import java.io.InputStream;
 import java.io.PrintStream;
 
+import org.jline.reader.LineReader;
+
 public class ActionContext {
 
    public ActionContext(InputStream in, PrintStream out, PrintStream err) {
@@ -34,6 +36,7 @@ public class ActionContext {
    public InputStream in;
    public PrintStream out;
    public PrintStream err;
+   public LineReader lineReader;
 
    private static ThreadLocal<ActionContext> contextThreadLocal = 
ThreadLocal.withInitial(() -> new ActionContext());
 
diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
index 375d4d803e..e76f9da2d1 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
@@ -17,13 +17,70 @@
 
 package org.apache.activemq.artemis.cli.commands;
 
+import java.io.InputStream;
+import java.io.PrintStream;
 import java.util.Scanner;
 
+import org.jline.reader.LineReader;
 import picocli.CommandLine.Option;
 
 public class InputAbstract extends ActionAbstract {
 
-   private Scanner scanner;
+   public interface InputReader {
+      String readLine(String prompt);
+      String readPassword(String prompt);
+   }
+
+   private class ScanReader implements InputReader {
+      ScanReader(InputStream inputStream, PrintStream promptStream) {
+         this.scanner = new Scanner(inputStream);
+         this.promptStream = promptStream;
+      }
+
+      Scanner scanner;
+      PrintStream promptStream;
+
+
+      @Override
+      public String readLine(String prompt) {
+         promptStream.println(prompt);
+         return scanner.nextLine();
+      }
+
+      @Override
+      public String readPassword(String prompt) {
+         promptStream.println(prompt);
+         char[] typedPassword = System.console().readPassword();
+         if (typedPassword == null) {
+            return null;
+         } else {
+            return new String(typedPassword);
+         }
+      }
+   }
+
+
+   private class JLineReader implements InputReader {
+      JLineReader(LineReader reader) {
+         this.reader = reader;
+      }
+
+      public LineReader reader;
+
+      @Override
+      public String readLine(String prompt) {
+         return reader.readLine(prompt);
+      }
+
+      @Override
+      public String readPassword(String prompt) {
+         return reader.readLine(prompt, '*');
+      }
+   }
+
+
+   InputReader lineReader;
+
 
    private static boolean inputEnabled = false;
 
@@ -101,8 +158,8 @@ public class InputAbstract extends ActionAbstract {
       getActionContext().out.println();
       do {
          getActionContext().out.println(propertyName + ":");
-         getActionContext().out.println(prompt);
-         inputStr = scanner.nextLine();
+         inputStr = lineReader.readLine(prompt);
+
          if (!acceptNull && inputStr.trim().isEmpty()) {
             getActionContext().out.println("Invalid Entry!");
          } else {
@@ -124,17 +181,14 @@ public class InputAbstract extends ActionAbstract {
       getActionContext().out.println();
       do {
          getActionContext().out.println(propertyName + ": is mandatory with 
this configuration:");
-         getActionContext().out.println(prompt);
-         char[] chars = System.console().readPassword();
+         inputStr = lineReader.readPassword(prompt + "\n");
 
          // could be null if the user input something weird like Ctrl-d
-         if (chars == null) {
+         if (inputStr == null) {
             getActionContext().out.println("Invalid Entry!");
             continue;
          }
 
-         inputStr = new String(chars);
-
          if (inputStr.trim().isEmpty()) {
             getActionContext().out.println("Invalid Entry!");
          } else {
@@ -150,7 +204,11 @@ public class InputAbstract extends ActionAbstract {
    public Object execute(ActionContext context) throws Exception {
       super.execute(context);
 
-      this.scanner = new Scanner(context.in);
+      if (context.lineReader != null) {
+         this.lineReader = new JLineReader(context.lineReader);
+      } else {
+         this.lineReader = new ScanReader(context.in, context.out);
+      }
 
       return null;
    }
diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Consumer.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Consumer.java
index 1965d893f6..f5af2227d3 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Consumer.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Consumer.java
@@ -131,20 +131,29 @@ public class Consumer extends DestAbstract {
 
          long received = 0;
 
-         for (ConsumerThread thread : threadsArray) {
-            thread.join();
-            received += thread.getReceived();
+         try {
+            for (ConsumerThread thread : threadsArray) {
+               thread.join();
+               received += thread.getReceived();
+            }
+         } catch (InterruptedException e) {
+            // Interrupt any sub threads if an interrupt is captured
+            for (Thread t : threadsArray) t.interrupt();
+            throw e;
          }
 
-         if (serializer != null) {
-            serializer.stop();
-         }
+         return received;
+      } finally {
+         try {
+            if (serializer != null) {
+               serializer.stop();
+            }
 
-         if (outputStream != null) {
-            outputStream.close();
+            if (outputStream != null) {
+               outputStream.close();
+            }
+         } catch (Throwable ignored) {
          }
-
-         return received;
       }
    }
 
diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
index d4bbf23bea..2b905b3dea 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
@@ -229,11 +229,17 @@ public class Producer extends DestAbstract {
             }
 
             long messagesProduced = 0;
-            for (ProducerThread thread : threadsArray) {
-               thread.join();
-               messagesProduced += thread.getSentCount();
+            try {
+               for (ProducerThread thread : threadsArray) {
+                  thread.join();
+                  messagesProduced += thread.getSentCount();
+               }
+               return messagesProduced;
+            } catch (InterruptedException e) {
+               // Interrupt any sub threads if an interrupt is captured
+               for (Thread t : threadsArray) t.interrupt();
+               throw e;
             }
-            return messagesProduced;
          }
       }
    }
diff --git a/docs/user-manual/versions.adoc b/docs/user-manual/versions.adoc
index 5305e7764b..555ff02007 100644
--- a/docs/user-manual/versions.adoc
+++ b/docs/user-manual/versions.adoc
@@ -24,6 +24,7 @@ 
https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12315920&versio
 * https://issues.apache.org/jira/browse/ARTEMIS-5925[ARTEMIS-5925] - The Lock 
coordinator can now be applied to broker connections, supporting "Star Mirror" 
for enhanced HA topologies
 * https://issues.apache.org/jira/browse/ARTEMIS-5946[ARTEMIS-5946] - Update to 
Artemis Console 1.7.0
 * https://issues.apache.org/jira/browse/ARTEMIS-5941[ARTEMIS-5941] - Route 
wildcard subscriptions direct to demand bindings for improved performance
+* https://issues.apache.org/jira/browse/ARTEMIS-5941[ARTEMIS-5951] - CTRL-C is 
now properly handled on 'artemis shell'
 
 == Version 2.52.0
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to