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]