You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2023/07/31 14:40:32 UTC

[activemq-artemis] branch main updated: ARTEMIS-4372 Implement Pico-cli and script auto-complete ARTEMIS-4375 Implement artemis shell using JLine3 integrated with auto-completion from picocli

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/activemq-artemis.git


The following commit(s) were added to refs/heads/main by this push:
     new 93ee61e35c ARTEMIS-4372 Implement Pico-cli and script auto-complete ARTEMIS-4375 Implement artemis shell using JLine3 integrated with auto-completion from picocli
93ee61e35c is described below

commit 93ee61e35c93564fb74b70f810a2a7a03dd0cb20
Author: Clebert Suconic <cl...@apache.org>
AuthorDate: Mon Jul 24 13:03:15 2023 -0400

    ARTEMIS-4372 Implement Pico-cli and script auto-complete
    ARTEMIS-4375 Implement artemis shell using JLine3 integrated with auto-completion from picocli
    
    This commit involves two JIRAs. One is adding PicoCLI and the next is Using JLine3 and implement a shell.
    I have tried to keep these commits separate but these changes became interdependent hence the two JIRAs are squashed in this commit.
---
 artemis-cli/pom.xml                                |  18 +-
 .../org/apache/activemq/artemis/cli/Artemis.java   | 241 +++++++-----
 .../org/apache/activemq/artemis/cli/Shell.java     | 162 ++++++++
 .../activemq/artemis/cli/commands/Action.java      |   3 +-
 .../artemis/cli/commands/ActionAbstract.java       |  28 +-
 .../artemis/cli/commands/AutoCompletion.java       |  56 +++
 .../artemis/cli/commands/Configurable.java         |  40 +-
 .../cli/commands/{Kill.java => Connect.java}       |  28 +-
 .../activemq/artemis/cli/commands/Create.java      | 146 ++++----
 .../cli/commands/{Kill.java => Disconnect.java}    |  22 +-
 .../activemq/artemis/cli/commands/HelpAction.java  |  62 ++--
 .../artemis/cli/commands/InputAbstract.java        |   4 +-
 .../artemis/cli/commands/InstallAbstract.java      |  32 +-
 .../apache/activemq/artemis/cli/commands/Kill.java |   2 +-
 .../apache/activemq/artemis/cli/commands/Mask.java |  21 +-
 .../activemq/artemis/cli/commands/OptionsUtil.java |  64 ----
 .../artemis/cli/commands/PrintVersion.java         |   2 +-
 .../apache/activemq/artemis/cli/commands/Run.java  |   8 +-
 .../apache/activemq/artemis/cli/commands/Stop.java |   2 +-
 .../activemq/artemis/cli/commands/Upgrade.java     |   4 +-
 .../{Kill.java => activation/ActivationGroup.java} |  31 +-
 .../activation/ActivationSequenceList.java         |  10 +-
 .../commands/activation/ActivationSequenceSet.java |  14 +-
 .../cli/commands/address/AddressAbstract.java      |  12 +-
 .../{Kill.java => address/AddressGroup.java}       |  30 +-
 .../cli/commands/address/CreateAddress.java        |   2 +-
 .../cli/commands/address/DeleteAddress.java        |   6 +-
 .../artemis/cli/commands/address/HelpAddress.java  |  63 ----
 .../artemis/cli/commands/address/ShowAddress.java  |   6 +-
 .../cli/commands/address/UpdateAddress.java        |   2 +-
 .../artemis/cli/commands/check/CheckAbstract.java  |   8 +-
 .../{Action.java => check/CheckGroup.java}         |  23 +-
 .../artemis/cli/commands/check/HelpCheck.java      |  63 ----
 .../artemis/cli/commands/check/NodeCheck.java      |  16 +-
 .../artemis/cli/commands/check/QueueCheck.java     |  13 +-
 .../artemis/cli/commands/messages/Browse.java      |   8 +-
 .../cli/commands/messages/ConnectionAbstract.java  | 125 +++++--
 .../ConnectionProtocol.java}                       |  35 +-
 .../artemis/cli/commands/messages/Consumer.java    |  16 +-
 .../cli/commands/messages/ConsumerThread.java      |   6 +-
 .../cli/commands/messages/DestAbstract.java        |  38 +-
 .../artemis/cli/commands/messages/Producer.java    |  22 +-
 .../cli/commands/messages/ProducerThread.java      |  20 +-
 .../artemis/cli/commands/messages/Transfer.java    |  50 +--
 .../commands/messages/perf/PerfClientCommand.java  |  63 ++--
 .../cli/commands/messages/perf/PerfCommand.java    |  32 +-
 .../messages/perf/PerfConsumerCommand.java         |  14 +-
 .../{Kill.java => messages/perf/PerfGroup.java}    |  31 +-
 .../messages/perf/PerfProducerCommand.java         |  31 +-
 .../artemis/cli/commands/queue/CreateQueue.java    |   2 +-
 .../artemis/cli/commands/queue/DeleteQueue.java    |  10 +-
 .../artemis/cli/commands/queue/HelpQueue.java      |  63 ----
 .../artemis/cli/commands/queue/PurgeQueue.java     |   6 +-
 .../artemis/cli/commands/queue/QueueAbstract.java  |  24 +-
 .../commands/{Kill.java => queue/QueueGroup.java}  |  31 +-
 .../artemis/cli/commands/queue/StatQueue.java      |  16 +-
 .../artemis/cli/commands/queue/UpdateQueue.java    |   2 +-
 .../artemis/cli/commands/tools/DBOption.java       |  47 ++-
 .../artemis/cli/commands/tools/DataAbstract.java   |  10 +-
 .../artemis/cli/commands/tools/DataGroup.java      |  43 +++
 .../artemis/cli/commands/tools/HelpData.java       |  66 ----
 .../artemis/cli/commands/tools/LockAbstract.java   |   6 +
 .../cli/commands/tools/OptionalLocking.java        |   4 +-
 .../artemis/cli/commands/tools/PrintData.java      |  16 +-
 .../cli/commands/tools/RecoverMessages.java        |  16 +-
 .../cli/commands/tools/journal/CompactJournal.java |   2 +-
 .../cli/commands/tools/journal/DecodeJournal.java  |  16 +-
 .../cli/commands/tools/journal/EncodeJournal.java  |  12 +-
 .../cli/commands/tools/journal/PerfJournal.java    |  22 +-
 .../cli/commands/tools/xml/XmlDataExporter.java    |   6 +-
 .../cli/commands/tools/xml/XmlDataImporter.java    |  24 +-
 .../artemis/cli/commands/user/AddUser.java         |   6 +-
 .../artemis/cli/commands/user/HelpUser.java        |  62 ----
 .../artemis/cli/commands/user/ListUser.java        |   7 +-
 .../artemis/cli/commands/user/PasswordAction.java  |   4 +-
 .../artemis/cli/commands/user/RemoveUser.java      |   2 +-
 .../artemis/cli/commands/user/ResetUser.java       |   6 +-
 .../artemis/cli/commands/user/UserAction.java      |   6 +-
 .../commands/{Kill.java => user/UserGroup.java}    |  31 +-
 .../commands/messages/ConnectionAbstractTest.java  |   3 +-
 .../org/apache/activemq/cli/test/ArtemisTest.java  | 140 +++----
 .../org/apache/activemq/cli/test/CheckTest.java    |  11 +-
 .../activemq/cli/test/OptionsValidationTest.java   | 115 ------
 .../src/main/resources/licenses/bin/LICENSE        |   6 +
 .../licenses/bin/licenses/LICENSE-jline.txt        |  34 ++
 docs/user-manual/en/SUMMARY.md                     |   1 +
 docs/user-manual/en/architecture.md                |   3 -
 docs/user-manual/en/embedding-activemq.md          |   5 +-
 docs/user-manual/en/using-cli.md                   | 409 +++++++++++++++++++++
 docs/user-manual/en/using-server.md                |   5 +
 pom.xml                                            |  35 +-
 .../artemis/tests/integration/cli/RecoverTest.java |   3 +-
 tests/smoke-tests/pom.xml                          |   3 +-
 tests/soak-tests/pom.xml                           |   4 -
 94 files changed, 1734 insertions(+), 1346 deletions(-)

diff --git a/artemis-cli/pom.xml b/artemis-cli/pom.xml
index c4b32a9f90..a213bc36a0 100644
--- a/artemis-cli/pom.xml
+++ b/artemis-cli/pom.xml
@@ -124,10 +124,22 @@
          <artifactId>jakarta.xml.bind-api</artifactId>
          <version>${jakarta.xml.bind-api.version}</version>
       </dependency>
-
       <dependency>
-         <groupId>com.github.rvesse</groupId>
-         <artifactId>airline</artifactId>
+         <groupId>info.picocli</groupId>
+         <artifactId>picocli</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>info.picocli</groupId>
+         <artifactId>picocli-shell-jline3</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>org.jline</groupId>
+         <artifactId>jline</artifactId>
+      </dependency>
+      <!-- Jansi is an optional dependency for jline, to provide a proper ANSI Terminal -->
+      <dependency>
+         <groupId>org.fusesource.jansi</groupId>
+         <artifactId>jansi</artifactId>
       </dependency>
       <dependency>
          <groupId>org.apache.commons</groupId>
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java
index f969bfd44b..9349fa83a6 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java
@@ -16,65 +16,46 @@
  */
 package org.apache.activemq.artemis.cli;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.util.Arrays;
 import java.util.List;
 
-import com.github.rvesse.airline.Cli;
-import com.github.rvesse.airline.builder.CliBuilder;
 import org.apache.activemq.artemis.cli.commands.Action;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.cli.commands.AutoCompletion;
 import org.apache.activemq.artemis.cli.commands.Create;
+import org.apache.activemq.artemis.cli.commands.Disconnect;
 import org.apache.activemq.artemis.cli.commands.HelpAction;
 import org.apache.activemq.artemis.cli.commands.InputAbstract;
 import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
 import org.apache.activemq.artemis.cli.commands.Kill;
 import org.apache.activemq.artemis.cli.commands.Mask;
 import org.apache.activemq.artemis.cli.commands.PrintVersion;
-import org.apache.activemq.artemis.cli.commands.Upgrade;
-import org.apache.activemq.artemis.cli.commands.activation.ActivationSequenceSet;
-import org.apache.activemq.artemis.cli.commands.check.HelpCheck;
-import org.apache.activemq.artemis.cli.commands.check.NodeCheck;
-import org.apache.activemq.artemis.cli.commands.check.QueueCheck;
-import org.apache.activemq.artemis.cli.commands.messages.Transfer;
-import org.apache.activemq.artemis.cli.commands.messages.perf.PerfClientCommand;
-import org.apache.activemq.artemis.cli.commands.messages.perf.PerfConsumerCommand;
-import org.apache.activemq.artemis.cli.commands.messages.perf.PerfProducerCommand;
-import org.apache.activemq.artemis.cli.commands.queue.StatQueue;
 import org.apache.activemq.artemis.cli.commands.Run;
 import org.apache.activemq.artemis.cli.commands.Stop;
-import org.apache.activemq.artemis.cli.commands.address.CreateAddress;
-import org.apache.activemq.artemis.cli.commands.address.DeleteAddress;
-import org.apache.activemq.artemis.cli.commands.address.HelpAddress;
-import org.apache.activemq.artemis.cli.commands.address.ShowAddress;
-import org.apache.activemq.artemis.cli.commands.address.UpdateAddress;
+import org.apache.activemq.artemis.cli.commands.Upgrade;
+import org.apache.activemq.artemis.cli.commands.activation.ActivationGroup;
+import org.apache.activemq.artemis.cli.commands.address.AddressGroup;
+import org.apache.activemq.artemis.cli.commands.check.CheckGroup;
 import org.apache.activemq.artemis.cli.commands.messages.Browse;
+import org.apache.activemq.artemis.cli.commands.Connect;
 import org.apache.activemq.artemis.cli.commands.messages.Consumer;
 import org.apache.activemq.artemis.cli.commands.messages.Producer;
-import org.apache.activemq.artemis.cli.commands.queue.CreateQueue;
-import org.apache.activemq.artemis.cli.commands.queue.DeleteQueue;
-import org.apache.activemq.artemis.cli.commands.queue.HelpQueue;
-import org.apache.activemq.artemis.cli.commands.queue.PurgeQueue;
-import org.apache.activemq.artemis.cli.commands.queue.UpdateQueue;
-import org.apache.activemq.artemis.cli.commands.activation.ActivationSequenceList;
-import org.apache.activemq.artemis.cli.commands.tools.HelpData;
-import org.apache.activemq.artemis.cli.commands.tools.PrintData;
-import org.apache.activemq.artemis.cli.commands.tools.RecoverMessages;
-import org.apache.activemq.artemis.cli.commands.tools.journal.CompactJournal;
-import org.apache.activemq.artemis.cli.commands.tools.journal.DecodeJournal;
-import org.apache.activemq.artemis.cli.commands.tools.journal.EncodeJournal;
+import org.apache.activemq.artemis.cli.commands.messages.Transfer;
+import org.apache.activemq.artemis.cli.commands.messages.perf.PerfGroup;
+import org.apache.activemq.artemis.cli.commands.queue.QueueGroup;
+import org.apache.activemq.artemis.cli.commands.tools.DataGroup;
 import org.apache.activemq.artemis.cli.commands.tools.journal.PerfJournal;
-import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataExporter;
-import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataImporter;
-import org.apache.activemq.artemis.cli.commands.user.AddUser;
-import org.apache.activemq.artemis.cli.commands.user.HelpUser;
-import org.apache.activemq.artemis.cli.commands.user.ListUser;
-import org.apache.activemq.artemis.cli.commands.user.RemoveUser;
-import org.apache.activemq.artemis.cli.commands.user.ResetUser;
+import org.apache.activemq.artemis.cli.commands.user.UserGroup;
 import org.apache.activemq.artemis.dto.ManagementContextDTO;
 import org.apache.activemq.artemis.dto.XmlUtil;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
 /**
  * Artemis is the main CLI entry point for managing/running a broker.
@@ -87,7 +68,26 @@ import org.apache.activemq.artemis.dto.XmlUtil;
  * Notice that this class should not use any logging as it's part of the bootstrap and using logging here could
  *        disrupt the order of bootstrapping on certain components (e.g. JMX being started from log4j)
  */
-public class Artemis {
+@Command(name = "artemis", description = "ActiveMQ Artemis Command Line")
+public class Artemis implements Runnable {
+
+   CommandLine commandLine;
+
+   public CommandLine getCommandLine() {
+      return commandLine;
+   }
+
+   public Artemis setCommandLine(CommandLine commandLine) {
+      this.commandLine = commandLine;
+      return this;
+   }
+
+   @Override
+   public void run() {
+      // We are running the shell by default.
+      // if you type ./artemis we will go straight to the shell
+      Shell.runShell(true);
+   }
 
    public static void main(String... args) throws Exception {
       String home = System.getProperty("artemis.home");
@@ -179,10 +179,7 @@ public class Artemis {
       } catch (RuntimeException | InvalidOptionsError re) {
          context.err.println(re.getMessage());
          context.out.println();
-
-         Cli<Action> parser = builder(null).build();
-
-         parser.parse("help").execute(context);
+         HelpAction.help(buildCommand(true, true), "help");
          return re;
       } finally {
          ActionContext.setSystem(new ActionContext());
@@ -194,68 +191,140 @@ public class Artemis {
     * Useful on test cases
     */
    private static Object internalExecute(File artemisHome, File artemisInstance, File etcFolder, String[] args) throws Exception {
-      return internalExecute(artemisHome, artemisInstance, etcFolder, args, ActionContext.system());
+      return internalExecute(artemisHome, artemisInstance, etcFolder, args, new ActionContext());
    }
 
    public static Object internalExecute(File artemisHome, File artemisInstance, File etcFolder, String[] args, ActionContext context) throws Exception {
-      Action action = builder(artemisInstance).build().parse(args);
-      action.setHomeValues(artemisHome, artemisInstance, etcFolder);
+      boolean isInstance = artemisInstance != null || System.getProperty("artemis.instance") != null;
+      CommandLine commandLine = buildCommand(isInstance, !isInstance);
+
+      Object userObject = parseAction(commandLine, args);
+
+      // Pico shouldn't allow generating a commandLine without an userObject.
+      // the following assert "should" never happen
+      assert userObject != null;
+
+      if (userObject instanceof Action) {
+         Action action = (Action) userObject;
+         action.setHomeValues(artemisHome, artemisInstance, etcFolder);
+         if (action.isVerbose()) {
+            context.out.print("Executing " + action.getClass().getName() + " ");
+            for (String arg : args) {
+               context.out.print(arg + " ");
+            }
+            context.out.println();
+            context.out.println("Home::" + action.getBrokerHome() + ", Instance::" + action.getBrokerInstance());
+         }
 
-      if (action.isVerbose()) {
-         context.out.print("Executing " + action.getClass().getName() + " ");
-         for (String arg : args) {
-            context.out.print(arg + " ");
+         try {
+            return action.execute(context);
+         } finally {
+            action.done();
+         }
+      } else {
+         if (userObject instanceof Runnable) {
+            ((Runnable) userObject).run();
+         } else {
+            throw new IllegalArgumentException(userObject.getClass() + " should implement either " + Action.class.getName() + " or " + Runnable.class.getName());
+         }
+      }
+      return null;
+   }
+
+   /*
+    Pico-cli traditionally would execute user objects that implement Runnable.
+    However as we used airline before, we needed parse for the proper action.
+    This method here is parsing the arg and find the proper user object in the hierarchy of sub-commands
+    and return it to the caller.
+    */
+   private static Object parseAction(CommandLine line, String[] args) {
+      CommandLine.ParseResult parseResult = line.parseArgs(args);
+      if (parseResult != null) {
+         while (parseResult.hasSubcommand()) {
+            parseResult = parseResult.subcommand();
          }
-         context.out.println();
-         context.out.println("Home::" + action.getBrokerHome() + ", Instance::" + action.getBrokerInstance());
       }
+      if (parseResult == null) {
+         throw new RuntimeException("Cannot match arg::" + Arrays.toString(args));
+      }
+      return parseResult.commandSpec().userObject();
+   }
+
+   public static CommandLine buildCommand(boolean includeInstanceCommands, boolean includeHomeCommands) {
+      return buildCommand(includeInstanceCommands, includeHomeCommands, false);
 
-      action.checkOptions(args);
-      return action.execute(context);
    }
 
-   private static CliBuilder<Action> builder(File artemisInstance) {
-      String instance = artemisInstance != null ? artemisInstance.getAbsolutePath() : System.getProperty("artemis.instance");
-      CliBuilder<Action> builder = Cli.<Action>builder("artemis").withDescription("ActiveMQ Artemis Command Line").
-         withCommand(HelpAction.class).withCommand(Producer.class).withCommand(Transfer.class).withCommand(Consumer.class).
-         withCommand(Browse.class).withCommand(Mask.class).withCommand(PrintVersion.class).withDefaultCommand(HelpAction.class);
-
-      builder.withGroup("perf").withDescription("Perf tools group (example ./artemis perf client)")
-         .withDefaultCommand(PerfClientCommand.class)
-         .withCommands(PerfProducerCommand.class, PerfConsumerCommand.class, PerfClientCommand.class);
-
-      builder.withGroup("check").withDescription("Check tools group (node|queue) (example ./artemis check node)").
-         withDefaultCommand(HelpCheck.class).withCommands(NodeCheck.class, QueueCheck.class);
-
-      builder.withGroup("queue").withDescription("Queue tools group (create|delete|update|stat|purge) (example ./artemis queue create)").
-         withDefaultCommand(HelpQueue.class).withCommands(CreateQueue.class, DeleteQueue.class, UpdateQueue.class, StatQueue.class, PurgeQueue.class);
-
-      builder.withGroup("address").withDescription("Address tools group (create|delete|update|show) (example ./artemis address create)").
-         withDefaultCommand(HelpAddress.class).withCommands(CreateAddress.class, DeleteAddress.class, UpdateAddress.class, ShowAddress.class);
-
-      if (instance != null) {
-         builder.withGroup("activation")
-            .withDescription("activation tools group (sync) (example ./artemis activation list)")
-            .withDefaultCommand(ActivationSequenceList.class)
-            .withCommands(ActivationSequenceList.class, ActivationSequenceSet.class);
-         builder.withGroup("data").withDescription("data tools group (print|imp|exp|encode|decode|compact|recover) (example ./artemis data print)").
-            withDefaultCommand(HelpData.class).withCommands(RecoverMessages.class, PrintData.class, XmlDataExporter.class, XmlDataImporter.class, DecodeJournal.class, EncodeJournal.class, CompactJournal.class);
-         builder.withGroup("user").withDescription("default file-based user management (add|rm|list|reset) (example ./artemis user list)").
-                 withDefaultCommand(HelpUser.class).withCommands(ListUser.class, AddUser.class, RemoveUser.class, ResetUser.class);
-         builder = builder.withCommands(Run.class, Stop.class, Kill.class, PerfJournal.class);
-      } else {
-         builder.withGroup("data").withDescription("data tools group (print|recover) (example ./artemis data print)").
-            withDefaultCommand(HelpData.class).withCommands(RecoverMessages.class, PrintData.class);
-         builder = builder.withCommands(Create.class, Upgrade.class);
+   public static CommandLine buildCommand(boolean includeInstanceCommands, boolean includeHomeCommands, boolean fromShell) {
+      Artemis artemis = new Artemis();
+
+      CommandLine commandLine = new CommandLine(artemis);
+      artemis.setCommandLine(commandLine);
+
+      HelpAction help = new HelpAction();
+      help.setCommandLine(commandLine);
+      commandLine.addSubcommand(help);
+
+      commandLine.addSubcommand(new AutoCompletion());
+
+      // we don't include the shell in the shell
+      if (!fromShell) {
+         commandLine.addSubcommand(new Shell(commandLine));
+      }
+
+      commandLine.addSubcommand(new Producer()).addSubcommand(new Transfer()).addSubcommand(new Consumer()).addSubcommand(new Browse()).addSubcommand(new Mask()).addSubcommand(new PrintVersion());
+
+      commandLine.addSubcommand(new PerfGroup(commandLine));
+      commandLine.addSubcommand(new CheckGroup(commandLine));
+      commandLine.addSubcommand(new QueueGroup(commandLine));
+      commandLine.addSubcommand(new AddressGroup(commandLine));
+
+      if (fromShell) {
+         commandLine.addSubcommand(new Connect());
+         commandLine.addSubcommand(new Disconnect());
       }
 
-      return builder;
+      if (includeInstanceCommands) {
+         commandLine.addSubcommand(new ActivationGroup(commandLine));
+         commandLine.addSubcommand(new DataGroup(commandLine));
+         commandLine.addSubcommand(new UserGroup(commandLine));
+
+         commandLine.addSubcommand(new Run());
+         commandLine.addSubcommand(new Stop());
+         commandLine.addSubcommand(new Kill());
+         commandLine.addSubcommand(new PerfJournal());
+      }
+
+      if (includeHomeCommands) {
+         if (!includeInstanceCommands) {
+            // Data is already present in InstanceCommands
+            commandLine.addSubcommand(new DataGroup(commandLine));
+         }
+         commandLine.addSubcommand(new Create());
+         commandLine.addSubcommand(new Upgrade());
+      }
+
+      return commandLine;
    }
 
    public static void printBanner(PrintStream out) throws Exception {
       copy(Artemis.class.getResourceAsStream("banner.txt"), out);
    }
 
+
+   public static String getNameFromBanner() throws Exception {
+      InputStream inputStream = Artemis.class.getResourceAsStream("banner.txt");
+      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+      String lastLine = "";
+      while (reader.ready()) {
+         String line = reader.readLine();
+         if (!line.trim().isEmpty()) {
+            lastLine = line;
+         }
+      }
+      return lastLine.trim();
+   }
+
    private static long copy(InputStream in, OutputStream out) throws Exception {
       try {
          byte[] buffer = new byte[1024];
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
new file mode 100644
index 0000000000..9cebfa67fa
--- /dev/null
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Shell.java
@@ -0,0 +1,162 @@
+/*
+ * 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.activemq.artemis.cli;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import org.apache.activemq.artemis.cli.commands.Connect;
+import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import org.jline.console.SystemRegistry;
+import org.jline.console.impl.SystemRegistryImpl;
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.MaskingCallback;
+import org.jline.reader.Parser;
+import org.jline.reader.UserInterruptException;
+import org.jline.reader.impl.DefaultParser;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.shell.jline3.PicocliCommands;
+
+@Command(name = "shell", description = "JLine3 shell helping using the CLI")
+public class Shell implements Runnable {
+
+   @CommandLine.Option(names = "--url", description = "It will be used for an initial connection if set.")
+   protected String brokerURL = ConnectionAbstract.DEFAULT_BROKER_URL;
+
+   @CommandLine.Option(names = "--user", description = "It will be used for an initial connection if set.")
+   protected String user;
+
+   @CommandLine.Option(names = "--password", description = "It will be used for an initial connection if set.")
+   protected String password;
+
+
+   private static String RED_UNICODE = "\u001B[31m";
+   private static String YELLOW_UNICODE = "\u001B[33m";
+   private static String CLEAR_UNICODE = "\u001B[0m";
+
+   public Shell(CommandLine commandLine) {
+   }
+
+   @Override
+   public void run() {
+      setInShell();
+      printBanner();
+      if (brokerURL != ConnectionAbstract.DEFAULT_BROKER_URL || user != null || password != null) {
+         Connect connect = new Connect();
+         connect.setUser(user).setPassword(password).setBrokerURL(brokerURL);
+         connect.run();
+      }
+      runShell(false);
+   }
+
+   private static ThreadLocal<AtomicBoolean> IN_SHELL = ThreadLocal.withInitial(() -> new AtomicBoolean(false));
+
+   public static boolean inShell() {
+      return IN_SHELL.get().get();
+   }
+
+   public static void setInShell() {
+      IN_SHELL.get().set(true);
+   }
+
+   public static void runShell(boolean printBanner) {
+      try {
+         setInShell();
+
+         boolean isInstance = System.getProperty("artemis.instance") != null;
+
+         Supplier<Path> workDir = () -> Paths.get(System.getProperty("user.dir"));
+
+         PicocliCommands.PicocliCommandsFactory factory = new PicocliCommands.PicocliCommandsFactory();
+
+         CommandLine commandLine = Artemis.buildCommand(isInstance, !isInstance, true);
+
+         PicocliCommands picocliCommands = new PicocliCommands(commandLine);
+
+         Parser parser = new DefaultParser();
+         try (Terminal terminal = TerminalBuilder.terminal()) {
+            SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, workDir, null);
+            systemRegistry.setCommandRegistries(picocliCommands);
+            systemRegistry.register("help", picocliCommands);
+
+            LineReader reader = LineReaderBuilder.builder()
+               .terminal(terminal)
+               .completer(systemRegistry.completer())
+               .parser(parser)
+               .variable(LineReader.LIST_MAX, 50)   // max tab completion candidates
+               .build();
+            factory.setTerminal(terminal);
+
+            String prompt = YELLOW_UNICODE + Artemis.getNameFromBanner() + " > " + CLEAR_UNICODE;
+            String rightPrompt = null;
+
+            if (printBanner) {
+               printBanner();
+            }
+
+            System.out.println("For a list of commands, type " + RED_UNICODE + "help" + CLEAR_UNICODE + " or press " + RED_UNICODE + "<TAB>" + CLEAR_UNICODE + ":");
+            System.out.println("Type " + RED_UNICODE + "exit" + CLEAR_UNICODE + " or press " + RED_UNICODE + "<CTRL-D>" + CLEAR_UNICODE + " to leave the session:");
+
+            // start the shell and process input until the user quits with Ctrl-D
+            String line;
+            while (true) {
+               try {
+                  // We build a new command every time, as they could have state from previous executions
+                  systemRegistry.setCommandRegistries(new PicocliCommands(Artemis.buildCommand(isInstance, !isInstance, true)));
+                  systemRegistry.cleanUp();
+                  line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null);
+                  systemRegistry.execute(line);
+               } catch (InterruptedException e) {
+                  e.printStackTrace();
+                  // Ignore
+               } catch (UserInterruptException userInterruptException) {
+                  // ignore
+               } catch (EndOfFileException e) {
+                  return;
+               } catch (Exception e) {
+                  systemRegistry.trace(e);
+               }
+            }
+         }
+      } catch (Throwable t) {
+         t.printStackTrace();
+      } finally {
+         IN_SHELL.get().set(false);
+      }
+
+   }
+
+   private static void printBanner() {
+      System.out.print(YELLOW_UNICODE);
+      try {
+         Artemis.printBanner(System.out);
+      } catch (Exception e) {
+         System.out.println("Error recovering the banner:");
+         e.printStackTrace();
+      }
+      System.out.print(CLEAR_UNICODE);
+   }
+
+}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java
index 5ed583754d..80ca0a9a8c 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java
@@ -26,9 +26,10 @@ public interface Action {
 
    Object execute(ActionContext context) throws Exception;
 
+   void done();
+
    String getBrokerInstance();
 
    String getBrokerHome();
 
-   void checkOptions(String[] options) throws InvalidOptionsError;
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionAbstract.java
index 6f038f4cc3..b20abe1fd5 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/ActionAbstract.java
@@ -22,7 +22,6 @@ import java.net.URI;
 import java.util.Collection;
 import java.util.Map;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.TransportConfiguration;
 import org.apache.activemq.artemis.core.config.Configuration;
 import org.apache.activemq.artemis.core.config.FileDeploymentManager;
@@ -30,14 +29,15 @@ import org.apache.activemq.artemis.core.config.impl.FileConfiguration;
 import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
 import org.apache.activemq.artemis.utils.ConfigurationHelper;
 import org.apache.activemq.artemis.utils.uri.SchemaConstants;
+import picocli.CommandLine.Option;
 
-public abstract class ActionAbstract implements Action {
+public abstract class ActionAbstract implements Action, Runnable {
 
    public static final String DEFAULT_BROKER_URL = "tcp://localhost:61616";
 
    public static final String DEFAULT_BROKER_ACCEPTOR = "artemis";
 
-   @Option(name = "--verbose", description = "Print additional information.")
+   @Option(names = "--verbose", description = "Print additional information.")
    public boolean verbose;
 
    // this could be changed by a test accessor for testing purposes.
@@ -205,6 +205,23 @@ public abstract class ActionAbstract implements Action {
       return brokerHome;
    }
 
+   @Override
+   public void run() {
+      try {
+         // this is used only by the Shell
+         // When  using the CLI outside of the shell the execute(ActionContext) will be used instead.
+         execute(getActionContext());
+      } catch (Throwable e) {
+         e.printStackTrace();
+      } finally {
+         done();
+      }
+   }
+
+   @Override
+   public void done() {
+   }
+
    @Override
    public Object execute(ActionContext context) throws Exception {
       this.actionContext = context;
@@ -213,9 +230,4 @@ public abstract class ActionAbstract implements Action {
       return null;
    }
 
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/AutoCompletion.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/AutoCompletion.java
new file mode 100644
index 0000000000..d54f70033c
--- /dev/null
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/AutoCompletion.java
@@ -0,0 +1,56 @@
+/*
+ * 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.activemq.artemis.cli.commands;
+
+import java.io.File;
+
+import org.apache.activemq.artemis.cli.Artemis;
+import picocli.AutoComplete;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "auto-complete", description = "Generates the auto complete script file to be used in bash or zsh.")
+public class AutoCompletion implements Runnable {
+
+   public AutoCompletion() {
+   }
+
+   @CommandLine.Parameters (description = "The generated auto-complete script", defaultValue = "auto-complete-artemis.sh")
+   File autoCompleteFile;
+
+   @Override
+   public void run() {
+      try {
+         CommandLine artemisCommand = Artemis.buildCommand(true, true);
+         AutoComplete.bash("artemis", autoCompleteFile, null, artemisCommand);
+         System.out.println("Type the following commands before you can use auto-complete:");
+         System.out.println("*******************************************************************************************************************************");
+         System.out.println("source " + autoCompleteFile.getAbsolutePath());
+         System.out.println("*******************************************************************************************************************************");
+
+      } catch (Throwable e) {
+         e.printStackTrace();
+      }
+   }
+
+   // I'm letting the possibility of calling AutoCompletion directly bypassing the artemis CLI.
+   public static void main(String[] args) {
+      CommandLine commandLine = new CommandLine(new AutoCompletion());
+      commandLine.execute(args);
+   }
+}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Configurable.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Configurable.java
index e676bda74b..d2aa15d30a 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Configurable.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Configurable.java
@@ -18,14 +18,8 @@
 package org.apache.activemq.artemis.cli.commands;
 
 import java.io.File;
+import java.lang.invoke.MethodHandles;
 
-import com.github.rvesse.airline.annotations.AirlineModule;
-import com.github.rvesse.airline.annotations.Arguments;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.help.Help;
-import com.github.rvesse.airline.model.CommandGroupMetadata;
-import com.github.rvesse.airline.model.CommandMetadata;
-import com.github.rvesse.airline.model.GlobalMetadata;
 import org.apache.activemq.artemis.api.core.ActiveMQException;
 import org.apache.activemq.artemis.cli.factory.BrokerFactory;
 import org.apache.activemq.artemis.cli.factory.jmx.ManagementFactory;
@@ -36,9 +30,8 @@ import org.apache.activemq.artemis.dto.ManagementContextDTO;
 import org.apache.activemq.artemis.jms.server.config.impl.FileJMSConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
 
 /**
  * Abstract class where we can replace the configuration in various places *
@@ -47,15 +40,12 @@ public abstract class Configurable extends ActionAbstract {
 
    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-   @Arguments(description = "Broker Configuration URI. Default: xml:${ARTEMIS_INSTANCE}/etc/bootstrap.xml.")
+   @Parameters(description = "Broker Configuration URI. Default: xml:${ARTEMIS_INSTANCE}/etc/bootstrap.xml.", defaultValue = "")
    String configuration;
 
-   @Option(name = "--broker", description = "Override the broker configuration from the bootstrap.xml.")
+   @Option(names = "--broker", description = "Override the broker configuration from the bootstrap.xml.")
    String brokerConfig;
 
-   @AirlineModule
-   public GlobalMetadata<Object> global;
-
    private BrokerDTO brokerDTO = null;
 
    private FileConfiguration fileConfiguration;
@@ -69,24 +59,6 @@ public abstract class Configurable extends ActionAbstract {
       if (!(e instanceof ActiveMQException)) {
          e.printStackTrace();
       }
-      helpGroup(group, command);
-   }
-
-   protected void helpGroup(String groupName, String commandName) {
-      for (CommandGroupMetadata group : global.getCommandGroups()) {
-         if (group.getName().equals(groupName)) {
-            for (CommandMetadata command : group.getCommands()) {
-               if (command.getName().equals(commandName)) {
-                  try {
-                     Help.help(command);
-                  } catch (IOException e) {
-                     throw new RuntimeException(e);
-                  }
-               }
-            }
-            break;
-         }
-      }
    }
 
    protected FileConfiguration getFileConfiguration() throws Exception {
@@ -145,7 +117,7 @@ public abstract class Configurable extends ActionAbstract {
    }
 
    protected String getConfiguration() {
-      if (configuration == null) {
+      if (configuration == null || configuration.equals("")) {
          File xmlFile = new File(new File(getBrokerEtc()), "bootstrap.xml");
          configuration = "xml:" + xmlFile.toURI().toString().substring("file:".length());
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Connect.java
similarity index 64%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Connect.java
index e34e376deb..5ff144f78a 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Connect.java
@@ -14,28 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@CommandLine.Command(name = "connect", description = "Connect to the broker validating credentials for commands.")
+public class Connect extends ConnectionAbstract {
 
    @Override
    public Object execute(ActionContext context) throws Exception {
       super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
-
-      killFile.createNewFile();
-
+      try {
+         CONNECTION_INFORMATION.remove();
+         createConnectionFactory();
+         System.out.println("Connection Successful!");
+      } catch (Exception e) {
+         System.out.println("Connection Failure!");
+         e.printStackTrace();
+      }
       return null;
    }
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
index f4e543a9a7..1717d042fb 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
@@ -25,8 +25,6 @@ import java.text.DecimalFormat;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
 import org.apache.activemq.artemis.api.core.RoutingType;
 import org.apache.activemq.artemis.cli.commands.util.HashUtil;
@@ -36,6 +34,8 @@ import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancing
 import org.apache.activemq.artemis.nativo.jlibaio.LibaioContext;
 import org.apache.activemq.artemis.nativo.jlibaio.LibaioFile;
 import org.apache.activemq.artemis.utils.FileUtil;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 /**
  * CLI action that creates a broker instance directory.
@@ -110,175 +110,175 @@ public class Create extends InstallAbstract {
    public static final String ETC_PAGE_SYNC_SETTINGS = "etc/page-sync-settings.txt";
    public static final String ETC_JOLOKIA_ACCESS_XML = "jolokia-access.xml";
 
-   @Option(name = "--host", description = "Broker's host name. Default: 0.0.0.0 or input if clustered).")
+   @Option(names = "--host", description = "Broker's host name. Default: 0.0.0.0 or input if clustered).")
    private String host;
 
-   @Option(name = "--http-host", description = "Embedded web server's host name. Default: localhost.")
+   @Option(names = "--http-host", description = "Embedded web server's host name. Default: localhost.")
    private String httpHost = HTTP_HOST;
 
-   @Option(name = "--relax-jolokia", description = "Disable strict checking in jolokia-access.xml.")
+   @Option(names = "--relax-jolokia", description = "Disable strict checking in jolokia-access.xml.")
    private boolean relaxJolokia;
 
-   @Option(name = "--ping", description = "A comma separated string to be passed on to the broker config as network-check-list. The broker will shutdown when all these addresses are unreachable.")
+   @Option(names = "--ping", description = "A comma separated string to be passed on to the broker config as network-check-list. The broker will shutdown when all these addresses are unreachable.")
    private String ping;
 
-   @Option(name = "--default-port", description = "The port number to use for the main 'artemis' acceptor. Default: 61616.")
+   @Option(names = "--default-port", description = "The port number to use for the main 'artemis' acceptor. Default: 61616.")
    private int defaultPort = DEFAULT_PORT;
 
-   @Option(name = "--http-port", description = "Embedded web server's port. Default: 8161.")
+   @Option(names = "--http-port", description = "Embedded web server's port. Default: 8161.")
    private int httpPort = HTTP_PORT;
 
-   @Option(name = "--ssl-key", description = "Embedded web server's key store path.")
+   @Option(names = "--ssl-key", description = "Embedded web server's key store path.")
    private String sslKey;
 
-   @Option(name = "--ssl-key-password", description = "The key store's password.")
+   @Option(names = "--ssl-key-password", description = "The key store's password.")
    private String sslKeyPassword;
 
-   @Option(name = "--use-client-auth", description = "Require client certificate authentication when connecting to the embedded web server.")
+   @Option(names = "--use-client-auth", description = "Require client certificate authentication when connecting to the embedded web server.")
    private boolean useClientAuth;
 
-   @Option(name = "--ssl-trust", description = "The trust store path in case of client authentication.")
+   @Option(names = "--ssl-trust", description = "The trust store path in case of client authentication.")
    private String sslTrust;
 
-   @Option(name = "--ssl-trust-password", description = "The trust store's password.")
+   @Option(names = "--ssl-trust-password", description = "The trust store's password.")
    private String sslTrustPassword;
 
-   @Option(name = "--name", description = "The name of the broker. Default: same as host name.")
+   @Option(names = "--name", description = "The name of the broker. Default: same as host name.")
    private String name;
 
-   @Option(name = "--port-offset", description = "How much to off-set the ports of every acceptor.")
+   @Option(names = "--port-offset", description = "How much to off-set the ports of every acceptor.")
    private int portOffset;
 
-   @Option(name = "--force", description = "Overwrite configuration at destination directory.")
+   @Option(names = "--force", description = "Overwrite configuration at destination directory.")
    private boolean force;
 
-   @Option(name = "--data", description = "Directory where ActiveMQ data are stored. Paths can be absolute or relative to artemis.instance directory. Default: data.")
+   @Option(names = "--data", description = "Directory where ActiveMQ data are stored. Paths can be absolute or relative to artemis.instance directory. Default: data.")
    private String data = "data";
 
-   @Option(name = "--clustered", description = "Enable clustering.")
+   @Option(names = "--clustered", description = "Enable clustering.")
    private boolean clustered = false;
 
-   @Option(name = "--max-hops", description = "Number of hops on the cluster configuration.")
+   @Option(names = "--max-hops", description = "Number of hops on the cluster configuration.")
    private int maxHops = 0;
 
-   @Option(name = "--message-load-balancing", description = "Message load balancing policy for cluster. Default: ON_DEMAND. Valid values: ON_DEMAND, STRICT, OFF, OFF_WITH_REDISTRIBUTION.")
+   @Option(names = "--message-load-balancing", description = "Message load balancing policy for cluster. Default: ON_DEMAND. Valid values: ON_DEMAND, STRICT, OFF, OFF_WITH_REDISTRIBUTION.")
    private MessageLoadBalancingType messageLoadBalancing = MessageLoadBalancingType.ON_DEMAND;
 
-   @Option(name = "--replicated", description = "Enable broker replication.")
+   @Option(names = "--replicated", description = "Enable broker replication.")
    private boolean replicated = false;
 
-   @Option(name = "--shared-store", description = "Enable broker shared store.")
+   @Option(names = "--shared-store", description = "Enable broker shared store.")
    private boolean sharedStore = false;
 
-   @Option(name = "--slave", description = "Be a slave broker. Valid for shared store or replication.")
+   @Option(names = "--slave", description = "Be a slave broker. Valid for shared store or replication.")
    private boolean slave;
 
-   @Option(name = "--failover-on-shutdown", description = "Whether broker shutdown will trigger failover for clients using the core protocol. Valid only for shared store. Default: false.")
+   @Option(names = "--failover-on-shutdown", description = "Whether broker shutdown will trigger failover for clients using the core protocol. Valid only for shared store. Default: false.")
    private boolean failoverOnShutodwn;
 
-   @Option(name = "--cluster-user", description = "The user to use for clustering. Default: input.")
+   @Option(names = "--cluster-user", description = "The user to use for clustering. Default: input.")
    private String clusterUser = null;
 
-   @Option(name = "--cluster-password", description = "The password to use for clustering. Default: input.")
+   @Option(names = "--cluster-password", description = "The password to use for clustering. Default: input.")
    private String clusterPassword = null;
 
-   @Option(name = "--allow-anonymous", description = "Allow connections from users with no security credentials. Opposite of --require-login. Default: input.")
+   @Option(names = "--allow-anonymous", description = "Allow connections from users with no security credentials. Opposite of --require-login. Default: input.")
    private Boolean allowAnonymous = null;
 
-   @Option(name = "--require-login", description = "Require security credentials from users for connection. Opposite of --allow-anonymous.")
+   @Option(names = "--require-login", description = "Require security credentials from users for connection. Opposite of --allow-anonymous.")
    private Boolean requireLogin = null;
 
-   @Option(name = "--paging", description = "Page messages to disk when address becomes full. Opposite of --blocking. Default: true.")
+   @Option(names = "--paging", description = "Page messages to disk when address becomes full. Opposite of --blocking. Default: true.")
    private Boolean paging;
 
-   @Option(name = "--blocking", description = "Block producers when address becomes full. Opposite of --paging. Default: false.")
+   @Option(names = "--blocking", description = "Block producers when address becomes full. Opposite of --paging. Default: false.")
    private Boolean blocking;
 
-   @Option(name = "--no-autotune", description = "Disable auto tuning of the journal-buffer-timeout in broker.xml.")
+   @Option(names = "--no-autotune", description = "Disable auto tuning of the journal-buffer-timeout in broker.xml.")
    private boolean noAutoTune;
 
-   @Option(name = "--no-autocreate", description = "Disable auto creation for addresses & queues.")
+   @Option(names = "--no-autocreate", description = "Disable auto creation for addresses & queues.")
    private Boolean noAutoCreate;
 
-   @Option(name = "--autocreate", description = "Allow automatic creation of addresses & queues. Default: true.")
+   @Option(names = "--autocreate", description = "Allow automatic creation of addresses & queues. Default: true.")
    private Boolean autoCreate;
 
-   @Option(name = "--autodelete", description = "Allow automatic deletion of addresses & queues. Default: false.")
+   @Option(names = "--autodelete", description = "Allow automatic deletion of addresses & queues. Default: false.")
    private boolean autoDelete;
 
-   @Option(name = "--user", description = "The username. Default: input.")
+   @Option(names = "--user", description = "The username. Default: input.")
    private String user;
 
-   @Option(name = "--password", description = "The user's password. Default: input.")
+   @Option(names = "--password", description = "The user's password. Default: input.")
    private String password;
 
-   @Option(name = "--role", description = "The name for the role created. Default: amq.")
+   @Option(names = "--role", description = "The name for the role created. Default: amq.")
    private String role = "amq";
 
-   @Option(name = "--no-web", description = "Whether to omit the web-server definition from bootstrap.xml.")
+   @Option(names = "--no-web", description = "Whether to omit the web-server definition from bootstrap.xml.")
    private boolean noWeb;
 
-   @Option(name = "--queues", description = "A comma separated list of queues with the option to specify a routing type, e.g. --queues myQueue1,myQueue2:multicast. Routing-type default: anycast.")
+   @Option(names = "--queues", description = "A comma separated list of queues with the option to specify a routing type, e.g. --queues myQueue1,myQueue2:multicast. Routing-type default: anycast.")
    private String queues;
 
-   @Option(name = "--addresses", description = "A comma separated list of addresses with the option to specify a routing type, e.g. --addresses myAddress1,myAddress2:anycast. Routing-type default: multicast.")
+   @Option(names = "--addresses", description = "A comma separated list of addresses with the option to specify a routing type, e.g. --addresses myAddress1,myAddress2:anycast. Routing-type default: multicast.")
    private String addresses;
 
-   @Option(name = "--aio", description = "Set the journal as asyncio.")
+   @Option(names = "--aio", description = "Set the journal as asyncio.")
    private boolean aio;
 
-   @Option(name = "--nio", description = "Set the journal as nio.")
+   @Option(names = "--nio", description = "Set the journal as nio.")
    private boolean nio;
 
-   @Option(name = "--mapped", description = "Set the journal as mapped.")
+   @Option(names = "--mapped", description = "Set the journal as mapped.")
    private boolean mapped;
 
    // this is used by the setupJournalType method
    private JournalType journalType;
 
-   @Option(name = "--disable-persistence", description = "Disable message persistence to the journal")
+   @Option(names = "--disable-persistence", description = "Disable message persistence to the journal")
    private boolean disablePersistence;
 
-   @Option(name = "--no-amqp-acceptor", description = "Disable the AMQP specific acceptor.")
+   @Option(names = "--no-amqp-acceptor", description = "Disable the AMQP specific acceptor.")
    private boolean noAmqpAcceptor;
 
-   @Option(name = "--no-mqtt-acceptor", description = "Disable the MQTT specific acceptor.")
+   @Option(names = "--no-mqtt-acceptor", description = "Disable the MQTT specific acceptor.")
    private boolean noMqttAcceptor;
 
-   @Option(name = "--no-stomp-acceptor", description = "Disable the STOMP specific acceptor.")
+   @Option(names = "--no-stomp-acceptor", description = "Disable the STOMP specific acceptor.")
    private boolean noStompAcceptor;
 
-   @Option(name = "--no-hornetq-acceptor", description = "Disable the HornetQ specific acceptor.")
+   @Option(names = "--no-hornetq-acceptor", description = "Disable the HornetQ specific acceptor.")
    private boolean noHornetQAcceptor;
 
-   @Option(name = "--no-fsync", description = "Disable usage of fdatasync (channel.force(false) from Java NIO) on the journal.")
+   @Option(names = "--no-fsync", description = "Disable usage of fdatasync (channel.force(false) from Java NIO) on the journal.")
    private boolean noJournalSync;
 
-   @Option(name = "--journal-device-block-size", description = "The block size of the journal's storage device. Default: 4096.")
+   @Option(names = "--journal-device-block-size", description = "The block size of the journal's storage device. Default: 4096.")
    private int journalDeviceBlockSize = 4096;
 
-   @Option(name = "--journal-retention", description = "Configure journal retention in days. If > 0 then enable journal-retention-directory from broker.xml allowing replay options.")
+   @Option(names = "--journal-retention", description = "Configure journal retention in days. If > 0 then enable journal-retention-directory from broker.xml allowing replay options.")
    private int retentionDays;
 
-   @Option(name = "--journal-retention-max-bytes", description = "Maximum number of bytes to keep in the retention directory.")
+   @Option(names = "--journal-retention-max-bytes", description = "Maximum number of bytes to keep in the retention directory.")
    private String retentionMaxBytes;
 
-   @Option(name = "--global-max-size", description = "Maximum amount of memory which message data may consume. Default: half of the JVM's max memory.")
+   @Option(names = "--global-max-size", description = "Maximum amount of memory which message data may consume. Default: half of the JVM's max memory.")
    private String globalMaxSize;
 
-   @Option(name = "--global-max-messages", description = "Maximum number of messages that will be accepted in memory before using address full policy mode. Default: undefined.")
+   @Option(names = "--global-max-messages", description = "Maximum number of messages that will be accepted in memory before using address full policy mode. Default: undefined.")
    private long globalMaxMessages = -1;
 
-   @Option(name = "--jdbc", description = "Store message data in JDBC instead of local files.")
+   @Option(names = "--jdbc", description = "Store message data in JDBC instead of local files.")
    boolean jdbc;
 
-   @Option(name = "--staticCluster", description = "Cluster node connectors list separated by comma, e.g. \"tcp://server:61616,tcp://server2:61616,tcp://server3:61616\".")
+   @Option(names = "--staticCluster", description = "Cluster node connectors list separated by comma, e.g. \"tcp://server:61616,tcp://server2:61616,tcp://server3:61616\".")
    String staticNode;
 
-   @Option(name = "--support-advisory", description = "Support advisory messages for the OpenWire protocol.")
+   @Option(names = "--support-advisory", description = "Support advisory messages for the OpenWire protocol.")
    boolean supportAdvisory = false;
 
-   @Option(name = "--suppress-internal-management-objects", description = "Do not register any advisory addresses/queues for the OpenWire protocol with the broker's management service.")
+   @Option(names = "--suppress-internal-management-objects", description = "Do not register any advisory addresses/queues for the OpenWire protocol with the broker's management service.")
    boolean suppressInternalManagementObjects = false;
 
    public String[] getStaticNodes() {
@@ -289,37 +289,37 @@ public class Create extends InstallAbstract {
       }
    }
 
-   @Option(name = "--security-manager", description = "Which security manager to use - jaas or basic. Default: jaas.")
+   @Option(names = "--security-manager", description = "Which security manager to use - jaas or basic. Default: jaas.")
    private String securityManager = "jaas";
 
-   @Option(name = "--jdbc-bindings-table-name", description = "Name of the jdbc bindings table.")
+   @Option(names = "--jdbc-bindings-table-name", description = "Name of the jdbc bindings table.")
    private String jdbcBindings = ActiveMQDefaultConfiguration.getDefaultBindingsTableName();
 
-   @Option(name = "--jdbc-message-table-name", description = "Name of the jdbc messages table.")
+   @Option(names = "--jdbc-message-table-name", description = "Name of the jdbc messages table.")
    private String jdbcMessages = ActiveMQDefaultConfiguration.getDefaultMessageTableName();
 
-   @Option(name = "--jdbc-large-message-table-name", description = "Name of the large messages table.")
+   @Option(names = "--jdbc-large-message-table-name", description = "Name of the large messages table.")
    private String jdbcLargeMessages = ActiveMQDefaultConfiguration.getDefaultLargeMessagesTableName();
 
-   @Option(name = "--jdbc-page-store-table-name", description = "Name of the page store messages table.")
+   @Option(names = "--jdbc-page-store-table-name", description = "Name of the page store messages table.")
    private String jdbcPageStore = ActiveMQDefaultConfiguration.getDefaultPageStoreTableName();
 
-   @Option(name = "--jdbc-node-manager-table-name", description = "Name of the jdbc node manager table.")
+   @Option(names = "--jdbc-node-manager-table-name", description = "Name of the jdbc node manager table.")
    private String jdbcNodeManager = ActiveMQDefaultConfiguration.getDefaultNodeManagerStoreTableName();
 
-   @Option(name = "--jdbc-connection-url", description = "The URL used for the database connection.")
+   @Option(names = "--jdbc-connection-url", description = "The URL used for the database connection.")
    private String jdbcURL = null;
 
-   @Option(name = "--jdbc-driver-class-name", description = "JDBC driver classname.")
+   @Option(names = "--jdbc-driver-class-name", description = "JDBC driver classname.")
    private String jdbcClassName = ActiveMQDefaultConfiguration.getDefaultDriverClassName();
 
-   @Option(name = "--jdbc-network-timeout", description = "Network timeout (in milliseconds).")
+   @Option(names = "--jdbc-network-timeout", description = "Network timeout (in milliseconds).")
    long jdbcNetworkTimeout = ActiveMQDefaultConfiguration.getDefaultJdbcNetworkTimeout();
 
-   @Option(name = "--jdbc-lock-renew-period", description = "Lock Renew Period (in milliseconds).")
+   @Option(names = "--jdbc-lock-renew-period", description = "Lock Renew Period (in milliseconds).")
    long jdbcLockRenewPeriod = ActiveMQDefaultConfiguration.getDefaultJdbcLockRenewPeriodMillis();
 
-   @Option(name = "--jdbc-lock-expiration", description = "Lock expiration (in milliseconds).")
+   @Option(names = "--jdbc-lock-expiration", description = "Lock expiration (in milliseconds).")
    long jdbcLockExpiration = ActiveMQDefaultConfiguration.getDefaultJdbcLockExpirationMillis();
 
    private boolean isAutoCreate() {
@@ -712,11 +712,9 @@ public class Create extends InstallAbstract {
       File logFolder = createDirectory("log", directory);
       File oomeDumpFile = new File(logFolder, "oom_dump.hprof");
 
-      if (javaOptions == null || javaOptions.length() == 0) {
-         javaOptions = "";
-      }
+      String processedJavaOptions = getJavaOptions();
 
-      addScriptFilters(filters, getHome(), getInstance(), etcFolder, dataFolder, oomeDumpFile, javaMemory, javaOptions, role);
+      addScriptFilters(filters, getHome(), getInstance(), etcFolder, dataFolder, oomeDumpFile, javaMemory, processedJavaOptions, role);
 
       boolean allowAnonymous = isAllowAnonymous();
 
@@ -739,7 +737,7 @@ public class Create extends InstallAbstract {
 
       filters.put("${journal-retention}", retentionTag);
 
-      filters.put("${java-opts}", javaOptions);
+      filters.put("${java-opts}", processedJavaOptions);
       filters.put("${java-memory}", javaMemory);
 
       if (allowAnonymous) {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Disconnect.java
similarity index 68%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Disconnect.java
index e34e376deb..4d05496b4c 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Disconnect.java
@@ -14,28 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@CommandLine.Command(name = "disconnect", description = "Clear previously typed user credentials.")
+public class Disconnect extends ConnectionAbstract {
 
    @Override
    public Object execute(ActionContext context) throws Exception {
       super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
-
-      killFile.createNewFile();
-
+      CONNECTION_INFORMATION.remove();
+      System.out.println("Connection information cleared!");
       return null;
    }
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/HelpAction.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/HelpAction.java
index d11db8c06d..61309a820b 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/HelpAction.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/HelpAction.java
@@ -16,40 +16,48 @@
  */
 package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
-
-import com.github.rvesse.airline.help.Help;
-
-public class HelpAction extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etcFolder) {
-
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "help", description = "use 'help <command>' for more information")
+public class HelpAction implements Runnable {
+
+   CommandLine commandLine;
+
+   @CommandLine.Parameters
+   String[] args;
+
+   public static void help(CommandLine commandLine, String... args) {
+      if (args != null) {
+         CommandLine theLIn = commandLine;
+         for (String i : args) {
+            Object subCommand = theLIn.getSubcommands().get(i);
+            if (subCommand == null) {
+               commandLine.usage(System.out);
+            } else if (subCommand instanceof CommandLine) {
+               theLIn = (CommandLine) subCommand;
+            } else {
+               commandLine.usage(System.out);
+            }
+         }
+         theLIn.usage(System.out);
+      } else {
+         commandLine.usage(System.out);
+      }
    }
 
-   @Override
-   public String getBrokerInstance() {
-      return null;
+   public CommandLine getCommandLine() {
+      return commandLine;
    }
 
-   @Override
-   public String getBrokerHome() {
-      return null;
+   public HelpAction setCommandLine(CommandLine commandLine) {
+      this.commandLine = commandLine;
+      return this;
    }
 
    @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
+   public void run() {
+      help(commandLine, args);
    }
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.run();
-      return null;
-   }
 }
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 3915816f7e..9720c1d632 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
@@ -19,7 +19,7 @@ package org.apache.activemq.artemis.cli.commands;
 
 import java.util.Scanner;
 
-import com.github.rvesse.airline.annotations.Option;
+import picocli.CommandLine.Option;
 
 public class InputAbstract extends ActionAbstract {
 
@@ -34,7 +34,7 @@ public class InputAbstract extends ActionAbstract {
       inputEnabled = true;
    }
 
-   @Option(name = "--silent", description = "Disable all the inputs, and make a best guess for any required input.")
+   @Option(names = "--silent", description = "Disable all the inputs, and make a best guess for any required input.")
    private boolean silentInput = false;
 
    public boolean isSilentInput() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InstallAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InstallAbstract.java
index dc28622d26..e13f1c8b93 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InstallAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InstallAbstract.java
@@ -26,42 +26,48 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import com.github.rvesse.airline.annotations.Arguments;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.cli.CLIException;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
 
 public class InstallAbstract extends InputAbstract {
 
-   @Arguments(description = "The instance directory to hold the broker's configuration and data. Path must be writable.")
-   @Required
+   @Parameters(description = "The instance directory to hold the broker's configuration and data. Path must be writable.")
    protected File directory;
 
-   @Option(name = "--etc", description = "Directory where ActiveMQ configuration is located. Paths can be absolute or relative to artemis.instance directory. Default: etc.")
+   @Option(names = "--etc", description = "Directory where ActiveMQ configuration is located. Paths can be absolute or relative to artemis.instance directory. Default: etc.")
    protected String etc = "etc";
 
-   @Option(name = "--home", description = "Directory where ActiveMQ Artemis is installed.")
+   @Option(names = "--home", description = "Directory where ActiveMQ Artemis is installed.")
    protected File home;
 
-   @Option(name = "--encoding", description = "The encoding that text files should use. Default: UTF-8.")
+   @Option(names = "--encoding", description = "The encoding that text files should use. Default: UTF-8.")
    protected String encoding = "UTF-8";
 
-   @Option(name = "--windows", description = "Force Windows script creation. Default: based on your actual system.")
+   @Option(names = "--windows", description = "Force Windows script creation. Default: based on your actual system.")
    protected boolean windows = false;
 
-   @Option(name = "--cygwin", description = "Force Cygwin script creation. Default: based on your actual system.")
+   @Option(names = "--cygwin", description = "Force Cygwin script creation. Default: based on your actual system.")
    protected boolean cygwin = false;
 
-   @Option(name = "--java-options", description = "Extra Java options to be passed to the profile.")
-   protected String javaOptions = "";
+   @Option(names = "--java-options", description = "Extra Java options to be passed to the profile.")
+   protected List<String> javaOptions;
 
-   @Option(name = "--java-memory", description = "Define the -Xmx memory parameter for the broker. Default: 2G.")
+   @Option(names = "--java-memory", description = "Define the -Xmx memory parameter for the broker. Default: 2G.")
    protected String javaMemory = "2G";
 
+   protected String getJavaOptions() {
+      StringBuilder builder = new StringBuilder();
+      if (javaOptions != null) {
+         javaOptions.forEach(s -> builder.append(s).append(" "));
+      }
+      return builder.toString();
+   }
 
    public String getEncoding() {
       return encoding;
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
index e34e376deb..cfe9613436 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
@@ -18,8 +18,8 @@ package org.apache.activemq.artemis.cli.commands;
 
 import java.io.File;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.dto.BrokerDTO;
+import picocli.CommandLine.Command;
 
 @Command(name = "kill", description = "Kill a broker started with --allow-kill.")
 public class Kill extends Configurable {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Mask.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Mask.java
index 40ef0ae856..aefb5c0b3f 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Mask.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Mask.java
@@ -19,29 +19,27 @@ package org.apache.activemq.artemis.cli.commands;
 import java.util.HashMap;
 import java.util.Map;
 
-import com.github.rvesse.airline.annotations.Arguments;
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.core.config.Configuration;
 import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
 import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import org.apache.activemq.artemis.utils.SensitiveDataCodec;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
 
 @Command(name = "mask", description = "Mask a password and print it out.")
 public class Mask extends ActionAbstract {
 
-   @Arguments(description = "The password to be masked.")
-   @Required
+   @Parameters(description = "The password to be masked.")
    String password;
 
-   @Option(name = "--hash", description = "Whether to use a hash (one-way). Default: false.")
+   @Option(names = "--hash", description = "Whether to use a hash (one-way). Default: false.")
    boolean hash = false;
 
-   @Option(name = "--key", description = "The key (Blowfish) to mask a password.")
+   @Option(names = "--key", description = "The key (Blowfish) to mask a password.")
    String key;
 
-   @Option(name = "--password-codec", description = "Whether to use the password codec defined in the configuration. Default: false")
+   @Option(names = "--password-codec", description = "Whether to use the password codec defined in the configuration. Default: false")
    boolean passwordCodec = false;
 
    private SensitiveDataCodec<String> codec;
@@ -100,9 +98,4 @@ public class Mask extends ActionAbstract {
       return codec;
    }
 
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/OptionsUtil.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/OptionsUtil.java
deleted file mode 100644
index 9c12b83dfd..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/OptionsUtil.java
+++ /dev/null
@@ -1,64 +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.activemq.artemis.cli.commands;
-
-import java.lang.reflect.Field;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.github.rvesse.airline.annotations.Option;
-
-public class OptionsUtil {
-
-   private static void findAllOptions(Set<String> options, Class<? extends Action> command) {
-      for (Field field  : command.getDeclaredFields()) {
-         if (field.isAnnotationPresent(Option.class)) {
-            Option annotation = field.getAnnotation(Option.class);
-            String[] names = annotation.name();
-            for (String n : names) {
-               options.add(n);
-            }
-         }
-      }
-      Class parent = command.getSuperclass();
-      if (Action.class.isAssignableFrom(parent)) {
-         findAllOptions(options, parent);
-      }
-   }
-
-   private static Set<String> findCommandOptions(Class<? extends Action> command) {
-      Set<String> options = new HashSet<>();
-      findAllOptions(options, command);
-
-      return options;
-   }
-
-   public static void checkCommandOptions(Class<? extends Action> cmdClass, String[] options) throws InvalidOptionsError {
-      Set<String> definedOptions = OptionsUtil.findCommandOptions(cmdClass);
-      for (String opt : options) {
-         if (opt.startsWith("--") && !"--".equals(opt.trim())) {
-            int index = opt.indexOf("=");
-            if (index > 0) {
-               opt = opt.substring(0, index);
-            }
-            if (!definedOptions.contains(opt)) {
-               throw new InvalidOptionsError("Found unexpected parameters: [" + opt + "]");
-            }
-         }
-      }
-   }
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/PrintVersion.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/PrintVersion.java
index 4bb2217e8f..fc0fb48f6c 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/PrintVersion.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/PrintVersion.java
@@ -16,9 +16,9 @@
  */
 package org.apache.activemq.artemis.cli.commands;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.core.version.Version;
 import org.apache.activemq.artemis.utils.VersionLoader;
+import picocli.CommandLine.Command;
 
 @Command(name = "version", description = "Print version information.")
 public class PrintVersion extends ActionAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
index 96b3e00761..bb3d926570 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
@@ -21,8 +21,6 @@ import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
 import org.apache.activemq.artemis.api.core.Pair;
 import org.apache.activemq.artemis.cli.Artemis;
@@ -41,14 +39,16 @@ import org.apache.activemq.artemis.integration.Broker;
 import org.apache.activemq.artemis.integration.bootstrap.ActiveMQBootstrapLogger;
 import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
 import org.apache.activemq.artemis.utils.ReusableLatch;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "run", description = "Run the broker.")
 public class Run extends LockAbstract {
 
-   @Option(name = "--allow-kill", description = "This will allow the server to kill itself. Useful for tests (e.g. failover tests).")
+   @Option(names = "--allow-kill", description = "This will allow the server to kill itself. Useful for tests (e.g. failover tests).")
    boolean allowKill;
 
-   @Option(name = "--properties", description = "URL to a properties file that is applied to the server's configuration.")
+   @Option(names = "--properties", description = "URL to a properties file that is applied to the server's configuration.")
    String properties;
 
    private static boolean embedded = false;
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Stop.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Stop.java
index 1232a9fb16..2ef2e8abcb 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Stop.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Stop.java
@@ -18,8 +18,8 @@ package org.apache.activemq.artemis.cli.commands;
 
 import java.io.File;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.dto.BrokerDTO;
+import picocli.CommandLine.Command;
 
 @Command(name = "stop", description = "Stop the broker.")
 public class Stop extends Configurable {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Upgrade.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Upgrade.java
index 2d7001821b..9185ef4dd4 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Upgrade.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Upgrade.java
@@ -30,8 +30,8 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.util.JVMArgumentParser;
+import picocli.CommandLine.Command;
 
 @Command(name = "upgrade", description = "Update a broker instance to the current artemis.home, keeping all the data and broker.xml. Warning: backup your instance before using this command and compare the files.")
 public class Upgrade extends InstallAbstract {
@@ -107,7 +107,7 @@ public class Upgrade extends InstallAbstract {
       }
 
       HashMap<String, String> filters = new HashMap<>();
-      Create.addScriptFilters(filters, getHome(), getInstance(), etcFolder, new File(getInstance(), "notUsed"), new File(getInstance(), "om-not-used.dmp"), javaMemory, javaOptions, "NA");
+      Create.addScriptFilters(filters, getHome(), getInstance(), etcFolder, new File(getInstance(), "notUsed"), new File(getInstance(), "om-not-used.dmp"), javaMemory, getJavaOptions(), "NA");
 
       if (IS_WINDOWS) {
          // recreating the service.exe and config in case we ever upgrade it
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationGroup.java
similarity index 57%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationGroup.java
index e34e376deb..6112ba0039 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationGroup.java
@@ -14,28 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.activation;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@Command(name = "activation", description = "use 'help activation' for sub commands list", subcommands = {ActivationSequenceList.class, ActivationSequenceSet.class})
+public class ActivationGroup implements Runnable {
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
+   CommandLine commandLine;
 
-      killFile.createNewFile();
+   public ActivationGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-      return null;
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "activation");
    }
+
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceList.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceList.java
index cd3c47dd08..39066beb9d 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceList.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceList.java
@@ -20,8 +20,6 @@ import java.io.PrintStream;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
 import org.apache.activemq.artemis.core.config.Configuration;
@@ -34,6 +32,8 @@ import org.apache.activemq.artemis.core.server.impl.FileLockNodeManager;
 import org.apache.activemq.artemis.quorum.DistributedLock;
 import org.apache.activemq.artemis.quorum.DistributedPrimitiveManager;
 import org.apache.activemq.artemis.quorum.MutableLong;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 import static org.apache.activemq.artemis.cli.commands.activation.ActivationSequenceUtils.applyCoordinationId;
 
@@ -41,11 +41,11 @@ import static org.apache.activemq.artemis.cli.commands.activation.ActivationSequ
 public class ActivationSequenceList extends LockAbstract {
 
    private static final int MANAGER_START_TIMEOUT_SECONDS = 60;
-   @Option(name = "--node-id", description = "This can be used just with --remote option. If not set, broker NodeID is used instead.")
+   @Option(names = "--node-id", description = "This can be used just with --remote option. If not set, broker NodeID is used instead.")
    public String nodeId = null;
-   @Option(name = "--remote", description = "List just remote (i.e. coordinated) activation sequence.")
+   @Option(names = "--remote", description = "List just remote (i.e. coordinated) activation sequence.")
    public boolean remote = false;
-   @Option(name = "--local", description = "List just local activation sequence.")
+   @Option(names = "--local", description = "List just local activation sequence.")
    public boolean local = false;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceSet.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceSet.java
index 4551a626fa..71b7fa35ac 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceSet.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/activation/ActivationSequenceSet.java
@@ -20,9 +20,6 @@ import java.io.PrintStream;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
 import org.apache.activemq.artemis.core.config.Configuration;
@@ -35,6 +32,8 @@ import org.apache.activemq.artemis.core.server.impl.FileLockNodeManager;
 import org.apache.activemq.artemis.quorum.DistributedLock;
 import org.apache.activemq.artemis.quorum.DistributedPrimitiveManager;
 import org.apache.activemq.artemis.quorum.MutableLong;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 import static org.apache.activemq.artemis.cli.commands.activation.ActivationSequenceUtils.applyCoordinationId;
 
@@ -43,17 +42,16 @@ public class ActivationSequenceSet extends LockAbstract {
 
    private static final int MANAGER_START_TIMEOUT_SECONDS = 60;
 
-   @Option(name = "--node-id", description = "Target sequence for this UUID overwriting the NodeID of this broker too. If not set, broker NodeID is used instead.")
+   @Option(names = "--node-id", description = "Target sequence for this UUID overwriting the NodeID of this broker too. If not set, broker NodeID is used instead.")
    public String nodeId = null;
 
-   @Option(name = "--remote", description = "Set just remote (i.e. coordinated) activation sequence.")
+   @Option(names = "--remote", description = "Set just remote (i.e. coordinated) activation sequence.")
    public boolean remote = false;
 
-   @Option(name = "--local", description = "Set just local activation sequence.")
+   @Option(names = "--local", description = "Set just local activation sequence.")
    public boolean local = false;
 
-   @Option(name = "--to", description = "The new activation sequence.")
-   @Required
+   @Option(names = "--to", description = "The new activation sequence.", required = true)
    public long value;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressAbstract.java
index 337b8c4b48..9398763e85 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressAbstract.java
@@ -16,24 +16,24 @@
  */
 package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine.Option;
 
 public abstract class AddressAbstract extends ConnectionAbstract {
 
-   @Option(name = "--name", description = "The address's name.")
+   @Option(names = "--name", description = "The address's name.")
    private String name;
 
-   @Option(name = "--anycast", description = "Whether the address supports anycast queues.")
+   @Option(names = "--anycast", description = "Whether the address supports anycast queues.")
    private Boolean anycast;
 
-   @Option(name = "--no-anycast", description = "Whether the address won't support anycast queues.")
+   @Option(names = "--no-anycast", description = "Whether the address won't support anycast queues.")
    private Boolean noAnycast;
 
-   @Option(name = "--multicast", description = "Whether the address supports multicast queues.")
+   @Option(names = "--multicast", description = "Whether the address supports multicast queues.")
    private Boolean multicast;
 
-   @Option(name = "--no-multicast", description = "Whether the address won't support multicast queues.")
+   @Option(names = "--no-multicast", description = "Whether the address won't support multicast queues.")
    private Boolean noMulticast;
 
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressGroup.java
similarity index 57%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressGroup.java
index e34e376deb..118698e953 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/AddressGroup.java
@@ -14,28 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@Command(name = "address", description = "use 'help address' for sub commands list", subcommands = {CreateAddress.class, DeleteAddress.class, UpdateAddress.class, ShowAddress.class})
+public class AddressGroup implements Runnable {
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
+   CommandLine commandLine;
 
-      File killFile = new File(file, "KILL_ME");
-
-      killFile.createNewFile();
+   public AddressGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-      return null;
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "address");
    }
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/CreateAddress.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/CreateAddress.java
index 6d5a004d46..354e983cc5 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/CreateAddress.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/CreateAddress.java
@@ -17,9 +17,9 @@
 
 package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
 
 @Command(name = "create", description = "Create an address.")
 public class CreateAddress extends AddressAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/DeleteAddress.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/DeleteAddress.java
index 6f18b442f6..2822d4ce18 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/DeleteAddress.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/DeleteAddress.java
@@ -17,15 +17,15 @@
 
 package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "delete", description = "Delete an address.")
 public class DeleteAddress extends AddressAbstract {
 
-   @Option(name = "--force", description = "Delete the address even if it has queues. All messages in those queues will be deleted! Default: false.")
+   @Option(names = "--force", description = "Delete the address even if it has queues. All messages in those queues will be deleted! Default: false.")
    private Boolean force = false;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/HelpAddress.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/HelpAddress.java
deleted file mode 100644
index cf28abb3dd..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/HelpAddress.java
+++ /dev/null
@@ -1,63 +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.activemq.artemis.cli.commands.address;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.github.rvesse.airline.help.Help;
-import org.apache.activemq.artemis.cli.commands.Action;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.apache.activemq.artemis.cli.commands.OptionsUtil;
-
-public class HelpAddress extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etc) {
-   }
-
-   @Override
-   public String getBrokerInstance() {
-      return null;
-   }
-
-   @Override
-   public String getBrokerHome() {
-      return null;
-   }
-
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      List<String> commands = new ArrayList<>(1);
-      commands.add("address");
-      help(global, commands);
-      return null;
-   }
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/ShowAddress.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/ShowAddress.java
index f2b7b42c3a..c6fd475949 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/ShowAddress.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/ShowAddress.java
@@ -17,15 +17,15 @@
 
 package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "show", description = "Show the selected address.")
 public class ShowAddress extends AddressAbstract {
 
-   @Option(name = "--bindings", description = "Show the bindings for this address.")
+   @Option(names = "--bindings", description = "Show the bindings for this address.")
    boolean bindings;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/UpdateAddress.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/UpdateAddress.java
index 614e7b61c2..2b9cfca6c2 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/UpdateAddress.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/address/UpdateAddress.java
@@ -16,9 +16,9 @@
  */
 package org.apache.activemq.artemis.cli.commands.address;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
 
 @Command(name = "update", description = "Update an address.")
 public class UpdateAddress extends AddressAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckAbstract.java
index dff6238f7e..e2a066f6c8 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckAbstract.java
@@ -23,23 +23,23 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ActiveMQManagementProxy;
 import org.apache.activemq.artemis.cli.CLIException;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
 import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
 import org.apache.commons.lang3.time.StopWatch;
+import picocli.CommandLine.Option;
 
 public abstract class CheckAbstract extends ConnectionAbstract {
 
-   @Option(name = "--name", description = "Name of the target to check.")
+   @Option(names = "--name", description = "Name of the target to check.")
    protected String name;
 
-   @Option(name = "--timeout", description = "Time to wait for the check to complete (in milliseconds).")
+   @Option(names = "--timeout", description = "Time to wait for the check to complete (in milliseconds).")
    private int timeout = 30000;
 
-   @Option(name = "--fail-at-end", description = "Continue with the rest of the checks even if a particular module check fails.")
+   @Option(names = "--fail-at-end", description = "Continue with the rest of the checks even if a particular module check fails.")
    private boolean failAtEnd = false;
 
    public String getName() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckGroup.java
similarity index 58%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckGroup.java
index 5ed583754d..c5d1dddadf 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Action.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/CheckGroup.java
@@ -14,21 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.check;
 
-public interface Action {
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-   boolean isVerbose();
+@Command(name = "check", description = "use 'help check' for sub commands list", subcommands = {NodeCheck.class, QueueCheck.class})
+public class CheckGroup implements Runnable {
 
-   void setHomeValues(File brokerHome, File brokerInstance, File etcFolder);
+   CommandLine commandLine;
 
-   Object execute(ActionContext context) throws Exception;
+   public CheckGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-   String getBrokerInstance();
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "check");
 
-   String getBrokerHome();
+   }
 
-   void checkOptions(String[] options) throws InvalidOptionsError;
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/HelpCheck.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/HelpCheck.java
deleted file mode 100644
index 30a25b88fc..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/HelpCheck.java
+++ /dev/null
@@ -1,63 +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.activemq.artemis.cli.commands.check;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.github.rvesse.airline.help.Help;
-import org.apache.activemq.artemis.cli.commands.Action;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.apache.activemq.artemis.cli.commands.OptionsUtil;
-
-public class HelpCheck extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etc) {
-   }
-
-   @Override
-   public String getBrokerInstance() {
-      return null;
-   }
-
-   @Override
-   public String getBrokerHome() {
-      return null;
-   }
-
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      List<String> commands = new ArrayList<>(1);
-      commands.add("check");
-      help(global, commands);
-      return null;
-   }
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/NodeCheck.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/NodeCheck.java
index 71b7354a94..71a50a8e9f 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/NodeCheck.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/NodeCheck.java
@@ -19,29 +19,29 @@ package org.apache.activemq.artemis.cli.commands.check;
 
 import java.util.ArrayList;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.NodeInfo;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "node", description = "Check a node.")
 public class NodeCheck extends CheckAbstract {
 
-   @Option(name = "--up", description = "Check that the node is started. This check is executed by default if there are no other checks.")
+   @Option(names = "--up", description = "Check that the node is started. This check is executed by default if there are no other checks.")
    private boolean up;
 
-   @Option(name = "--diskUsage", description = "Disk usage percentage to check or -1 to use the max-disk-usage.")
+   @Option(names = "--diskUsage", description = "Disk usage percentage to check or -1 to use the max-disk-usage.")
    private Integer diskUsage;
 
-   @Option(name = "--memoryUsage", description = "Memory usage percentage to check.")
+   @Option(names = "--memoryUsage", description = "Memory usage percentage to check.")
    private Integer memoryUsage;
 
-   @Option(name = "--live", description = "Check that the node has a connected live.")
+   @Option(names = "--live", description = "Check that the node has a connected live.")
    private boolean live;
 
-   @Option(name = "--backup", description = "Check that the node has a connected backup.")
+   @Option(names = "--backup", description = "Check that the node has a connected backup.")
    private boolean backup;
 
-   @Option(name = "--peers", description = "Number of peers to check.")
+   @Option(names = "--peers", description = "Number of peers to check.")
    private Integer peers;
 
    public boolean isUp() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/QueueCheck.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/QueueCheck.java
index 9cee8a072e..db3592e712 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/QueueCheck.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/check/QueueCheck.java
@@ -23,27 +23,26 @@ import javax.jms.MessageConsumer;
 import javax.jms.MessageProducer;
 import javax.jms.QueueBrowser;
 import javax.jms.Session;
-
 import java.util.ArrayList;
 import java.util.Enumeration;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ResourceNames;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "queue", description = "Check a queue.")
 public class QueueCheck extends CheckAbstract {
 
-   @Option(name = "--up", description = "Check that the queue exists and is not paused. This check is executed by default if there are no other checks.")
+   @Option(names = "--up", description = "Check that the queue exists and is not paused. This check is executed by default if there are no other checks.")
    private boolean up;
 
-   @Option(name = "--browse", description = "Number of the messages to browse or -1 to check that the queue is browsable.")
+   @Option(names = "--browse", description = "Number of the messages to browse or -1 to check that the queue is browsable.")
    private Integer browse;
 
-   @Option(name = "--consume", description = "Number of the messages to consume or -1 to check that the queue is consumable.")
+   @Option(names = "--consume", description = "Number of the messages to consume or -1 to check that the queue is consumable.")
    private Integer consume;
 
-   @Option(name = "--produce", description = "Number of the messages to produce.")
+   @Option(names = "--produce", description = "Number of the messages to produce.")
    private Integer produce;
 
    public boolean isUp() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Browse.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Browse.java
index 4e4a7b7301..c133ed7d89 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Browse.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Browse.java
@@ -22,14 +22,14 @@ import javax.jms.ConnectionFactory;
 import javax.jms.Destination;
 import javax.jms.Session;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "browser", description = "Browse messages on a queue.")
 public class Browse extends DestAbstract {
 
-   @Option(name = "--filter", description = "The message filter.")
+   @Option(names = "--filter", description = "The message filter.")
    String filter;
 
    @Override
@@ -66,7 +66,7 @@ public class Browse extends DestAbstract {
 
          connection.start();
 
-         int received = 0;
+         long received = 0;
 
          for (ConsumerThread thread : threadsArray) {
             thread.join();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstract.java
index c9ee1a4fb8..ba8a2fd3f9 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstract.java
@@ -21,32 +21,44 @@ import javax.jms.ConnectionFactory;
 import javax.jms.JMSException;
 import javax.jms.JMSSecurityException;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
+import org.apache.activemq.artemis.cli.Shell;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.InputAbstract;
 import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
 import org.apache.qpid.jms.JmsConnectionFactory;
+import picocli.CommandLine.Option;
 
 public class ConnectionAbstract extends InputAbstract {
-
-   @Option(name = "--url", description = "Connection URL. Default: build URL from the 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the acceptor cannot be parsed.")
+   @Option(names = "--url", description = "Connection URL. Default: build URL from the 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the acceptor cannot be parsed.")
    protected String brokerURL = DEFAULT_BROKER_URL;
 
-   @Option(name = "--acceptor", description = "Name used to find the default connection URL on the acceptor list. If an acceptor with that name cannot be found the CLI will look for a connector with the same name.")
+   @Option(names = "--acceptor", description = "Name used to find the default connection URL on the acceptor list. If an acceptor with that name cannot be found the CLI will look for a connector with the same name.")
    protected String acceptor;
 
-   @Option(name = "--user", description = "User used to connect.")
+   @Option(names = "--user", description = "User used to connect.")
    protected String user;
 
-   @Option(name = "--password", description = "Password used to connect.")
+   @Option(names = "--password", description = "Password used to connect.")
    protected String password;
 
-   @Option(name = "--clientID", description = "ClientID set on the connection.")
+   @Option(names = "--clientID", description = "ClientID set on the connection.")
    protected String clientID;
 
-   @Option(name = "--protocol", description = "Protocol used. Valid values are amqp or core. Default: core.")
-   protected String protocol = "core";
+   @Option(names = "--protocol", description = "Protocol used. Valid values are ${COMPLETION-CANDIDATES}", converter = ConnectionProtocol.ProtocolConverter.class)
+   protected ConnectionProtocol protocol = ConnectionProtocol.CORE;
+
+   protected static ThreadLocal<ConnectionInformation> CONNECTION_INFORMATION = new ThreadLocal<>();
+
+   static class ConnectionInformation {
+      String uri, user, password;
+
+      private ConnectionInformation(String uri, String user, String password) {
+         this.uri = uri;
+         this.user = user;
+         this.password = password;
+      }
+   }
 
    public String getBrokerURL() {
       return brokerURL;
@@ -92,14 +104,18 @@ public class ConnectionAbstract extends InputAbstract {
       return this;
    }
 
-   public String getProtocol() {
+   public ConnectionProtocol getProtocol() {
       return protocol;
    }
 
-   public void setProtocol(String protocol) {
+   public void setProtocol(ConnectionProtocol protocol) {
       this.protocol = protocol;
    }
 
+   public void setProtocol(String protocol) {
+      this.protocol = ConnectionProtocol.fromString(protocol);
+   }
+
    @SuppressWarnings("StringEquality")
    @Override
    public Object execute(ActionContext context) throws Exception {
@@ -126,6 +142,7 @@ public class ConnectionAbstract extends InputAbstract {
    }
 
    protected ConnectionFactory createConnectionFactory() throws Exception {
+      recoverConnectionInformation();
       return createConnectionFactory(brokerURL, user, password, clientID, protocol);
    }
 
@@ -133,10 +150,10 @@ public class ConnectionAbstract extends InputAbstract {
                                                        String user,
                                                        String password,
                                                        String clientID,
-                                                       String protocol) throws Exception {
-      if (protocol.equals("core")) {
+                                                       ConnectionProtocol protocol) throws Exception {
+      if (protocol == ConnectionProtocol.CORE) {
          return createCoreConnectionFactory(brokerURL, user, password, clientID);
-      } else if (protocol.equals("amqp")) {
+      } else if (protocol == ConnectionProtocol.AMQP) {
          return createAMQPConnectionFactory(brokerURL, user, password, clientID);
       } else {
          throw new IllegalStateException("protocol " + protocol + " not supported");
@@ -157,68 +174,132 @@ public class ConnectionAbstract extends InputAbstract {
       }
 
       try {
-         Connection connection = cf.createConnection();
-         connection.close();
+         tryConnect(brokerURL, user, password, cf);
          return cf;
       } catch (JMSSecurityException e) {
          // if a security exception will get the user and password through an input
          getActionContext().err.println("Connection failed::" + e.getMessage());
-         cf = new JmsConnectionFactory(inputUser(user), inputPassword(password), brokerURL);
+         user = inputUser(user);
+         password = inputPassword(password);
+         cf = new JmsConnectionFactory(user, password, brokerURL);
          if (clientID != null) {
             cf.setClientID(clientID);
          }
+         try {
+            tryConnect(brokerURL, user, password, cf);
+         } catch (Exception e2) {
+            e.printStackTrace();
+         }
          return cf;
       } catch (JMSException e) {
          // if a connection exception will ask for the URL, user and password
          getActionContext().err.println("Connection failed::" + e.getMessage());
-         cf = new JmsConnectionFactory(inputUser(user), inputPassword(password), inputBrokerURL(brokerURL));
+         brokerURL = inputBrokerURL(brokerURL);
+         user = inputUser(user);
+         password = inputPassword(password);
+         cf = new JmsConnectionFactory(user, password, brokerURL);
          if (clientID != null) {
             cf.setClientID(clientID);
          }
+         try {
+            tryConnect(brokerURL, user, password, cf);
+         } catch (Exception e2) {
+            e2.printStackTrace();
+         }
          return cf;
       }
    }
 
    protected ActiveMQConnectionFactory createCoreConnectionFactory() {
+      recoverConnectionInformation();
       return createCoreConnectionFactory(brokerURL, user, password, clientID);
    }
 
+   private void recoverConnectionInformation() {
+      if (CONNECTION_INFORMATION.get() != null) {
+         ConnectionInformation connectionInfo = CONNECTION_INFORMATION.get();
+         if (this.user == null) {
+            this.user  = connectionInfo.user;
+         }
+         if (this.password == null) {
+            this.password  = connectionInfo.password;
+         }
+         if (this.brokerURL == null) {
+            this.brokerURL  = connectionInfo.uri;
+         }
+
+      }
+   }
+
+   void saveConnectionInfo(String brokerURL, String user, String password) {
+      if (Shell.inShell() && CONNECTION_INFORMATION.get() == null) {
+         CONNECTION_INFORMATION.set(new ConnectionInformation(brokerURL, user, password));
+         System.out.println("CLI connected to broker " + brokerURL + ", user:" + user);
+      }
+   }
+
    protected ActiveMQConnectionFactory createCoreConnectionFactory(String brokerURL,
                                                                    String user,
                                                                    String password,
                                                                    String clientID) {
+      if (brokerURL.startsWith("amqp://")) {
+         // replacing amqp:// by tcp://
+         brokerURL = "tcp" + brokerURL.substring(4);
+      }
+
       ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(brokerURL, user, password);
       if (clientID != null) {
          getActionContext().out.println("Consumer:: clientID = " + clientID);
          cf.setClientID(clientID);
       }
       try {
-         Connection connection = cf.createConnection();
-         connection.close();
+         tryConnect(brokerURL, user, password, cf);
          return cf;
       } catch (JMSSecurityException e) {
          // if a security exception will get the user and password through an input
          if (getActionContext() != null) {
             getActionContext().err.println("Connection failed::" + e.getMessage());
          }
-         cf = new ActiveMQConnectionFactory(brokerURL, inputUser(user), inputPassword(password));
+         user = inputUser(user);
+         password = inputPassword(password);
+         cf = new ActiveMQConnectionFactory(brokerURL, user, password);
          if (clientID != null) {
             cf.setClientID(clientID);
          }
+         try {
+            tryConnect(brokerURL, user, password, cf);
+         } catch (Exception e2) {
+         }
          return cf;
       } catch (JMSException e) {
          // if a connection exception will ask for the URL, user and password
          if (getActionContext() != null) {
             getActionContext().err.println("Connection failed::" + e.getMessage());
          }
-         cf = new ActiveMQConnectionFactory(inputBrokerURL(brokerURL), inputUser(user), inputPassword(password));
+         brokerURL = inputBrokerURL(brokerURL);
+         user = inputUser(user);
+         password = inputPassword(password);
+         cf = new ActiveMQConnectionFactory(brokerURL, user, password);
          if (clientID != null) {
             cf.setClientID(clientID);
          }
+         try {
+            tryConnect(brokerURL, user, password, cf);
+         } catch (Exception e2) {
+         }
          return cf;
       }
    }
 
+   private void tryConnect(String brokerURL,
+                          String user,
+                          String password,
+                          ConnectionFactory cf) throws JMSException {
+      Connection connection = cf.createConnection();
+      connection.close();
+      saveConnectionInfo(brokerURL, user, password);
+   }
+
    private String inputBrokerURL(String defaultValue) {
       return input("--url", "Type in the connection URL for a retry (e.g. tcp://localhost:61616)", defaultValue);
    }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionProtocol.java
similarity index 54%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionProtocol.java
index e34e376deb..3fb9c14bdd 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionProtocol.java
@@ -14,28 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.messages;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import picocli.CommandLine;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+public enum ConnectionProtocol {
+   AMQP("AMQP"), CORE("CORE");
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
 
-      BrokerDTO broker = getBrokerDTO();
+   private final String name;
 
-      File file = broker.server.getConfigurationFile().getParentFile();
+   ConnectionProtocol(String name) {
+      this.name = name;
+   }
 
-      File killFile = new File(file, "KILL_ME");
+   @Override
+   public String toString() {
+      return name;
+   }
 
-      killFile.createNewFile();
+   public static ConnectionProtocol fromString(String value) {
+      return ConnectionProtocol.valueOf(value.toUpperCase());
+   }
 
-      return null;
+   public static class ProtocolConverter implements CommandLine.ITypeConverter<ConnectionProtocol> {
+      @Override
+      public ConnectionProtocol convert(String value) throws Exception {
+         return ConnectionProtocol.fromString(value);
+      }
    }
 }
+
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 fa7dd1854b..e137d04165 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
@@ -26,27 +26,27 @@ import javax.jms.Session;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.factory.serialize.MessageSerializer;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "consumer", description = "Consume messages from a queue.")
 public class Consumer extends DestAbstract {
 
-   @Option(name = "--durable", description = "Whether the consumer's subscription will be durable.")
+   @Option(names = "--durable", description = "Whether the consumer's subscription will be durable.")
    boolean durable = false;
 
-   @Option(name = "--break-on-null", description = "Stop consuming when a null message is received.")
+   @Option(names = "--break-on-null", description = "Stop consuming when a null message is received.")
    boolean breakOnNull = false;
 
-   @Option(name = "--receive-timeout", description = "Timeout for receiving messages (in milliseconds).")
+   @Option(names = "--receive-timeout", description = "Timeout for receiving messages (in milliseconds).")
    int receiveTimeout = 3000;
 
-   @Option(name = "--filter", description = "The message filter.")
+   @Option(names = "--filter", description = "The message filter.")
    String filter;
 
-   @Option(name = "--data", description = "Serialize the messages to the specified file as they are consumed.")
+   @Option(names = "--data", description = "Serialize the messages to the specified file as they are consumed.")
    String file;
 
    @Override
@@ -111,7 +111,7 @@ public class Consumer extends DestAbstract {
 
          connection.start();
 
-         int received = 0;
+         long received = 0;
 
          for (ConsumerThread thread : threadsArray) {
             thread.join();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConsumerThread.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConsumerThread.java
index f80a628bdd..f2bee77ba4 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConsumerThread.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ConsumerThread.java
@@ -33,7 +33,7 @@ import java.util.concurrent.CountDownLatch;
 
 public class ConsumerThread extends Thread {
 
-   int messageCount = 1000;
+   long messageCount = 1000;
    int receiveTimeOut = 3000;
    Destination destination;
    Session session;
@@ -259,7 +259,7 @@ public class ConsumerThread extends Thread {
       return this;
    }
 
-   public ConsumerThread setMessageCount(int messageCount) {
+   public ConsumerThread setMessageCount(long messageCount) {
       this.messageCount = messageCount;
       return this;
    }
@@ -278,7 +278,7 @@ public class ConsumerThread extends Thread {
       return this;
    }
 
-   public int getMessageCount() {
+   public long getMessageCount() {
       return messageCount;
    }
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
index e50cef6834..7ded6ecb77 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
@@ -21,29 +21,33 @@ import javax.jms.Destination;
 import javax.jms.JMSException;
 import javax.jms.Session;
 
-import com.github.rvesse.airline.annotations.Option;
+import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.factory.serialize.MessageSerializer;
 import org.apache.activemq.artemis.cli.factory.serialize.XMLMessageSerializer;
 import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
+import picocli.CommandLine.Option;
 
 public class DestAbstract extends ConnectionAbstract {
 
-   @Option(name = "--destination", description = "Destination to be used. It can be prefixed with queue:// or topic:// and can be an FQQN in the form of <address>::<queue>. Default: queue://TEST.")
+   @Option(names = "--destination", description = "Destination to be used. It can be prefixed with queue:// or topic:// and can be an FQQN in the form of <address>::<queue>. Default: queue://TEST.")
    String destination = "queue://TEST";
 
-   @Option(name = "--message-count", description = "Number of messages to act on. Default: 1000.")
-   int messageCount = 1000;
+   @Option(names = "--message-count", description = "Number of messages to act on. Default: 1000.")
+   long messageCount = 1000;
 
-   @Option(name = "--sleep", description = "Time wait between each message.")
+   @Option(names = "--sleep", description = "Time wait between each message.")
    int sleep = 0;
 
-   @Option(name = "--txt-size", description = "Transaction batch size.")
-   int txBatchSize;
+   @Option(names = {"--txt-size"}, description = "Transaction batch size. (deprecated)", hidden = true)
+   int oldBatchSize;
 
-   @Option(name = "--threads", description = "Number of threads to use. Default: 1.")
+   @Option(names = {"--commit-interval"}, description = "Transaction batch size.")
+   protected int txBatchSize;
+
+   @Option(names = "--threads", description = "Number of threads to use. Default: 1.")
    int threads = 1;
 
-   @Option(name = "--serializer", description = "The class name of the custom serializer implementation to use intead of the default.")
+   @Option(names = "--serializer", description = "The class name of the custom serializer implementation to use intead of the default.")
    String serializer;
 
    protected MessageSerializer getMessageSerializer() {
@@ -56,7 +60,7 @@ public class DestAbstract extends ConnectionAbstract {
          }
       }
 
-      if (!protocol.equalsIgnoreCase("CORE")) {
+      if (protocol != ConnectionProtocol.CORE) {
          System.err.println("Default Serializer does not support: " + protocol + " protocol");
          return null;
       }
@@ -93,7 +97,7 @@ public class DestAbstract extends ConnectionAbstract {
       return this;
    }
 
-   public int getMessageCount() {
+   public long getMessageCount() {
       return messageCount;
    }
 
@@ -137,4 +141,16 @@ public class DestAbstract extends ConnectionAbstract {
       this.serializer = serializer;
       return this;
    }
+
+   @Override
+   public Object execute(ActionContext context) throws Exception {
+      super.execute(context);
+
+      if (oldBatchSize > 0) {
+         context.out.println("--txt-size is deprecated, please use --commit-interval");
+         txBatchSize = oldBatchSize;
+      }
+
+      return null;
+   }
 }
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 eed6f5ef24..20235f7e0c 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
@@ -27,38 +27,38 @@ import javax.jms.Session;
 import java.io.FileInputStream;
 import java.io.InputStream;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.factory.serialize.MessageSerializer;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "producer", description = "Send message(s) to a broker.")
 public class Producer extends DestAbstract {
 
    public static final String DEMO_TEXT = "demo.txt";
 
-   @Option(name = "--non-persistent", description = "Send messages non persistently.")
+   @Option(names = "--non-persistent", description = "Send messages non persistently.")
    boolean nonpersistent = false;
 
-   @Option(name = "--message-size", description = "Size of each bytesMessage. The producer will use JMS BytesMessage.")
+   @Option(names = "--message-size", description = "Size of each bytesMessage. The producer will use JMS BytesMessage.")
    int messageSize = 0;
 
-   @Option(name = "--message", description = "Content of each textMessage. The producer will use JMS TextMessage.")
+   @Option(names = "--message", description = "Content of each textMessage. The producer will use JMS TextMessage.")
    String message = null;
 
-   @Option(name = "--text-size", description = "Size of each textMessage. The producer will use JMS TextMessage.")
+   @Option(names = "--text-size", description = "Size of each textMessage. The producer will use JMS TextMessage.")
    int textMessageSize;
 
-   @Option(name = "--object-size", description = "Size of each ObjectMessage. The producer will use JMS ObjectMessage.")
+   @Option(names = "--object-size", description = "Size of each ObjectMessage. The producer will use JMS ObjectMessage.")
    int objectSize;
 
-   @Option(name = "--msgttl", description = "TTL for each message.")
+   @Option(names = "--msgttl", description = "TTL for each message.")
    long msgTTL = 0L;
 
-   @Option(name = "--group", description = "Message Group to be used.")
+   @Option(names = "--group", description = "Message Group to be used.")
    String msgGroupID = null;
 
-   @Option(name = "--data", description = "Messages will be read from the specified file. Other message options will be ignored.")
+   @Option(names = "--data", description = "Messages will be read from the specified file. Other message options will be ignored.")
    String file = null;
 
    public boolean isNonpersistent() {
@@ -216,7 +216,7 @@ public class Producer extends DestAbstract {
                thread.start();
             }
 
-            int messagesProduced = 0;
+            long messagesProduced = 0;
             for (ProducerThread thread : threadsArray) {
                thread.join();
                messagesProduced += thread.getSentCount();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
index 19fbba9a09..efff71af67 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
@@ -28,7 +28,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.activemq.artemis.utils.ReusableLatch;
 
@@ -37,7 +37,7 @@ public class ProducerThread extends Thread {
    protected final Session session;
 
    boolean verbose;
-   int messageCount = 1000;
+   long messageCount = 1000;
    boolean runIndefinitely = false;
    Destination destination;
    int sleep = 0;
@@ -50,7 +50,7 @@ public class ProducerThread extends Thread {
    int transactionBatchSize;
 
    int transactions = 0;
-   final AtomicInteger sentCount = new AtomicInteger(0);
+   final AtomicLong sentCount = new AtomicLong(0);
    String message = null;
    String messageText = null;
    String payloadUrl = null;
@@ -146,7 +146,7 @@ public class ProducerThread extends Thread {
       }
    }
 
-   protected Message createMessage(int i, String threadName) throws Exception {
+   protected Message createMessage(long i, String threadName) throws Exception {
       Message answer;
       if (payload != null) {
          answer = session.createBytesMessage();
@@ -188,12 +188,12 @@ public class ProducerThread extends Thread {
          answer.setStringProperty("JMSXGroupID", msgGroupID);
       }
 
-      answer.setIntProperty("count", i);
+      answer.setLongProperty("count", i);
       answer.setStringProperty("ThreadSent", threadName);
       return answer;
    }
 
-   private String readInputStream(InputStream is, int size, int messageNumber) throws IOException {
+   private String readInputStream(InputStream is, int size, long messageNumber) throws IOException {
       try (InputStreamReader reader = new InputStreamReader(is)) {
          char[] buffer;
          if (size > 0) {
@@ -214,11 +214,11 @@ public class ProducerThread extends Thread {
       }
    }
 
-   private String createDefaultMessage(int messageNumber) {
+   private String createDefaultMessage(long messageNumber) {
       return "test message: " + messageNumber;
    }
 
-   public ProducerThread setMessageCount(int messageCount) {
+   public ProducerThread setMessageCount(long messageCount) {
       this.messageCount = messageCount;
       return this;
    }
@@ -232,11 +232,11 @@ public class ProducerThread extends Thread {
       return this;
    }
 
-   public int getMessageCount() {
+   public long getMessageCount() {
       return messageCount;
    }
 
-   public int getSentCount() {
+   public long getSentCount() {
       return sentCount.get();
    }
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Transfer.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Transfer.java
index 8674b3e9ba..abb1bf6e2a 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Transfer.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Transfer.java
@@ -28,84 +28,84 @@ import javax.jms.Queue;
 import javax.jms.Session;
 import javax.jms.Topic;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.Pair;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.InputAbstract;
 import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
 import org.apache.qpid.jms.JmsConnectionFactory;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "transfer", description = "Move messages from one destination towards another destination.")
 public class Transfer extends InputAbstract {
 
-   @Option(name = "--source-url", description = "URL for the source broker. Default: build URL from 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the default cannot be parsed.")
+   @Option(names = "--source-url", description = "URL for the source broker. Default: build URL from 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the default cannot be parsed.")
    protected String sourceURL = DEFAULT_BROKER_URL;
 
-   @Option(name = "--source-acceptor", description = "Acceptor used to build URL towards the broker. Default: 'artemis'.")
+   @Option(names = "--source-acceptor", description = "Acceptor used to build URL towards the broker. Default: 'artemis'.")
    protected String sourceAcceptor = DEFAULT_BROKER_ACCEPTOR;
 
-   @Option(name = "--source-user", description = "User used to connect to source broker.")
+   @Option(names = "--source-user", description = "User used to connect to source broker.")
    protected String sourceUser;
 
-   @Option(name = "--source-password", description = "Password used to connect to source broker.")
+   @Option(names = "--source-password", description = "Password used to connect to source broker.")
    protected String sourcePassword;
 
-   @Option(name = "--target-url", description = "URL for the target broker. Default: build URL from 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the default cannot be parsed.")
+   @Option(names = "--target-url", description = "URL for the target broker. Default: build URL from 'artemis' acceptor defined in the broker.xml or tcp://localhost:61616 if the default cannot be parsed.")
    protected String targetURL = DEFAULT_BROKER_URL;
 
-   @Option(name = "--target-user", description = "User used to connect to target broker.")
+   @Option(names = "--target-user", description = "User used to connect to target broker.")
    protected String targetUser;
 
-   @Option(name = "--target-password", description = "Password used to connect to target broker.")
+   @Option(names = "--target-password", description = "Password used to connect to target broker.")
    protected String targetPassword;
 
-   @Option(name = "--receive-timeout", description = "Amount of time (in milliseconds) to wait before giving up the receiving loop; 0 means no wait, -1 means wait forever. Default: 5000.")
+   @Option(names = "--receive-timeout", description = "Amount of time (in milliseconds) to wait before giving up the receiving loop; 0 means no wait, -1 means wait forever. Default: 5000.")
    int receiveTimeout = 5000;
 
-   @Option(name = "--source-client-id", description = "ClientID to be associated with source connection.")
+   @Option(names = "--source-client-id", description = "ClientID to be associated with source connection.")
    String sourceClientID;
 
-   @Option(name = "--source-protocol", description = "Protocol used. Valid values are amqp or core. Default: core.")
+   @Option(names = "--source-protocol", description = "Protocol used. Valid values are amqp or core. Default: core.")
    String sourceProtocol = "core";
 
-   @Option(name = "--source-queue", description = "Source JMS queue to be used. Cannot be set with --source-topic.")
+   @Option(names = "--source-queue", description = "Source JMS queue to be used. Cannot be set with --source-topic.")
    String sourceQueue;
 
-   @Option(name = "--shared-durable-subscription", description = "Name of a shared subscription name to be used on the source topic.")
+   @Option(names = "--shared-durable-subscription", description = "Name of a shared subscription name to be used on the source topic.")
    String sharedDurableSubscription;
 
-   @Option(name = "--shared-subscription", description = "Name of a shared subscription name to be used on the source topic.")
+   @Option(names = "--shared-subscription", description = "Name of a shared subscription name to be used on the source topic.")
    String sharedSubscription;
 
-   @Option(name = "--durable-consumer", description = "Name of a durable consumer to be used on the source topic.")
+   @Option(names = "--durable-consumer", description = "Name of a durable consumer to be used on the source topic.")
    String durableConsumer;
 
-   @Option(name = "--no-Local", description = "Use noLocal when applicable on topic operation")
+   @Option(names = "--no-Local", description = "Use noLocal when applicable on topic operation")
    boolean noLocal;
 
-   @Option(name = "--source-topic", description = "Source JMS topic to be used. Cannot bet set with --source-queue.")
+   @Option(names = "--source-topic", description = "Source JMS topic to be used. Cannot bet set with --source-queue.")
    String sourceTopic;
 
-   @Option(name = "--source-filter", description = "Filter to be used with the source consumer.")
+   @Option(names = "--source-filter", description = "Filter to be used with the source consumer.")
    String filter;
 
-   @Option(name = "--target-protocol", description = "Protocol used. Valid values are amqp or core. Default: core.")
+   @Option(names = "--target-protocol", description = "Protocol used. Valid values are amqp or core. Default: core.")
    String targetProtocol = "core";
 
-   @Option(name = "--commit-interval", description = "Transaction batch interval.")
+   @Option(names = {"--commit-interval"}, description = "Transaction batch size.")
    int commitInterval = 1000;
 
-   @Option(name = "--copy", description = "If this option is chosen we will perform a copy by rolling back the original transaction on the source.")
+   @Option(names = "--copy", description = "If this option is chosen we will perform a copy by rolling back the original transaction on the source.")
    boolean copy;
 
-   @Option(name = "--target-queue", description = "Target JMS queue to be used. Cannot be set with --target-topic.")
+   @Option(names = "--target-queue", description = "Target JMS queue to be used. Cannot be set with --target-topic.")
    String targetQueue;
 
-   @Option(name = "--target-topic", description = "Target JMS topic to be used. Cannot bet set with --target-queue.")
+   @Option(names = "--target-topic", description = "Target JMS topic to be used. Cannot bet set with --target-queue.")
    String targetTopic;
 
-   @Option(name = "--message-count", description = "Number of messages to transfer.")
+   @Option(names = "--message-count", description = "Number of messages to transfer.")
    int messageCount = Integer.MAX_VALUE;
 
    public String getSourceURL() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfClientCommand.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfClientCommand.java
index e258f52be8..ca98a63dc7 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfClientCommand.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfClientCommand.java
@@ -25,79 +25,77 @@ import java.util.concurrent.LinkedTransferQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.LockSupport;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import io.netty.channel.DefaultEventLoop;
 import io.netty.channel.DefaultEventLoopGroup;
 import io.netty.channel.EventLoop;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.cli.commands.messages.ConnectionProtocol;
 import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "client", description = "Produce messages to and consume messages from a broker instance.")
 public class PerfClientCommand extends PerfCommand {
 
-   @Option(name = "--tx", description = "Perform Message::acknowledge per each message received. Default: disabled.")
+   @Option(names = "--tx", description = "Perform Message::acknowledge per each message received. Default: disabled.")
    protected boolean transaction;
 
-   @Option(name = "--shared", description = "Create a shared subscription. Default: 0.")
+   @Option(names = "--shared", description = "Create a shared subscription. Default: 0.")
    protected int sharedSubscription = 0;
 
-   @Option(name = "--durable", description = "Enabled durable subscription. Default: disabled.")
+   @Option(names = "--durable", description = "Enabled durable subscription. Default: disabled.")
    protected boolean durableSubscription = false;
 
-   @Option(name = "--consumer-connections", description = "Number of consumer connections to be used. Default: same as the total number of consumers")
+   @Option(names = "--consumer-connections", description = "Number of consumer connections to be used. Default: same as the total number of consumers")
    protected int consumerConnections = 0;
 
-   @Option(name = "--consumers", description = "Number of consumer to use for each generated destination. Default: 1.")
+   @Option(names = "--consumers", description = "Number of consumer to use for each generated destination. Default: 1.")
    protected int consumersPerDestination = 1;
 
-   @Option(name = "--persistent", description = "Send messages persistently. Default: non persistent")
+   @Option(names = "--persistent", description = "Send messages persistently. Default: non persistent")
    protected boolean persistent = false;
 
-   @Option(name = "--message-size", description = "Size of each bytesMessage. Default: is 1024.")
+   @Option(names = "--message-size", description = "Size of each bytesMessage. Default: is 1024.")
    protected int messageSize = 1024;
 
-   @Option(name = "--rate", description = "Expected total message rate. Default: unbounded.")
+   @Option(names = "--rate", description = "Expected total message rate. Default: unbounded.")
    protected Long rate = null;
 
-   @Option(name = "--ttl", description = "TTL for each message.")
+   @Option(names = "--ttl", description = "TTL for each message.")
    protected long ttl = 0L;
 
-   @Option(name = "--group", description = "Message Group to be used.")
+   @Option(names = "--group", description = "Message Group to be used.")
    protected String msgGroupID = null;
 
-   @Option(name = "--shared-connections", description = "Create --threads shared connections among producers. Default: not shared.")
+   @Option(names = "--shared-connections", description = "Create --threads shared connections among producers. Default: not shared.")
    protected boolean sharedConnections = false;
 
-   @Option(name = "--tx-size", description = "Transaction size.")
-   protected long txSize;
-
-   @Option(name = "--producers", description = "Number of producers to use for each generated destination. Default: 1")
+   @Option(names = "--producers", description = "Number of producers to use for each generated destination. Default: 1")
    protected int producersPerDestination = 1;
 
-   @Option(name = "--threads", description = "Number of worker threads to schedule producer load tasks. Default: 1.")
+   @Option(names = "--threads", description = "Number of worker threads to schedule producer load tasks. Default: 1.")
    protected int threads = 1;
 
-   @Option(name = "--max-pending", description = "How many not yet completed messages can exists. Default: 1.")
+   @Option(names = "--max-pending", description = "How many not yet completed messages can exists. Default: 1.")
    protected long maxPending = 1;
 
-   @Option(name = "--consumer-url", description = "The url used for MessageListener(s) connections. Default: same as --url.")
+   @Option(names = "--consumer-url", description = "The url used for MessageListener(s) connections. Default: same as --url.")
    protected String consumerUrl = null;
 
-   @Option(name = "--consumer-protocol", description = "The protocol used for MessageListener(s) connections. Default: same as --protocol.")
-   protected String consumerProtocol = null;
+   @Option(names = "--consumer-protocol", description = "The protocol used for MessageListener(s) connections. Default: same as --protocol. Valid values are ${COMPLETION-CANDIDATES}", converter = ConnectionProtocol.ProtocolConverter.class)
+   protected ConnectionProtocol consumerProtocol = null;
 
-   @Option(name = "--enable-msg-id", description = "Set JMS messageID per-message. Default: disabled.")
+   @Option(names = "--enable-msg-id", description = "Set JMS messageID per-message. Default: disabled.")
    protected boolean enableMessageID;
 
-   @Option(name = "--enable-timestamp", description = "Set JMS timestamp per-message. Default: disabled.")
+   @Option(names = "--enable-timestamp", description = "Set JMS timestamp per-message. Default: disabled.")
    protected boolean enableTimestamp;
 
    private volatile BenchmarkService producerBenchmark;
 
    @Override
    protected void onExecuteBenchmark(final ConnectionFactory producerConnectionFactory, final Destination[] jmsDestinations, final ActionContext context) throws Exception {
-      final String listenerProtocol = this.consumerProtocol != null ? this.consumerProtocol : getProtocol();
+      final ConnectionProtocol listenerProtocol = this.consumerProtocol != null ? this.consumerProtocol : protocol;
       final String listenerUrl = this.consumerUrl != null ? this.consumerUrl : brokerURL;
       final ConnectionFactory consumerConnectionFactory = createConnectionFactory(listenerUrl, user, password, null, listenerProtocol);
       if (consumerConnections == 0) {
@@ -152,7 +150,7 @@ public class PerfClientCommand extends PerfCommand {
             .setDestinations(jmsDestinations)
             .setFactory(producerConnectionFactory)
             .setTtl(ttl)
-            .setTransactionCapacity(txSize)
+            .setTransactionCapacity(commitInterval)
             .setGroup(msgGroupID)
             .setProducers(producersPerDestination)
             .setMessageRate(rate)
@@ -316,15 +314,6 @@ public class PerfClientCommand extends PerfCommand {
       return this;
    }
 
-   public long getTxSize() {
-      return txSize;
-   }
-
-   public PerfClientCommand setTxSize(long txSize) {
-      this.txSize = txSize;
-      return this;
-   }
-
    public int getProducersPerDestination() {
       return producersPerDestination;
    }
@@ -362,11 +351,11 @@ public class PerfClientCommand extends PerfCommand {
    }
 
    public String getConsumerProtocol() {
-      return consumerProtocol;
+      return consumerProtocol.toString();
    }
 
    public PerfClientCommand setConsumerProtocol(String consumerProtocol) {
-      this.consumerProtocol = consumerProtocol;
+      this.consumerProtocol = ConnectionProtocol.fromString(consumerProtocol);
       return this;
    }
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfCommand.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfCommand.java
index 2aa4f84fa1..a8f3011dd1 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfCommand.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfCommand.java
@@ -26,38 +26,44 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.LockSupport;
 
-import com.github.rvesse.airline.annotations.Arguments;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
 import org.apache.activemq.artemis.cli.commands.messages.DestAbstract;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
 
 import static java.util.Collections.singletonList;
 
 public abstract class PerfCommand extends ConnectionAbstract {
 
-   @Option(name = "--show-latency", description = "Show latencies at interval on output. Default: disabled.")
+   @Option(names = "--show-latency", description = "Show latencies at interval on output. Default: disabled.")
    protected boolean showLatency = false;
 
-   @Option(name = "--json", description = "Report file name. Default: none.")
+   @Option(names = "--json", description = "Report file name. Default: none.")
    protected String reportFileName = null;
 
-   @Option(name = "--hdr", description = "HDR Histogram Report file name. Default: none.")
+   @Option(names = "--hdr", description = "HDR Histogram Report file name. Default: none.")
    protected String hdrFileName = null;
 
-   @Option(name = "--duration", description = "Test duration (in seconds). Default: 0.")
+   @Option(names = "--duration", description = "Test duration (in seconds). Default: 0.")
    protected int duration = 0;
 
-   @Option(name = "--warmup", description = "Warmup time (in seconds). Default: 0.")
+   @Option(names = "--warmup", description = "Warmup time (in seconds). Default: 0.")
    protected int warmup = 0;
 
-   @Option(name = "--message-count", description = "Total number of messages. Default: 0.")
+   @Option(names = "--message-count", description = "Total number of messages. Default: 0.")
    protected long messageCount = 0;
 
-   @Option(name = "--num-destinations", description = "If present, generate --num-destinations for each destination name, using it as a prefix and adding a number [0,--num-destinations) as suffix. Default: none.")
+   @Option(names = "--num-destinations", description = "If present, generate --num-destinations for each destination name, using it as a prefix and adding a number [0,--num-destinations) as suffix. Default: none.")
    protected int numDestinations = 1;
 
-   @Arguments(description = "List of destination names. Each name can be prefixed with queue:// or topic:// and can be an FQQN in the form of <address>::<queue>. Default: queue://TEST.")
+   @Option(names = "--tx-size", description = "Transaction size.", hidden = true)
+   protected long txSize;
+
+   @Option(names = "--commit-interval", description = "Transaction size.")
+   protected long commitInterval;
+
+   @Parameters(description = "List of destination names. Each name can be prefixed with queue:// or topic:// and can be an FQQN in the form of <address>::<queue>. Default: queue://TEST.")
    protected List<String> destinations;
 
    private final CountDownLatch completed = new CountDownLatch(1);
@@ -65,7 +71,11 @@ public abstract class PerfCommand extends ConnectionAbstract {
    @Override
    public Object execute(ActionContext context) throws Exception {
       super.execute(context);
-      final ConnectionFactory factory = createConnectionFactory(brokerURL, user, password, null, getProtocol());
+      if (txSize > 0) {
+         System.out.println("--tx-size is deprecated, please use --commit-interval");
+         commitInterval = txSize;
+      }
+      final ConnectionFactory factory = createConnectionFactory(brokerURL, user, password, null, protocol);
       final Destination[] jmsDestinations = lookupDestinations(factory, destinations, numDestinations);
       Runtime.getRuntime().addShutdownHook(new Thread(() -> {
          onInterruptBenchmark();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfConsumerCommand.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfConsumerCommand.java
index 5b307b738a..1465306105 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfConsumerCommand.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfConsumerCommand.java
@@ -21,26 +21,26 @@ import javax.jms.Destination;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.LockSupport;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "consumer", description = "Consume messages from a queue.")
 public class PerfConsumerCommand extends PerfCommand {
 
-   @Option(name = "--tx", description = "Individually acknowledge each message received. Default: disabled.")
+   @Option(names = "--tx", description = "Individually acknowledge each message received. Default: disabled.")
    protected boolean transaction;
 
-   @Option(name = "--shared", description = "Create shared subscription. Default: 0.")
+   @Option(names = "--shared", description = "Create shared subscription. Default: 0.")
    protected int sharedSubscription = 0;
 
-   @Option(name = "--durable", description = "Enabled durable subscription. Default: disabled.")
+   @Option(names = "--durable", description = "Enabled durable subscription. Default: disabled.")
    protected boolean durableSubscription = false;
 
-   @Option(name = "--num-connections", description = "Number of connections to be used. Default: same as the total number of consumers.")
+   @Option(names = "--num-connections", description = "Number of connections to be used. Default: same as the total number of consumers.")
    protected int connections = 0;
 
-   @Option(name = "--consumers", description = "Number of consumer to use for each generated destination. Default: 1.")
+   @Option(names = "--consumers", description = "Number of consumer to use for each generated destination. Default: 1.")
    protected int consumersPerDestination = 1;
 
    private BenchmarkService benchmark;
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfGroup.java
similarity index 57%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfGroup.java
index e34e376deb..31326a8740 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfGroup.java
@@ -14,28 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.messages.perf;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@Command(name = "perf", description = "use 'help perf' for sub commands list", subcommands = {PerfClientCommand.class, PerfProducerCommand.class, PerfConsumerCommand.class})
+public class PerfGroup implements Runnable {
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
+   CommandLine commandLine;
 
-      killFile.createNewFile();
+   public PerfGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-      return null;
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "perf");
    }
+
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfProducerCommand.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfProducerCommand.java
index 4440ef5f8f..0e8d58464f 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfProducerCommand.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/perf/PerfProducerCommand.java
@@ -25,49 +25,46 @@ import java.util.concurrent.LinkedTransferQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.LockSupport;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import io.netty.channel.DefaultEventLoop;
 import io.netty.channel.DefaultEventLoopGroup;
 import io.netty.channel.EventLoop;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "producer", description = "Send messages to a broker.")
 public class PerfProducerCommand extends PerfCommand {
-   @Option(name = "--persistent", description = "Send messages persistently. Default: non persistent.")
+   @Option(names = "--persistent", description = "Send messages persistently. Default: non persistent.")
    protected boolean persistent = false;
 
-   @Option(name = "--message-size", description = "Size of each bytesMessage. Default: 1024.")
+   @Option(names = "--message-size", description = "Size of each bytesMessage. Default: 1024.")
    protected int messageSize = 1024;
 
-   @Option(name = "--rate", description = "Expected total message rate. Default: unbounded.")
+   @Option(names = "--rate", description = "Expected total message rate. Default: unbounded.")
    protected Long rate = null;
 
-   @Option(name = "--ttl", description = "TTL for each message.")
+   @Option(names = "--ttl", description = "TTL for each message.")
    protected long ttl = 0L;
 
-   @Option(name = "--group", description = "Message Group to be used.")
+   @Option(names = "--group", description = "Message Group to be used.")
    protected String msgGroupID = null;
 
-   @Option(name = "--shared-connections", description = "Create --threads shared connections among producers. Default: not shared.")
+   @Option(names = "--shared-connections", description = "Create --threads shared connections among producers. Default: not shared.")
    protected boolean sharedConnections = false;
 
-   @Option(name = "--tx-size", description = "Transaction size.")
-   protected long txSize;
-
-   @Option(name = "--producers", description = "Number of producers to use for each generated destination. Default: 1.")
+   @Option(names = "--producers", description = "Number of producers to use for each generated destination. Default: 1.")
    protected int producersPerDestination = 1;
 
-   @Option(name = "--threads", description = "Number of worker threads to schedule producer load tasks. Default: 1.")
+   @Option(names = "--threads", description = "Number of worker threads to schedule producer load tasks. Default: 1.")
    protected int threads = 1;
 
-   @Option(name = "--max-pending", description = "How many not yet completed messages can exists. Default is 1.")
+   @Option(names = "--max-pending", description = "How many not yet completed messages can exists. Default is 1.")
    protected long maxPending = 1;
 
-   @Option(name = "--enable-msg-id", description = "Enable setting JMS messageID per-message. Default: disabled.")
+   @Option(names = "--enable-msg-id", description = "Enable setting JMS messageID per-message. Default: disabled.")
    protected boolean enableMessageID;
 
-   @Option(name = "--enable-timestamp", description = "Enable setting JMS timestamp per-message. Default: disabled.")
+   @Option(names = "--enable-timestamp", description = "Enable setting JMS timestamp per-message. Default: disabled.")
    protected boolean enableTimestamp;
 
    protected volatile BenchmarkService benchmark;
@@ -114,7 +111,7 @@ public class PerfProducerCommand extends PerfCommand {
          .setDestinations(jmsDestinations)
          .setFactory(factory)
          .setTtl(ttl)
-         .setTransactionCapacity(txSize)
+         .setTransactionCapacity(commitInterval)
          .setGroup(msgGroupID)
          .setProducers(producersPerDestination)
          .setMessageRate(rate)
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/CreateQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/CreateQueue.java
index a015b1f317..29e4a86dfd 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/CreateQueue.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/CreateQueue.java
@@ -17,9 +17,9 @@
 
 package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
 
 @Command(name = "create", description = "Create a queue.")
 public class CreateQueue extends QueueAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/DeleteQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/DeleteQueue.java
index 38e994d7d1..3e90f2592a 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/DeleteQueue.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/DeleteQueue.java
@@ -17,22 +17,22 @@
 
 package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "delete", description = "Delete a queue.")
 public class DeleteQueue extends ConnectionAbstract {
 
-   @Option(name = "--name", description = "The queue's name")
+   @Option(names = "--name", description = "The queue's name")
    String name;
 
-   @Option(name = "--removeConsumers", description = "Whether to delete the queue even if it has active consumers. Default: false.")
+   @Option(names = "--removeConsumers", description = "Whether to delete the queue even if it has active consumers. Default: false.")
    boolean removeConsumers = false;
 
-   @Option(name = "--autoDeleteAddress", description = "Whether to delete the address if this is its only queue.")
+   @Option(names = "--autoDeleteAddress", description = "Whether to delete the address if this is its only queue.")
    boolean autoDeleteAddress = false;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/HelpQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/HelpQueue.java
deleted file mode 100644
index f353bc7f88..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/HelpQueue.java
+++ /dev/null
@@ -1,63 +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.activemq.artemis.cli.commands.queue;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.github.rvesse.airline.help.Help;
-import org.apache.activemq.artemis.cli.commands.Action;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.apache.activemq.artemis.cli.commands.OptionsUtil;
-
-public class HelpQueue extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etc) {
-   }
-
-   @Override
-   public String getBrokerInstance() {
-      return null;
-   }
-
-   @Override
-   public String getBrokerHome() {
-      return null;
-   }
-
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      List<String> commands = new ArrayList<>(1);
-      commands.add("queue");
-      help(global, commands);
-      return null;
-   }
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java
index 09d217da3d..31e6fb9920 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java
@@ -17,17 +17,17 @@
 
 package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.api.core.management.ResourceNames;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "purge", description = "Delete all messages in a queue.")
 public class PurgeQueue extends ConnectionAbstract {
 
-   @Option(name = "--name", description = "The queue's name.")
+   @Option(names = "--name", description = "The queue's name.")
    String name;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueAbstract.java
index 69bb869b4a..5fb757d90b 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueAbstract.java
@@ -16,42 +16,42 @@
  */
 package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine.Option;
 
 public class QueueAbstract extends ConnectionAbstract {
 
-   @Option(name = "--name", description = "The queue's name.")
+   @Option(names = "--name", description = "The queue's name.")
    private String name;
 
-   @Option(name = "--filter", description = "The queue's filter string. Default: null.")
+   @Option(names = "--filter", description = "The queue's filter string. Default: null.")
    private String filter = null;
 
-   @Option(name = "--address", description = "The queue's address. Default: queue's name.")
+   @Option(names = "--address", description = "The queue's address. Default: queue's name.")
    private String address;
 
-   @Option(name = "--durable", description = "The queue is durable. Default: input.")
+   @Option(names = "--durable", description = "The queue is durable. Default: input.")
    private Boolean durable;
 
-   @Option(name = "--no-durable", description = "The queue is not durable. Default: input.")
+   @Option(names = "--no-durable", description = "The queue is not durable. Default: input.")
    private Boolean noDurable;
 
-   @Option(name = "--purge-on-no-consumers", description = "Delete the contents of this queue when its last consumer disconnects. Default: input.")
+   @Option(names = "--purge-on-no-consumers", description = "Delete the contents of this queue when its last consumer disconnects. Default: input.")
    private Boolean purgeOnNoConsumers;
 
-   @Option(name = "--preserve-on-no-consumers", description = "Preserve the contents of this queue when its last consumer disconnects. Default: input.")
+   @Option(names = "--preserve-on-no-consumers", description = "Preserve the contents of this queue when its last consumer disconnects. Default: input.")
    private Boolean preserveOnNoConsumers;
 
-   @Option(name = "--max-consumers", description = "The maximum number of concurrent consumers allowed on this queue. Default: no limit.")
+   @Option(names = "--max-consumers", description = "The maximum number of concurrent consumers allowed on this queue. Default: no limit.")
    private Integer maxConsumers;
 
-   @Option(name = "--auto-create-address", description = "Automatically create the address (if it doesn't exist) with default values. Default: input.")
+   @Option(names = "--auto-create-address", description = "Automatically create the address (if it doesn't exist) with default values. Default: input.")
    private Boolean autoCreateAddress;
 
-   @Option(name = "--anycast", description = "Create an anycast queue. Default: input.")
+   @Option(names = "--anycast", description = "Create an anycast queue. Default: input.")
    private Boolean anycast;
 
-   @Option(name = "--multicast", description = "Create a multicast queue. Default: input.")
+   @Option(names = "--multicast", description = "Create a multicast queue. Default: input.")
    private Boolean multicast;
 
    public void setFilter(String filter) {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueGroup.java
similarity index 57%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueGroup.java
index e34e376deb..92a55e0f49 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/QueueGroup.java
@@ -14,28 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@Command(name = "queue", description = "use 'help check' for sub commands list", subcommands = {CreateQueue.class, DeleteQueue.class, UpdateQueue.class, StatQueue.class, PurgeQueue.class})
+public class QueueGroup implements Runnable {
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
+   CommandLine commandLine;
 
-      killFile.createNewFile();
+   public QueueGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-      return null;
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "queue");
    }
+
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/StatQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/StatQueue.java
index 17b2ed70ec..9a22631aa1 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/StatQueue.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/StatQueue.java
@@ -21,14 +21,14 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.JsonUtil;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
 import org.apache.activemq.artemis.json.JsonArray;
 import org.apache.activemq.artemis.json.JsonObject;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "stat", description = "Print basic stats of a queue. Output includes CONSUMER_COUNT (number of consumers), MESSAGE_COUNT (current message count on the queue, including scheduled, paged and in-delivery messages), MESSAGES_ADDED (messages added to the queue), DELIVERING_COUNT (messages broker is currently delivering to consumer(s)), MESSAGES_ACKED (messages acknowledged from the consumer(s))." + " Queues can be filtered using EITHER '--queueName X' where X is contained in t [...]
 public class StatQueue extends ConnectionAbstract {
@@ -67,22 +67,22 @@ public class StatQueue extends ConnectionAbstract {
 
    public static final int DEFAULT_MAX_COLUMN_SIZE = 25;
 
-   @Option(name = "--queueName", description = "Display queue stats for queue(s) with names containing this string.")
+   @Option(names = "--queueName", description = "Display queue stats for queue(s) with names containing this string.")
    private String queueName;
 
-   @Option(name = "--field", description = "The field to filter. Possible values: NAME, ADDRESS, MESSAGE_COUNT, MESSAGES_ADDED, DELIVERING_COUNT, MESSAGES_ACKED, SCHEDULED_COUNT, ROUTING_TYPE.")
+   @Option(names = "--field", description = "The field to filter. Possible values: NAME, ADDRESS, MESSAGE_COUNT, MESSAGES_ADDED, DELIVERING_COUNT, MESSAGES_ACKED, SCHEDULED_COUNT, ROUTING_TYPE.")
    private String fieldName;
 
-   @Option(name = "--operation", description = "The operation to filter. Possible values: CONTAINS, NOT_CONTAINS, EQUALS, GREATER_THAN, LESS_THAN.")
+   @Option(names = "--operation", description = "The operation to filter. Possible values: CONTAINS, NOT_CONTAINS, EQUALS, GREATER_THAN, LESS_THAN.")
    private String operationName;
 
-   @Option(name = "--value", description = "The value to filter.")
+   @Option(names = "--value", description = "The value to filter.")
    private String value;
 
-   @Option(name = "--maxRows", description = "The max number of queues displayed. Default is 50.")
+   @Option(names = "--maxRows", description = "The max number of queues displayed. Default is 50.")
    private int maxRows = DEFAULT_MAX_ROWS;
 
-   @Option(name = "--maxColumnSize", description = "The max width of data column. Set to -1 for no limit. Default is 25.")
+   @Option(names = "--maxColumnSize", description = "The max width of data column. Set to -1 for no limit. Default is 25.")
    private int maxColumnSize = DEFAULT_MAX_COLUMN_SIZE;
 
    private int statCount = 0;
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/UpdateQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/UpdateQueue.java
index 416fbc61cc..7adaa783c4 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/UpdateQueue.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/UpdateQueue.java
@@ -16,9 +16,9 @@
  */
 package org.apache.activemq.artemis.cli.commands.queue;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
 
 @Command(name = "update", description = "Update a queue.")
 public class UpdateQueue extends QueueAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DBOption.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DBOption.java
index e757a9592e..9cdea178c1 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DBOption.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DBOption.java
@@ -17,6 +17,7 @@
 
 package org.apache.activemq.artemis.cli.commands.tools;
 
+import java.io.File;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
 import java.util.concurrent.ExecutorService;
@@ -25,7 +26,6 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.ThreadFactory;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.core.config.Configuration;
@@ -46,6 +46,7 @@ import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
 import org.apache.activemq.artemis.utils.ExecutorFactory;
 import org.apache.activemq.artemis.utils.actors.OrderedExecutorFactory;
 import org.apache.activemq.artemis.utils.critical.EmptyCriticalAnalyzer;
+import picocli.CommandLine.Option;
 
 public class DBOption extends OptionalLocking {
 
@@ -59,31 +60,35 @@ public class DBOption extends OptionalLocking {
 
    protected ScheduledExecutorService scheduledExecutorService;
 
-   @Option(name = "--output", description = "Output name for the file.")
-   private String output;
+   @Option(names = "--output", description = "Output name for the file.")
+   private File output;
 
-   @Option(name = "--jdbc", description = "Whether to store message data in JDBC instead of local files.")
+   private FileOutputStream fileOutputStream;
+
+   private PrintStream originalOut;
+
+   @Option(names = "--jdbc", description = "Whether to store message data in JDBC instead of local files.")
    Boolean jdbc;
 
-   @Option(name = "--jdbc-bindings-table-name", description = "Name of the jdbc bindings table.")
+   @Option(names = "--jdbc-bindings-table-name", description = "Name of the jdbc bindings table.")
    private String jdbcBindings = ActiveMQDefaultConfiguration.getDefaultBindingsTableName();
 
-   @Option(name = "--jdbc-message-table-name", description = "Name of the jdbc messages table.")
+   @Option(names = "--jdbc-message-table-name", description = "Name of the jdbc messages table.")
    private String jdbcMessages = ActiveMQDefaultConfiguration.getDefaultMessageTableName();
 
-   @Option(name = "--jdbc-large-message-table-name", description = "Name of the large messages table.")
+   @Option(names = "--jdbc-large-message-table-name", description = "Name of the large messages table.")
    private String jdbcLargeMessages = ActiveMQDefaultConfiguration.getDefaultLargeMessagesTableName();
 
-   @Option(name = "--jdbc-page-store-table-name", description = "Name of the page store messages table.")
+   @Option(names = "--jdbc-page-store-table-name", description = "Name of the page store messages table.")
    private String jdbcPageStore = ActiveMQDefaultConfiguration.getDefaultPageStoreTableName();
 
-   @Option(name = "--jdbc-node-manager-table-name", description = "Name of the jdbc node manager table.")
+   @Option(names = "--jdbc-node-manager-table-name", description = "Name of the jdbc node manager table.")
    private String jdbcNodeManager = ActiveMQDefaultConfiguration.getDefaultNodeManagerStoreTableName();
 
-   @Option(name = "--jdbc-connection-url", description = "The URL used for the database connection.")
+   @Option(names = "--jdbc-connection-url", description = "The URL used for the database connection.")
    private String jdbcURL = null;
 
-   @Option(name = "--jdbc-driver-class-name", description = "JDBC driver classname.")
+   @Option(names = "--jdbc-driver-class-name", description = "JDBC driver classname.")
    private String jdbcClassName = ActiveMQDefaultConfiguration.getDefaultDriverClassName();
 
    public boolean isJDBC() throws Exception {
@@ -167,15 +172,29 @@ public class DBOption extends OptionalLocking {
       super.execute(context);
 
       if (output != null) {
-         FileOutputStream fileOutputStream = new FileOutputStream(output);
+         fileOutputStream = new FileOutputStream(output);
+         originalOut = context.out;
          PrintStream printStream = new PrintStream(fileOutputStream);
          context.out = printStream;
-
-         Runtime.getRuntime().addShutdownHook(new Thread(printStream::close));
       }
       return null;
    }
 
+   @Override
+   public void done() {
+      super.done();
+      if (fileOutputStream != null) {
+         try {
+            fileOutputStream.close();
+         } catch (Throwable e) {
+            e.printStackTrace();
+         }
+         getActionContext().out = originalOut;
+         fileOutputStream = null;
+         originalOut = null;
+      }
+   }
+
    private void parseDBConfig() throws Exception {
       if (jdbc == null) {
          FileConfiguration fileConfiguration = getFileConfiguration();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java
index d3b2cb8a91..98c251c491 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java
@@ -19,24 +19,24 @@ package org.apache.activemq.artemis.cli.commands.tools;
 
 import java.io.File;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.Configurable;
+import picocli.CommandLine.Option;
 
 /**
  * Abstract class for places where you need bindings, journal paging and large messages configuration
  */
 public abstract class DataAbstract extends Configurable {
 
-   @Option(name = "--bindings", description = "The folder used for bindings. Default: read from broker.xml.")
+   @Option(names = "--bindings", description = "The folder used for bindings. Default: read from broker.xml.")
    public String binding;
 
-   @Option(name = "--journal", description = "The folder used for normal messages. Default: read from broker.xml.")
+   @Option(names = "--journal", description = "The folder used for normal messages. Default: read from broker.xml.")
    public String journal;
 
-   @Option(name = "--paging", description = "The folder used for paged messages. Default: read from broker.xml.")
+   @Option(names = "--paging", description = "The folder used for paged messages. Default: read from broker.xml.")
    public String paging;
 
-   @Option(name = "--large-messages", description = "The folder used for large-messages. Default: read from broker.xml.")
+   @Option(names = "--large-messages", description = "The folder used for large-messages. Default: read from broker.xml.")
    public String largeMessges;
 
    public String getLargeMessages() throws Exception {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataGroup.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataGroup.java
new file mode 100644
index 0000000000..f1f077c5a8
--- /dev/null
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataGroup.java
@@ -0,0 +1,43 @@
+/*
+ * 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.activemq.artemis.cli.commands.tools;
+
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import org.apache.activemq.artemis.cli.commands.tools.journal.CompactJournal;
+import org.apache.activemq.artemis.cli.commands.tools.journal.DecodeJournal;
+import org.apache.activemq.artemis.cli.commands.tools.journal.EncodeJournal;
+import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataExporter;
+import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataImporter;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "data", description = "use 'help data' for sub commands list", subcommands = {RecoverMessages.class, PrintData.class, XmlDataExporter.class, XmlDataImporter.class, DecodeJournal.class, EncodeJournal.class, CompactJournal.class})
+public class DataGroup implements Runnable {
+
+   CommandLine commandLine;
+
+   public DataGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
+
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "data");
+   }
+
+}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/HelpData.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/HelpData.java
deleted file mode 100644
index 0598737b2f..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/HelpData.java
+++ /dev/null
@@ -1,66 +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.activemq.artemis.cli.commands.tools;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.github.rvesse.airline.help.Help;
-import org.apache.activemq.artemis.cli.commands.Action;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.apache.activemq.artemis.cli.commands.OptionsUtil;
-
-public class HelpData extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etc) {
-
-   }
-
-   @Override
-   public String getBrokerInstance() {
-      return null;
-   }
-
-   @Override
-   public String getBrokerHome() {
-      return null;
-   }
-
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-
-      List<String> commands = new ArrayList<>(1);
-      commands.add("data");
-      help(global, commands);
-      return null;
-   }
-
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/LockAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/LockAbstract.java
index bef4e90fb4..88e9e8375b 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/LockAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/LockAbstract.java
@@ -60,6 +60,12 @@ public abstract class LockAbstract extends DataAbstract {
       return null;
    }
 
+   @Override
+   public void done() {
+      super.done();
+      unlock();
+   }
+
    void lockCLI(File lockPlace) throws Exception {
       if (lockPlace != null) {
          lockPlace.mkdirs();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/OptionalLocking.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/OptionalLocking.java
index 9285ea6386..afa3cb1671 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/OptionalLocking.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/OptionalLocking.java
@@ -18,14 +18,14 @@ package org.apache.activemq.artemis.cli.commands.tools;
 
 import java.io.File;
 
-import com.github.rvesse.airline.annotations.Option;
+import picocli.CommandLine.Option;
 
 /**
  * This is for commands where --f on ignoring lock could be valid.
  */
 public class OptionalLocking extends LockAbstract {
 
-   @Option(name = "--f", description = "This will allow certain tools like print-data to be performed ignoring any running servers. WARNING: Changing data concurrently with a running broker may damage your data. Be careful with this option.")
+   @Option(names = "--f", description = "This will allow certain tools like print-data to be performed ignoring any running servers. WARNING: Changing data concurrently with a running broker may damage your data. Be careful with this option.")
    boolean ignoreLock;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java
index 7e09b01296..93361abcde 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java
@@ -27,8 +27,6 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
 import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
 import org.apache.activemq.artemis.api.core.SimpleString;
@@ -63,25 +61,25 @@ import org.apache.activemq.artemis.utils.ExecutorFactory;
 import org.apache.activemq.artemis.utils.actors.ArtemisExecutor;
 import org.apache.activemq.artemis.utils.collections.LinkedList;
 import org.apache.activemq.artemis.utils.collections.LinkedListIterator;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "print", description = "Print data records information. WARNING: don't use while a production server is running.")
 public class PrintData extends DBOption {
 
-
-   @Option(name = "--safe", description = "Print your data structure without showing your data.")
+   @Option(names = "--safe", description = "Print your data structure without showing your data.")
    private boolean safe = false;
 
-
-   @Option(name = "--reclaimed", description = "Try to print as many records as possible from reclaimed files.")
+   @Option(names = "--reclaimed", description = "Try to print as many records as possible from reclaimed files.")
    private boolean reclaimed = false;
 
-   @Option(name = "--max-pages", description = "Maximum number of pages to read. Default: unlimited (-1).")
+   @Option(names = "--max-pages", description = "Maximum number of pages to read. Default: unlimited (-1).")
    private int maxPages = -1;
 
-   @Option(name = "--skip-bindings", description = "Do not print data from the bindings journal.")
+   @Option(names = "--skip-bindings", description = "Do not print data from the bindings journal.")
    private boolean skipBindings = false;
 
-   @Option(name = "--skip-journal", description = "Do not print data from the messages journal.")
+   @Option(names = "--skip-journal", description = "Do not print data from the messages journal.")
    private boolean skipJournal = false;
 
    private static final String BINDINGS_BANNER = "B I N D I N G S  J O U R N A L";
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/RecoverMessages.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/RecoverMessages.java
index 085c892e94..8431e681af 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/RecoverMessages.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/RecoverMessages.java
@@ -20,9 +20,6 @@ import java.io.File;
 import java.util.HashSet;
 import java.util.List;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.api.core.Pair;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.core.config.Configuration;
@@ -38,6 +35,8 @@ import org.apache.activemq.artemis.core.message.impl.CoreMessagePersister;
 import org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds;
 import org.apache.activemq.artemis.spi.core.protocol.MessagePersister;
 import org.apache.activemq.artemis.utils.ByteUtil;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "recover", description = "Recover (undelete) every message on the journal by creating a new output journal. Rolled back and acked messages will be sent out to the output as much as possible.")
 public class RecoverMessages extends DBOption {
@@ -46,11 +45,10 @@ public class RecoverMessages extends DBOption {
       MessagePersister.registerPersister(CoreMessagePersister.getInstance());
    }
 
-   @Option(name = "--reclaimed", description = "Try to recover as many records as possible from reclaimed files.")
+   @Option(names = "--reclaimed", description = "Try to recover as many records as possible from reclaimed files.")
    private boolean reclaimed = false;
 
-   @Option(name = "--target", description = "Output folder container the new journal with all the generated messages.")
-   @Required
+   @Option(names = "--target", description = "Output folder container the new journal with all the generated messages.", required = true)
    private String outputJournal;
 
 
@@ -67,7 +65,7 @@ public class RecoverMessages extends DBOption {
          if (configuration.isJDBC()) {
             throw new IllegalAccessException("JDBC Not supported on recover");
          } else {
-            recover(configuration, getJournal(), journalOutput, new File(getLargeMessages()), reclaimed);
+            recover(context, configuration, getJournal(), journalOutput, new File(getLargeMessages()), reclaimed);
          }
       } catch (Exception e) {
          treatError(e, "data", "recover");
@@ -75,9 +73,7 @@ public class RecoverMessages extends DBOption {
       return null;
    }
 
-   public static void recover(Configuration configuration, String journallocation, File journalOutput, File largeMessage, boolean reclaimed) throws Exception {
-
-      final ActionContext context =  ActionContext.system();
+   public static void recover(ActionContext context, Configuration configuration, String journallocation, File journalOutput, File largeMessage, boolean reclaimed) throws Exception {
 
       File journal = new File(journallocation);
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/CompactJournal.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/CompactJournal.java
index 41e5cd59c8..198c94d494 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/CompactJournal.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/CompactJournal.java
@@ -18,7 +18,6 @@ package org.apache.activemq.artemis.cli.commands.tools.journal;
 
 import java.io.File;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
 import org.apache.activemq.artemis.core.config.Configuration;
@@ -26,6 +25,7 @@ import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
 import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
 import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
 import org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds;
+import picocli.CommandLine.Command;
 
 @Command(name = "compact", description = "Compact the journal of a non running server.")
 public final class CompactJournal extends LockAbstract {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/DecodeJournal.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/DecodeJournal.java
index f0cb73b64e..ffc7496e34 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/DecodeJournal.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/DecodeJournal.java
@@ -27,33 +27,31 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
 import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
 import org.apache.activemq.artemis.core.journal.RecordInfo;
 import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
 import org.apache.activemq.artemis.utils.Base64;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "decode", description = "Decode a journal's internal format into a new set of journal files.")
 public class DecodeJournal extends LockAbstract {
 
-   @Option(name = "--directory", description = "The journal folder. Default: read 'journal-directory' from broker.xml.")
+   @Option(names = "--directory", description = "The journal folder. Default: read 'journal-directory' from broker.xml.")
    public String directory;
 
-   @Option(name = "--prefix", description = "The journal prefix. Default: activemq-data.")
+   @Option(names = "--prefix", description = "The journal prefix. Default: activemq-data.")
    public String prefix = "activemq-data";
 
-   @Option(name = "--suffix", description = "The journal suffix. Default: amq.")
+   @Option(names = "--suffix", description = "The journal suffix. Default: amq.")
    public String suffix = "amq";
 
-   @Option(name = "--file-size", description = "The journal size. Default: 10485760.")
+   @Option(names = "--file-size", description = "The journal size. Default: 10485760.")
    public int size = 10485760;
 
-   @Option(name = "--input", description = "The input file name. Default: exp.dmp.")
-   @Required
+   @Option(names = "--input", description = "The input file name. Default: exp.dmp.", required = true)
    public String input = "exp.dmp";
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/EncodeJournal.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/EncodeJournal.java
index 1a3db097bc..81c0ee6b44 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/EncodeJournal.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/EncodeJournal.java
@@ -22,8 +22,6 @@ import java.io.FileOutputStream;
 import java.io.PrintStream;
 import java.util.List;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
 import org.apache.activemq.artemis.core.io.SequentialFileFactory;
@@ -33,20 +31,22 @@ import org.apache.activemq.artemis.core.journal.impl.JournalFile;
 import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
 import org.apache.activemq.artemis.core.journal.impl.JournalReaderCallback;
 import org.apache.activemq.artemis.utils.Base64;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "encode", description = "Encode a set of journal files into an internal encoded data format.")
 public class EncodeJournal extends LockAbstract {
 
-   @Option(name = "--directory", description = "The journal folder. Default: read 'journal-directory' from broker.xml.")
+   @Option(names = "--directory", description = "The journal folder. Default: read 'journal-directory' from broker.xml.")
    public String directory;
 
-   @Option(name = "--prefix", description = "The journal prefix. Default: activemq-data.")
+   @Option(names = "--prefix", description = "The journal prefix. Default: activemq-data.")
    public String prefix = "activemq-data";
 
-   @Option(name = "--suffix", description = "The journal suffix. Default: amq.")
+   @Option(names = "--suffix", description = "The journal suffix. Default: amq.")
    public String suffix = "amq";
 
-   @Option(name = "--file-size", description = "The journal size. Default: 10485760.")
+   @Option(names = "--file-size", description = "The journal size. Default: 10485760.")
    public int size = 10485760;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/PerfJournal.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/PerfJournal.java
index 471af9f0f7..ba7b9b46d6 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/PerfJournal.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/journal/PerfJournal.java
@@ -18,43 +18,43 @@ package org.apache.activemq.artemis.cli.commands.tools.journal;
 
 import java.text.DecimalFormat;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.OptionalLocking;
 import org.apache.activemq.artemis.cli.commands.util.SyncCalculation;
 import org.apache.activemq.artemis.core.config.impl.FileConfiguration;
 import org.apache.activemq.artemis.core.server.JournalType;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 @Command(name = "perf-journal", description = "Calculate the journal-buffer-timeout to use with the current data folder.")
 public class PerfJournal extends OptionalLocking {
 
 
-   @Option(name = "--block-size", description = "The block size for each write. Default 4096.")
+   @Option(names = "--block-size", description = "The block size for each write. Default 4096.")
    public int size = 4 * 1024;
 
-   @Option(name = "--writes", description = "The number of writes to be performed. Default: 250.")
+   @Option(names = "--writes", description = "The number of writes to be performed. Default: 250.")
    public int writes = 250;
 
-   @Option(name = "--tries", description = "The number of tries for the test. Default: 5.")
+   @Option(names = "--tries", description = "The number of tries for the test. Default: 5.")
    public int tries = 5;
 
-   @Option(name = "--no-sync", description = "Disable syncs.")
+   @Option(names = "--no-sync", description = "Disable syncs.")
    public boolean nosyncs = false;
 
-   @Option(name = "--sync", description = "Enable syncs.")
+   @Option(names = "--sync", description = "Enable syncs.")
    public boolean syncs = false;
 
-   @Option(name = "--journal-type", description = "Journal type to be used: Default: read from broker.xml.")
+   @Option(names = "--journal-type", description = "Journal type to be used: Default: read from broker.xml.")
    public String journalType = null;
 
-   @Option(name = "--sync-writes", description = "Perform each write synchronously, e.g. if there was a single producer.")
+   @Option(names = "--sync-writes", description = "Perform each write synchronously, e.g. if there was a single producer.")
    public boolean syncWrites = false;
 
-   @Option(name = "--file", description = "The file name to be used. Default: test.tmp.")
+   @Option(names = "--file", description = "The file name to be used. Default: test.tmp.")
    public String fileName = "test.tmp";
 
-   @Option(name = "--max-aio", description = "libaio.maxAIO to be used. Default: read from broker.xml.")
+   @Option(names = "--max-aio", description = "libaio.maxAIO to be used. Default: read from broker.xml.")
    public int maxAIO = 0;
 
 
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataExporter.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataExporter.java
index 23e6d6c7d0..f230fdfed8 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataExporter.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataExporter.java
@@ -21,6 +21,7 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import java.io.File;
 import java.io.OutputStream;
+import java.lang.invoke.MethodHandles;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
@@ -32,8 +33,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.stream.Collectors;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
 import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
 import org.apache.activemq.artemis.api.core.ICoreMessage;
@@ -66,8 +67,7 @@ import org.apache.activemq.artemis.core.server.JournalType;
 import org.apache.activemq.artemis.utils.collections.LinkedListIterator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import java.lang.invoke.MethodHandles;
-import java.util.stream.Collectors;
+import picocli.CommandLine.Command;
 
 @Command(name = "exp", description = "Export all message-data using an XML that could be interpreted by any system.")
 public final class XmlDataExporter extends DBOption {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java
index 6f515a3cfe..7922a36a89 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java
@@ -23,6 +23,7 @@ import javax.xml.validation.Validator;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
 import java.net.URL;
 import java.nio.ByteBuffer;
 import java.security.AccessController;
@@ -34,9 +35,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
-import com.github.rvesse.airline.annotations.restrictions.Required;
 import org.apache.activemq.artemis.api.core.Message;
 import org.apache.activemq.artemis.api.core.QueueConfiguration;
 import org.apache.activemq.artemis.api.core.RoutingType;
@@ -63,7 +61,8 @@ import org.apache.activemq.artemis.utils.ListUtil;
 import org.apache.activemq.artemis.utils.XmlProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import java.lang.invoke.MethodHandles;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 /**
  * Read XML output from <code>org.apache.activemq.artemis.core.persistence.impl.journal.XmlDataExporter</code>, create a core session, and
@@ -92,29 +91,28 @@ public final class XmlDataImporter extends ActionAbstract {
 
    private ClientSession session;
 
-   @Option(name = "--host", description = "The host used to import the data. Default: localhost.")
+   @Option(names = "--host", description = "The host used to import the data. Default: localhost.")
    public String host = "localhost";
 
-   @Option(name = "--port", description = "The port used to import the data. Default: 61616.")
+   @Option(names = "--port", description = "The port used to import the data. Default: 61616.")
    public int port = 61616;
 
-   @Option(name = "--transaction", description = "Import every message using a single transction. If anything goes wrong during the process the entire import will be aborted. Default: false.")
+   @Option(names = "--transaction", description = "Import every message using a single transction. If anything goes wrong during the process the entire import will be aborted. Default: false.")
    public boolean transactional;
 
-   @Option(name = "--user", description = "User name used to import the data. Default: null.")
+   @Option(names = "--user", description = "User name used to import the data. Default: null.")
    public String user = null;
 
-   @Option(name = "--password", description = "User name used to import the data. Default: null.")
+   @Option(names = "--password", description = "User name used to import the data. Default: null.")
    public String password = null;
 
-   @Option(name = "--input", description = "The input file name. Default: exp.dmp.")
-   @Required
+   @Option(names = "--input", description = "The input file name. Default: exp.dmp.", required = true)
    public String input = "exp.dmp";
 
-   @Option(name = "--sort", description = "Sort the messages from the input (used for older versions that won't sort messages).")
+   @Option(names = "--sort", description = "Sort the messages from the input (used for older versions that won't sort messages).")
    public boolean sort = false;
 
-   @Option(name = "--legacy-prefixes", description = "Do not remove prefixes from legacy imports.")
+   @Option(names = "--legacy-prefixes", description = "Do not remove prefixes from legacy imports.")
    public boolean legacyPrefixes = false;
 
    TreeSet<XMLMessageImporter.MessageInfo> messages;
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
index 73f7cf6340..3f91ef7de8 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
@@ -16,10 +16,10 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 /**
  * Adding a new user, example:
@@ -28,7 +28,7 @@ import org.apache.activemq.artemis.cli.commands.ActionContext;
 @Command(name = "add", description = "Add a user.")
 public class AddUser extends PasswordAction {
 
-   @Option(name = "--plaintext", description = "Store the password in plaintext. Default: false.")
+   @Option(names = "--plaintext", description = "Store the password in plaintext. Default: false.")
    boolean plaintext = false;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/HelpUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/HelpUser.java
deleted file mode 100644
index 925c645412..0000000000
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/HelpUser.java
+++ /dev/null
@@ -1,62 +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.activemq.artemis.cli.commands.user;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.github.rvesse.airline.help.Help;
-import org.apache.activemq.artemis.cli.commands.Action;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.apache.activemq.artemis.cli.commands.OptionsUtil;
-
-public class HelpUser extends Help implements Action {
-
-   @Override
-   public boolean isVerbose() {
-      return false;
-   }
-
-   @Override
-   public void setHomeValues(File brokerHome, File brokerInstance, File etcFolder) {
-   }
-
-   @Override
-   public String getBrokerInstance() {
-      return null;
-   }
-
-   @Override
-   public String getBrokerHome() {
-      return null;
-   }
-
-   @Override
-   public void checkOptions(String[] options) throws InvalidOptionsError {
-      OptionsUtil.checkCommandOptions(this.getClass(), options);
-   }
-
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      List<String> commands = new ArrayList<>(1);
-      commands.add("user");
-      help(global, commands);
-      return null;
-   }
-}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
index a01cbf00a0..ad7455627d 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
@@ -16,13 +16,12 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import org.apache.activemq.artemis.json.JsonArray;
-import org.apache.activemq.artemis.json.JsonObject;
-
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.JsonUtil;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.json.JsonArray;
+import org.apache.activemq.artemis.json.JsonObject;
+import picocli.CommandLine.Command;
 
 /**
  * list existing users, example:
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/PasswordAction.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/PasswordAction.java
index f49caf8fe9..7e0a74448f 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/PasswordAction.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/PasswordAction.java
@@ -16,11 +16,11 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Option;
+import picocli.CommandLine.Option;
 
 public class PasswordAction extends UserAction {
 
-   @Option(name = "--user-command-password", description = "The password to use for the chosen user command. Default: input.")
+   @Option(names = "--user-command-password", description = "The password to use for the chosen user command. Default: input.")
    String userCommandPassword;
 
    void checkInputPassword() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
index 9073dab1f8..de17056cbc 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
@@ -16,9 +16,9 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Command;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
 
 /**
  * Remove a user, example:
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
index e221b8ea03..795d1b739e 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
@@ -16,10 +16,10 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Command;
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
 import org.apache.activemq.artemis.cli.commands.ActionContext;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
 
 /**
  * Reset a user's password or roles, example:
@@ -28,7 +28,7 @@ import org.apache.activemq.artemis.cli.commands.ActionContext;
 @Command(name = "reset", description = "Reset user's password or roles.")
 public class ResetUser extends PasswordAction {
 
-   @Option(name = "--plaintext", description = "Store the password in plaintext. Default: false.")
+   @Option(names = "--plaintext", description = "Store the password in plaintext. Default: false.")
    boolean plaintext = false;
 
    @Override
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
index 50fcecd5e4..3f0cd0efa3 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
@@ -16,15 +16,15 @@
  */
 package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Option;
 import org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
+import picocli.CommandLine.Option;
 
 public abstract class UserAction extends ConnectionAbstract {
 
-   @Option(name = "--role", description = "The user's role(s). Separate multiple roles with comma.")
+   @Option(names = "--role", description = "The user's role(s). Separate multiple roles with comma.")
    String role;
 
-   @Option(name = "--user-command-user", description = "The username to use for the chosen user command. Default: input.")
+   @Option(names = "--user-command-user", description = "The username to use for the chosen user command. Default: input.")
    String userCommandUser = null;
 
    void checkInputUser() {
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserGroup.java
similarity index 57%
copy from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
copy to artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserGroup.java
index e34e376deb..ef4aecdc28 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Kill.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserGroup.java
@@ -14,28 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.cli.commands;
 
-import java.io.File;
+package org.apache.activemq.artemis.cli.commands.user;
 
-import com.github.rvesse.airline.annotations.Command;
-import org.apache.activemq.artemis.dto.BrokerDTO;
+import org.apache.activemq.artemis.cli.commands.HelpAction;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
 
-@Command(name = "kill", description = "Kill a broker started with --allow-kill.")
-public class Kill extends Configurable {
+@Command(name = "user", description = "file-based user management. Use 'help user' for sub commands list.", subcommands = {ListUser.class, AddUser.class, RemoveUser.class, ResetUser.class})
+public class UserGroup implements Runnable {
 
-   @Override
-   public Object execute(ActionContext context) throws Exception {
-      super.execute(context);
-
-      BrokerDTO broker = getBrokerDTO();
-
-      File file = broker.server.getConfigurationFile().getParentFile();
-
-      File killFile = new File(file, "KILL_ME");
+   CommandLine commandLine;
 
-      killFile.createNewFile();
+   public UserGroup(CommandLine commandLine) {
+      this.commandLine = commandLine;
+   }
 
-      return null;
+   @Override
+   public void run() {
+      HelpAction.help(commandLine, "user");
    }
+
 }
diff --git a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstractTest.java b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstractTest.java
index 331afbce9f..fbe060ad12 100644
--- a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstractTest.java
+++ b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ConnectionAbstractTest.java
@@ -48,11 +48,12 @@ public class ConnectionAbstractTest {
 
    @Test
    public void testDefaultSourceAcceptorNoArtemis() {
-      ConnectionAbstract connectionAbstract = new ConnectionAbstract();
 
       File brokerInstanceEtc = new File(this.getClass().getClassLoader().getResource("broker.xml").getFile()).getParentFile();
 
       System.setProperty("artemis.instance.etc", brokerInstanceEtc.getAbsolutePath());
+
+      ConnectionAbstract connectionAbstract = new ConnectionAbstract();
       try {
          connectionAbstract.setHomeValues(null, brokerInstanceEtc.getParentFile(), null);
          ActionAbstractAccessor.setBrokerConfig(connectionAbstract, "broker-with-connector.xml");
diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
index 6d1f994b12..e06e0a68b5 100644
--- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
+++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
@@ -144,12 +144,12 @@ public class ArtemisTest extends CliTestBase {
       org.apache.activemq.artemis.api.config.ActiveMQDefaultConfigurationTestAccessor.setDefaultAddressQueueScanPeriod(timeBefore);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void invalidCliDoesntThrowException() {
       testCli("--silent", "create");
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void invalidPathDoesntThrowException() {
       if (isWindows()) {
          testCli("create", "zzzzz:/rawr", "--silent");
@@ -158,14 +158,14 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSupportsLibaio() throws Exception {
       Create x = new Create();
       x.setInstance(new File("/tmp/foo"));
       x.supportsLibaio();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSync() throws Exception {
       int writes = 2;
       int tries = 5;
@@ -177,7 +177,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSimpleCreate() throws Exception {
       //instance1: default using http
       File instance1 = new File(temporaryFolder.getRoot(), "instance1");
@@ -185,14 +185,14 @@ public class ArtemisTest extends CliTestBase {
    }
 
 
-   @Test
+   @Test(timeout = 60_000)
    public void testCreateDB() throws Exception {
       File instance1 = new File(temporaryFolder.getRoot(), "instance1");
       Artemis.internalExecute("create", instance1.getAbsolutePath(), "--silent", "--jdbc");
    }
 
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSimpleCreateMapped() throws Throwable {
       try {
          //instance1: default using http
@@ -204,7 +204,7 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testOpenwireSupportAdvisoryDisabledByDefault() throws Exception {
       FileConfiguration configuration = createFileConfiguration("supportAdvisory",
                                                                 "--force", "--silent", "--no-web", "--no-autotune");
@@ -214,7 +214,7 @@ public class ArtemisTest extends CliTestBase {
       Assert.assertFalse(Boolean.parseBoolean(params.get("suppressInternalManagementObjects").toString()));
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testOpenwireEnabledSupportAdvisory() throws Exception {
       FileConfiguration configuration = createFileConfiguration("supportAdvisory",
                                                                 "--force", "--silent", "--no-web", "--no-autotune",
@@ -249,7 +249,7 @@ public class ArtemisTest extends CliTestBase {
       return fc;
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testWebConfig() throws Exception {
       setupAuth();
       Run.setEmbedded(true);
@@ -326,7 +326,7 @@ public class ArtemisTest extends CliTestBase {
       assertEquals("password2", trustPass);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSecurityManagerConfiguration() throws Exception {
       setupAuth();
       Run.setEmbedded(true);
@@ -403,7 +403,7 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testStopManagementContext() throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
@@ -418,12 +418,12 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandJAAS() throws Exception {
       testUserCommand(false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandBasic() throws Exception {
       testUserCommand(true);
    }
@@ -482,7 +482,7 @@ public class ArtemisTest extends CliTestBase {
          addCmd.setRole("admin,operator");
          addCmd.setUser("admin");
          addCmd.setPassword("admin");
-         addCmd.execute(ActionContext.system());
+         addCmd.execute(new ActionContext());
 
          //verify
          context = new TestActionContext();
@@ -592,22 +592,22 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandViaManagementPlaintextJAAS() throws Exception {
       internalTestUserCommandViaManagement(true, false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandViaManagementHashedJAAS() throws Exception {
       internalTestUserCommandViaManagement(false, false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandViaManagementPlaintextBasic() throws Exception {
       internalTestUserCommandViaManagement(true, true);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandViaManagementHashedBasic() throws Exception {
       internalTestUserCommandViaManagement(false, true);
    }
@@ -713,7 +713,7 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testListUserWithMultipleRolesWithSpaces() throws Exception {
       try {
          Run.setEmbedded(true);
@@ -753,12 +753,12 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testProperReloadWhenAddingUserViaManagementJAAS() throws Exception {
       testProperReloadWhenAddingUserViaManagement(false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testProperReloadWhenAddingUserViaManagementBasic() throws Exception {
       testProperReloadWhenAddingUserViaManagement(true);
    }
@@ -802,7 +802,7 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testMissingUserFileViaManagement() throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
@@ -825,7 +825,7 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testMissingRoleFileViaManagement() throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
@@ -848,12 +848,12 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandResetJAAS() throws Exception {
       testUserCommandReset(false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandResetBasic() throws Exception {
       testUserCommandReset(true);
    }
@@ -979,12 +979,12 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testConcurrentUserAdministrationJAAS() throws Exception {
       testConcurrentUserAdministration(false);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testConcurrentUserAdministrationBasic() throws Exception {
       testConcurrentUserAdministration(true);
    }
@@ -1091,7 +1091,7 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testRoleWithSpaces() throws Exception {
       String roleWithSpaces = "amq with spaces";
       Run.setEmbedded(true);
@@ -1121,12 +1121,12 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandResetViaManagementPlaintext() throws Exception {
       internalTestUserCommandResetViaManagement(true);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testUserCommandResetViaManagementHashed() throws Exception {
       internalTestUserCommandResetViaManagement(false);
    }
@@ -1200,7 +1200,7 @@ public class ArtemisTest extends CliTestBase {
       stopServer();
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testMaskCommand() throws Exception {
 
       String password1 = "password";
@@ -1236,7 +1236,7 @@ public class ArtemisTest extends CliTestBase {
       assertEquals(encrypt2, result);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testMaskCommandWithPasswordCodec() throws Exception {
       File instanceWithPasswordCodec = new File(temporaryFolder.getRoot(), "instance_with_password_codec");
       Files.createDirectories(Paths.get(instanceWithPasswordCodec.getAbsolutePath(), "etc"));
@@ -1265,17 +1265,17 @@ public class ArtemisTest extends CliTestBase {
       assertEquals(password, result);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSimpleRun() throws Exception {
       testSimpleRun("server");
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testProducerRetry() throws Exception {
       File instanceFolder = temporaryFolder.newFolder("server");
       setupAuth(instanceFolder);
       Run.setEmbedded(true);
-      Artemis.main("create", instanceFolder.getAbsolutePath(), "--verbose", "--force", "--silent", "--no-web", "--queues", "q1", "--no-autotune", "--require-login", "--default-port", "61616");
+      Artemis.main("create", instanceFolder.getAbsolutePath(), "--verbose", "--force", "--disable-persistence", "--silent", "--no-web", "--queues", "q1", "--no-autotune", "--require-login", "--default-port", "61616");
       System.setProperty("artemis.instance", instanceFolder.getAbsolutePath());
 
       try {
@@ -1288,20 +1288,20 @@ public class ArtemisTest extends CliTestBase {
           * it will read from the InputStream in the ActionContext. It can't read the password since it's using
           * System.console.readPassword() for that.
           */
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute(null, null, null, new String[] {"producer", "--destination", "queue://q1", "--message-count", "100", "--password", "admin"}, context));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute(null, null, null, new String[] {"producer", "--destination", "queue://q1", "--message-count", "100", "--password", "admin"}, context));
 
          /*
           * This is the same as above except it will prompt the user to re-enter both the URL and the username.
           */
          in = new ByteArrayInputStream("tcp://localhost:61616\nadmin\n".getBytes());
          context = new ActionContext(in, System.out, System.err);
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute(null, null, null, new String[] {"producer", "--destination", "queue://q1", "--message-count", "100", "--password", "admin", "--url", "tcp://badhost:11111"}, context));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute(null, null, null, new String[] {"producer", "--destination", "queue://q1", "--message-count", "100", "--password", "admin", "--url", "tcp://badhost:11111"}, context));
       } finally {
          stopServer();
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testQueueStatRetry() throws Exception {
       File instanceFolder = temporaryFolder.newFolder("server");
       setupAuth(instanceFolder);
@@ -1332,24 +1332,24 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testWeirdCharacter() throws Exception {
       testSimpleRun("test%26%26x86_6");
    }
 
 
-   @Test
+   @Test(timeout = 60_000)
    public void testSpaces() throws Exception {
       testSimpleRun("with space");
    }
 
 
-   @Test
+   @Test(timeout = 60_000)
    public void testCustomPort() throws Exception {
       testSimpleRun("server", 61696);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testPerfJournal() throws Exception {
       File instanceFolder = temporaryFolder.newFolder("server1");
       setupAuth(instanceFolder);
@@ -1411,14 +1411,14 @@ public class ArtemisTest extends CliTestBase {
          }
          Artemis.internalExecute("data", "print", "--f");
 
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message-count", "100", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--text-size", "500", "--message-count", "10", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message-size", "500", "--message-count", "10", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message", "message", "--message-count", "10", "--user", "admin", "--password", "admin"));
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message-count", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--text-size", "500", "--message-count", "10", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message-size", "500", "--message-count", "10", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("producer", "--destination", "queue://q1", "--message", "message", "--message-count", "10", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
 
          ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:" + acceptorPort);
          Connection connection = cf.createConnection("admin", "admin");
@@ -1439,20 +1439,20 @@ public class ArtemisTest extends CliTestBase {
          connection.close();
          cf.close();
 
-         assertEquals(Integer.valueOf(1), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--filter", "fruit='banana'", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(1), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--filter", "fruit='banana'", "--user", "admin", "--password", "admin"));
 
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--filter", "fruit='orange'", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--filter", "fruit='orange'", "--user", "admin", "--password", "admin"));
 
-         assertEquals(Integer.valueOf(101), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(101), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--user", "admin", "--password", "admin"));
 
          // should only receive 10 messages on browse as I'm setting messageCount=10
-         assertEquals(Integer.valueOf(10), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--message-count", "10", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(10), Artemis.internalExecute("browser", "--destination", "queue://q1", "--txt-size", "50", "--message-count", "10", "--user", "admin", "--password", "admin"));
 
          // Nothing was consumed until here as it was only browsing, check it's receiving again
-         assertEquals(Integer.valueOf(1), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--txt-size", "50", "--break-on-null", "--receive-timeout", "100", "--filter", "fruit='banana'", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(1), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--txt-size", "50", "--break-on-null", "--receive-timeout", "100", "--filter", "fruit='banana'", "--user", "admin", "--password", "admin"));
 
          // Checking it was acked before
-         assertEquals(Integer.valueOf(100), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--txt-size", "50", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
+         assertEquals(Long.valueOf(100), Artemis.internalExecute("consumer", "--destination", "queue://q1", "--txt-size", "50", "--break-on-null", "--receive-timeout", "100", "--user", "admin", "--password", "admin"));
 
          //add a simple user
          AddUser addCmd = new AddUser();
@@ -1477,12 +1477,12 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testAutoDeleteTrue() throws Exception {
       testAutoDelete(true);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testAutoDeleteFalse() throws Exception {
       testAutoDelete(false);
    }
@@ -1535,7 +1535,7 @@ public class ArtemisTest extends CliTestBase {
 
 
 
-   @Test
+   @Test(timeout = 60_000)
    public void testPing() throws Exception {
       File instanceFolder = temporaryFolder.newFolder("pingTest");
 
@@ -1545,7 +1545,7 @@ public class ArtemisTest extends CliTestBase {
 
       // This is usually set when run from the command line via artemis.profile
       Run.setEmbedded(true);
-      Artemis.main("create", instanceFolder.getAbsolutePath(), "--force", "--silent", "--no-web", "--queues", queues, "--addresses", topics, "--no-autotune", "--require-login", "--ping", "127.0.0.1", "--no-autotune");
+      Artemis.main("create", instanceFolder.getAbsolutePath(), "--force", "--silent", "--no-web", "--queues", queues, "--addresses", topics, "--require-login", "--ping", "127.0.0.1", "--no-autotune");
       System.setProperty("artemis.instance", instanceFolder.getAbsolutePath());
 
       FileConfiguration fc = new FileConfiguration();
@@ -1557,7 +1557,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testAutoTune() throws Exception {
       File instanceFolder = temporaryFolder.newFolder("autoTuneTest");
 
@@ -1578,7 +1578,7 @@ public class ArtemisTest extends CliTestBase {
       Assert.assertNotEquals(-1, fc.getPageSyncTimeout());
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testQstat() throws Exception {
 
       File instanceQstat = new File(temporaryFolder.getRoot(), "instanceQStat");
@@ -1828,7 +1828,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testHugeQstat() throws Exception {
 
       File instanceQstat = new File(temporaryFolder.getRoot(), "instanceQStat");
@@ -1855,7 +1855,7 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testQstatColumnWidth() throws Exception {
 
       File instanceQstat = new File(temporaryFolder.getRoot(), "instanceQStat");
@@ -1930,7 +1930,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testQstatErrors() throws Exception {
 
       File instanceQstat = new File(temporaryFolder.getRoot(), "instanceQStatErrors");
@@ -2033,7 +2033,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testQstatWarnings() throws Exception {
 
       File instanceQstat = new File(temporaryFolder.getRoot(), "instanceQStat");
@@ -2115,7 +2115,7 @@ public class ArtemisTest extends CliTestBase {
 
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testRunPropertiesArgumentSetsAcceptorPort() throws Exception {
       File instanceFile = new File(temporaryFolder.getRoot(), "testRunPropertiesArgumentSetsAcceptorPort");
       setupAuth(instanceFile);
@@ -2136,7 +2136,7 @@ public class ArtemisTest extends CliTestBase {
       }
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testRunPropertiesDudArgument() throws Exception {
       File instanceFile = new File(temporaryFolder.getRoot(), "testRunPropertiesDudArgument");
       setupAuth(instanceFile);
@@ -2149,7 +2149,7 @@ public class ArtemisTest extends CliTestBase {
       assertTrue(ret instanceof FileNotFoundException);
    }
 
-   @Test
+   @Test(timeout = 60_000)
    public void testVersionCommand() throws Exception {
       TestActionContext context = new TestActionContext();
       PrintVersion printVersion = new PrintVersion();
diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/CheckTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/CheckTest.java
index e7ba8e0023..7545d23f0a 100644
--- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/CheckTest.java
+++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/CheckTest.java
@@ -26,7 +26,6 @@ import org.apache.activemq.artemis.api.core.management.QueueControl;
 import org.apache.activemq.artemis.api.core.management.ResourceNames;
 import org.apache.activemq.artemis.cli.Artemis;
 import org.apache.activemq.artemis.cli.CLIException;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.Run;
 import org.apache.activemq.artemis.cli.commands.check.NodeCheck;
 import org.apache.activemq.artemis.cli.commands.check.QueueCheck;
@@ -148,9 +147,9 @@ public class CheckTest extends CliTestBase {
       setupAuth(masterInstance);
 
       Artemis.main("create", masterInstance.getAbsolutePath(), "--cluster-password", "artemis", "--cluster-user", "artemis", "--clustered",
-                   "--replicated", "--host", "127.0.0.1", "--default-port", "61616", "--silent", "--no-autotune", "--no-web", "--require-login");
+                   "--replicated", "--host", "127.0.0.1", "--default-port", "61616", "--silent", "--no-autotune", "--no-web", "--require-login", "--name=live");
       Artemis.main("create", slaveInstance.getAbsolutePath(), "--cluster-password", "artemis", "--cluster-user", "artemis", "--clustered",
-                   "--replicated", "--host", "127.0.0.1", "--default-port", "61626", "--silent", "--no-autotune", "--no-web", "--require-login", "--slave");
+                   "--replicated", "--host", "127.0.0.1", "--default-port", "61626", "--silent", "--no-autotune", "--no-web", "--require-login", "--slave", "--name=backup");
 
       System.setProperty("artemis.instance", masterInstance.getAbsolutePath());
       Object master = Artemis.execute(false, false, null, masterInstance, null, "run");
@@ -196,10 +195,12 @@ public class CheckTest extends CliTestBase {
             nodeCheck.setPeers(2);
             Assert.assertEquals(3, nodeCheck.execute(context));
          } finally {
-            Artemis.internalExecute(null, slaveInstance, null, new String[] {"stop"}, ActionContext.system());
+            Artemis.execute(false, false, null, slaveInstance, null, "stop");
+            Wait.assertFalse(slaveServer::isStarted);
          }
       } finally {
-         stopServer();
+         Artemis.execute(false, false, null, masterInstance, null, "stop");
+         Wait.assertFalse(masterServer::isStarted);
       }
    }
 
diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/OptionsValidationTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/OptionsValidationTest.java
deleted file mode 100644
index 3d2f03611c..0000000000
--- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/OptionsValidationTest.java
+++ /dev/null
@@ -1,115 +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.activemq.cli.test;
-
-import com.github.rvesse.airline.parser.errors.ParseArgumentsUnexpectedException;
-import org.apache.activemq.artemis.cli.Artemis;
-import org.apache.activemq.artemis.cli.commands.ActionContext;
-import org.apache.activemq.artemis.cli.commands.InvalidOptionsError;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collection;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-@RunWith(value = Parameterized.class)
-public class OptionsValidationTest extends CliTestBase {
-
-   private File artemisInstance;
-
-   private String group;
-   private String command;
-   private boolean needInstance;
-
-   @Parameterized.Parameters(name = "group={0}, command={1}, need-instance={2}")
-   public static Collection getParameters() {
-      return Arrays.asList(new Object[][]{{null, "create", false},
-                                          {null, "run", true},
-                                          {null, "kill", true},
-                                          {null, "stop", true},
-                                          {"address", "create", false},
-                                          {"address", "delete", false},
-                                          {"address", "update", false},
-                                          {"address", "show", false},
-                                          {null, "browser", false},
-                                          {null, "consumer", false},
-                                          {null, "mask", false},
-                                          {null, "help", false},
-                                          {null, "migrate1x", false},
-                                          {null, "producer", false},
-                                          {"queue", "create", false},
-                                          {"queue", "delete", false},
-                                          {"queue", "update", false},
-                                          {"data", "print", false},
-                                          {"data", "print", true},
-                                          {"data", "exp", true},
-                                          {"data", "imp", true},
-                                          {"data", "encode", true},
-                                          {"data", "decode", true},
-                                          {"data", "compact", true},
-                                          {"user", "add", true},
-                                          {"user", "rm", true},
-                                          {"user", "list", true},
-                                          {"user", "reset", true}
-      });
-   }
-
-   public OptionsValidationTest(String group, String command, boolean needInstance) {
-      this.group = group;
-      this.command = command;
-      this.needInstance = needInstance;
-   }
-
-   @Before
-   public void setUp() throws Exception {
-      super.setup();
-      this.artemisInstance = new File(temporaryFolder.getRoot() + "instance1");
-   }
-
-   @After
-   @Override
-   public void tearDown() throws Exception {
-      super.tearDown();
-   }
-
-   @Test
-   public void testCommand() throws Exception {
-      ActionContext context = new TestActionContext();
-      String[] invalidArgs = null;
-      if (group == null) {
-         invalidArgs = new String[] {command, "--blahblah-" + command, "--rubbish-" + command + "=" + "more-rubbish", "--input=blahblah"};
-      } else {
-         invalidArgs = new String[] {group, command, "--blahblah-" + command, "--rubbish-" + command + "=" + "more-rubbish", "--input=blahblah"};
-      }
-      try {
-         Artemis.internalExecute(null, needInstance ? this.artemisInstance : null, null, invalidArgs, context);
-         fail("cannot detect invalid options");
-      } catch (InvalidOptionsError e) {
-         assertTrue(e.getMessage().contains("Found unexpected parameters"));
-      } catch (ParseArgumentsUnexpectedException e) {
-         //airline can detect some invalid args during parsing
-         //which is fine.
-      }
-   }
-}
diff --git a/artemis-distribution/src/main/resources/licenses/bin/LICENSE b/artemis-distribution/src/main/resources/licenses/bin/LICENSE
index 2c4e096055..f40c8ec910 100644
--- a/artemis-distribution/src/main/resources/licenses/bin/LICENSE
+++ b/artemis-distribution/src/main/resources/licenses/bin/LICENSE
@@ -286,6 +286,12 @@ For HdrHistogram:
 This product bundles HdrHistogram, which is available under the
 "2-clause BSD" license. For details, see licences/LICENSE-hdrhistogram.txt.
 
+==============================================================================
+For JLine:
+==============================================================================
+This product bundles JLine, which is available under the
+"3-clause BSD" license. For details, see licences/LICENSE-jline.txt.
+
 ==============================================================================
 Apache ActiveMQ Artemis Subcomponents:
 
diff --git a/artemis-distribution/src/main/resources/licenses/bin/licenses/LICENSE-jline.txt b/artemis-distribution/src/main/resources/licenses/bin/licenses/LICENSE-jline.txt
new file mode 100644
index 0000000000..d159219c31
--- /dev/null
+++ b/artemis-distribution/src/main/resources/licenses/bin/licenses/LICENSE-jline.txt
@@ -0,0 +1,34 @@
+Copyright (c) 2002-2018, the original author or authors.
+All rights reserved.
+
+https://opensource.org/licenses/BSD-3-Clause
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md
index 7fadf46a19..0ece7a6a66 100644
--- a/docs/user-manual/en/SUMMARY.md
+++ b/docs/user-manual/en/SUMMARY.md
@@ -8,6 +8,7 @@
 * [Messaging Concepts](messaging-concepts.md)
 * [Architecture](architecture.md)
 * [Using the Server](using-server.md)
+* [Command Line Interface](using-cli.md)
 * [Upgrading](upgrading.md)
 * Address
     * [Model](address-model.md)
diff --git a/docs/user-manual/en/architecture.md b/docs/user-manual/en/architecture.md
index 5dd62db87e..572ff60207 100644
--- a/docs/user-manual/en/architecture.md
+++ b/docs/user-manual/en/architecture.md
@@ -66,9 +66,6 @@ The normal stand-alone messaging broker configuration comprises a core
 messaging broker and a number of protocol managers that provide support for the
 various protocol mentioned earlier.
 
-The standalone broker configuration uses
-[Airline](http://rvesse.github.io/airline/) for bootstrapping the Broker.
-
 The stand-alone broker architecture is shown in figure 3.3 below:
 
 ![ActiveMQ Artemis architecture3](images/architecture3.jpg)
diff --git a/docs/user-manual/en/embedding-activemq.md b/docs/user-manual/en/embedding-activemq.md
index be4b15b380..7b00c45089 100644
--- a/docs/user-manual/en/embedding-activemq.md
+++ b/docs/user-manual/en/embedding-activemq.md
@@ -122,7 +122,4 @@ server.start();
 
 You may also choose to use a dependency injection framework such as The Spring
 Framework. See [Spring Integration](spring-integration.md) for more details on
-Spring and Apache ActiveMQ Artemis.
-
-Apache ActiveMQ Artemis standalone uses
-[Airline](http://rvesse.github.io/airline/) to bootstrap.
+Spring and Apache ActiveMQ Artemis.
\ No newline at end of file
diff --git a/docs/user-manual/en/using-cli.md b/docs/user-manual/en/using-cli.md
new file mode 100644
index 0000000000..059ea334f1
--- /dev/null
+++ b/docs/user-manual/en/using-cli.md
@@ -0,0 +1,409 @@
+## Command Line Interface
+
+ActiveMQ Artemis has a Command Line Interface (CLI) that can used to manage a few aspects of the broker like instance creation, basic user management, queues, etc.
+
+There are two ways the CLI can be used:
+
+- Bash Shell
+    - A traditional CLI that can be accessed by './artemis <COMMAND> ARGUMENTS...'
+- Artemis Shell
+    - A shell emulation that is accesssed by './artemis' or './artemis shell".
+
+
+All commands available through the traditional Command Line Interface (CLI) are also available through the Shell interface.
+
+The Shell interface will reuse some information as you repeat commands, such as the user, password, and target broker URI making the repetitive use a bit simpler.
+
+The CLI interface however could be used in your bash scripts for your own automation while the Shell session being a user interface in a terminal session.
+
+### Getting Help
+
+All of these commands can be accessed in the form of calling "artemis [COMMAND] [PARAMETERS]". You can get a complete list of available commands by typing:
+
+./artemis help
+
+```shell
+Usage: artemis [COMMAND]
+ActiveMQ Artemis Command Line
+Commands:
+  help           use 'help <command>' for more information
+  auto-complete  Generates the auto complete script file to be used in bash or
+                   zsh.
+  shell          JLine3 shell helping using the CLI
+  producer       Send message(s) to a broker.
+  transfer       Move messages from one destination towards another destination.
+  consumer       Consume messages from a queue.
+  browser        Browse messages on a queue.
+  mask           Mask a password and print it out.
+  version        Print version information.
+  perf           use 'help perf' for sub commands list
+  check          use 'help check' for sub commands list
+  queue          use 'help check' for sub commands list
+  address        use 'help address' for sub commands list
+  data           use 'help data' for sub commands list
+  create         Create a new broker instance.
+  upgrade        Update a broker instance to the current artemis.home, keeping
+                   all the data and broker.xml. Warning: backup your instance
+                   before using this command and compare the files.
+```
+
+It is also possible to use help at a specific command or sub-command for more information.
+Examples:
+
+To get a list of sub commands for data, you type './artemis help data':
+
+```shell
+Commands:
+  recover  Recover (undelete) every message on the journal by creating a new
+             output journal. Rolled back and acked messages will be sent out to
+             the output as much as possible.
+  print    Print data records information. WARNING: don't use while a
+             production server is running.
+  exp      Export all message-data using an XML that could be interpreted by
+             any system.
+  imp      Import all message-data using an XML that could be interpreted by
+             any system.
+  decode   Decode a journal's internal format into a new set of journal files.
+  encode   Encode a set of journal files into an internal encoded data format.
+  compact  Compact the journal of a non running server.
+```
+
+Or getting information about a particular command.
+
+For example './artemis help create'
+
+```shell
+Usage: artemis create [--aio] [--allow-anonymous] [--autocreate] [--autodelete]
+                      [--blocking] [--clustered] [--cygwin]
+                      [--disable-persistence] [--failover-on-shutdown]
+                      [--force] [--jdbc] [--mapped] [--nio]
+                      [--no-amqp-acceptor] [--no-autocreate] [--no-autotune]
+                      [--no-fsync] [--no-hornetq-acceptor] [--no-mqtt-acceptor]
+                      [--no-stomp-acceptor] [--no-web] [--paging]
+                      [--relax-jolokia] [--replicated] [--require-login]
+                      [--shared-store] [--silent] [--slave]
+                      [--support-advisory]
+                      [--suppress-internal-management-objects]
+                      [--use-client-auth] [--verbose] [--windows]
+                      [--addresses=<addresses>]
+                      [--cluster-password=<clusterPassword>]
+                      [--cluster-user=<clusterUser>] [--data=<data>]
+                      [--default-port=<defaultPort>] [--encoding=<encoding>]
+                      [--etc=<etc>] [--global-max-messages=<globalMaxMessages>]
+                      [--global-max-size=<globalMaxSize>] [--home=<home>]
+                      [--host=<host>] [--http-host=<httpHost>]
+                      [--http-port=<httpPort>] [--java-memory=<javaMemory>]
+                      [--jdbc-bindings-table-name=<jdbcBindings>]
+                      [--jdbc-connection-url=<jdbcURL>]
+                      [--jdbc-driver-class-name=<jdbcClassName>]
+                      [--jdbc-large-message-table-name=<jdbcLargeMessages>]
+                      [--jdbc-lock-expiration=<jdbcLockExpiration>]
+                      [--jdbc-lock-renew-period=<jdbcLockRenewPeriod>]
+                      [--jdbc-message-table-name=<jdbcMessages>]
+                      [--jdbc-network-timeout=<jdbcNetworkTimeout>]
+                      [--jdbc-node-manager-table-name=<jdbcNodeManager>]
+                      [--jdbc-page-store-table-name=<jdbcPageStore>]
+                      [--journal-device-block-size=<journalDeviceBlockSize>]
+                      [--journal-retention=<retentionDays>]
+                      [--journal-retention-max-bytes=<retentionMaxBytes>]
+                      [--max-hops=<maxHops>]
+                      [--message-load-balancing=<messageLoadBalancing>]
+                      [--name=<name>] [--password=<password>] [--ping=<ping>]
+                      [--port-offset=<portOffset>] [--queues=<queues>]
+                      [--role=<role>] [--security-manager=<securityManager>]
+                      [--ssl-key=<sslKey>]
+                      [--ssl-key-password=<sslKeyPassword>]
+                      [--ssl-trust=<sslTrust>]
+                      [--ssl-trust-password=<sslTrustPassword>]
+                      [--staticCluster=<staticNode>] [--user=<user>]
+                      [--java-options=<javaOptions>]... <directory>
+Create a new broker instance.
+      <directory>            The instance directory to hold the broker's
+                               configuration and data. Path must be writable.
+      --addresses=<addresses>
+                             A comma separated list of addresses with the
+                               option to specify a routing type, e.g.
+                               --addresses myAddress1,myAddress2:anycast.
+                               Routing-type default: multicast.
+      --aio                  Set the journal as asyncio.
+      --allow-anonymous      Allow connections from users with no security
+                               credentials. Opposite of --require-login.
+                               Default: input.
+      --autocreate           Allow automatic creation of addresses & queues.
+                               Default: true.
+      --autodelete           Allow automatic deletion of addresses & queues.
+                               Default: false.
+      --blocking             Block producers when address becomes full.
+                               Opposite of --paging. Default: false.
+      --cluster-password=<clusterPassword>
+                             The password to use for clustering. Default: input.
+      --cluster-user=<clusterUser>
+                             The user to use for clustering. Default: input.
+      --clustered            Enable clustering.
+      --cygwin               Force Cygwin script creation. Default: based on
+                               your actual system.
+      --data=<data>          Directory where ActiveMQ data are stored. Paths
+                               can be absolute or relative to artemis.instance
+                               directory. Default: data.
+      --default-port=<defaultPort>
+                             The port number to use for the main 'artemis'
+                               acceptor. Default: 61616.
+      --disable-persistence  Disable message persistence to the journal
+      --encoding=<encoding>  The encoding that text files should use. Default:
+                               UTF-8.
+      --etc=<etc>            Directory where ActiveMQ configuration is located.
+                               Paths can be absolute or relative to artemis.
+                               instance directory. Default: etc.
+      --failover-on-shutdown Whether broker shutdown will trigger failover for
+                               clients using the core protocol. Valid only for
+                               shared store. Default: false.
+      --force                Overwrite configuration at destination directory.
+      --global-max-messages=<globalMaxMessages>
+                             Maximum number of messages that will be accepted
+                               in memory before using address full policy mode.
+                               Default: undefined.
+      --global-max-size=<globalMaxSize>
+                             Maximum amount of memory which message data may
+                               consume. Default: half of the JVM's max memory.
+      --home=<home>          Directory where ActiveMQ Artemis is installed.
+      --host=<host>          Broker's host name. Default: 0.0.0.0 or input if
+                               clustered).
+      --http-host=<httpHost> Embedded web server's host name. Default:
+                               localhost.
+      --http-port=<httpPort> Embedded web server's port. Default: 8161.
+      --java-memory=<javaMemory>
+                             Define the -Xmx memory parameter for the broker.
+                               Default: 2G.
+      --java-options=<javaOptions>
+                             Extra Java options to be passed to the profile.
+      --jdbc                 Store message data in JDBC instead of local files.
+      --jdbc-bindings-table-name=<jdbcBindings>
+                             Name of the jdbc bindings table.
+      --jdbc-connection-url=<jdbcURL>
+                             The URL used for the database connection.
+      --jdbc-driver-class-name=<jdbcClassName>
+                             JDBC driver classname.
+      --jdbc-large-message-table-name=<jdbcLargeMessages>
+                             Name of the large messages table.
+      --jdbc-lock-expiration=<jdbcLockExpiration>
+                             Lock expiration (in milliseconds).
+      --jdbc-lock-renew-period=<jdbcLockRenewPeriod>
+                             Lock Renew Period (in milliseconds).
+      --jdbc-message-table-name=<jdbcMessages>
+                             Name of the jdbc messages table.
+      --jdbc-network-timeout=<jdbcNetworkTimeout>
+                             Network timeout (in milliseconds).
+      --jdbc-node-manager-table-name=<jdbcNodeManager>
+                             Name of the jdbc node manager table.
+      --jdbc-page-store-table-name=<jdbcPageStore>
+                             Name of the page store messages table.
+      --journal-device-block-size=<journalDeviceBlockSize>
+                             The block size of the journal's storage device.
+                               Default: 4096.
+      --journal-retention=<retentionDays>
+                             Configure journal retention in days. If > 0 then
+                               enable journal-retention-directory from broker.
+                               xml allowing replay options.
+      --journal-retention-max-bytes=<retentionMaxBytes>
+                             Maximum number of bytes to keep in the retention
+                               directory.
+      --mapped               Set the journal as mapped.
+      --max-hops=<maxHops>   Number of hops on the cluster configuration.
+      --message-load-balancing=<messageLoadBalancing>
+                             Message load balancing policy for cluster.
+                               Default: ON_DEMAND. Valid values: ON_DEMAND,
+                               STRICT, OFF, OFF_WITH_REDISTRIBUTION.
+      --name=<name>          The name of the broker. Default: same as host name.
+      --nio                  Set the journal as nio.
+      --no-amqp-acceptor     Disable the AMQP specific acceptor.
+      --no-autocreate        Disable auto creation for addresses & queues.
+      --no-autotune          Disable auto tuning of the journal-buffer-timeout
+                               in broker.xml.
+      --no-fsync             Disable usage of fdatasync (channel.force(false)
+                               from Java NIO) on the journal.
+      --no-hornetq-acceptor  Disable the HornetQ specific acceptor.
+      --no-mqtt-acceptor     Disable the MQTT specific acceptor.
+      --no-stomp-acceptor    Disable the STOMP specific acceptor.
+      --no-web               Whether to omit the web-server definition from
+                               bootstrap.xml.
+      --paging               Page messages to disk when address becomes full.
+                               Opposite of --blocking. Default: true.
+      --password=<password>  The user's password. Default: input.
+      --ping=<ping>          A comma separated string to be passed on to the
+                               broker config as network-check-list. The broker
+                               will shutdown when all these addresses are
+                               unreachable.
+      --port-offset=<portOffset>
+                             How much to off-set the ports of every acceptor.
+      --queues=<queues>      A comma separated list of queues with the option
+                               to specify a routing type, e.g. --queues
+                               myQueue1,myQueue2:multicast. Routing-type
+                               default: anycast.
+      --relax-jolokia        Disable strict checking in jolokia-access.xml.
+      --replicated           Enable broker replication.
+      --require-login        Require security credentials from users for
+                               connection. Opposite of --allow-anonymous.
+      --role=<role>          The name for the role created. Default: amq.
+      --security-manager=<securityManager>
+                             Which security manager to use - jaas or basic.
+                               Default: jaas.
+      --shared-store         Enable broker shared store.
+      --silent               Disable all the inputs, and make a best guess for
+                               any required input.
+      --slave                Be a slave broker. Valid for shared store or
+                               replication.
+      --ssl-key=<sslKey>     Embedded web server's key store path.
+      --ssl-key-password=<sslKeyPassword>
+                             The key store's password.
+      --ssl-trust=<sslTrust> The trust store path in case of client
+                               authentication.
+      --ssl-trust-password=<sslTrustPassword>
+                             The trust store's password.
+      --staticCluster=<staticNode>
+                             Cluster node connectors list separated by comma, e.
+                               g. "tcp://server:61616,tcp://server2:61616,tcp:
+                               //server3:61616".
+      --support-advisory     Support advisory messages for the OpenWire
+                               protocol.
+      --suppress-internal-management-objects
+                             Do not register any advisory addresses/queues for
+                               the OpenWire protocol with the broker's
+                               management service.
+      --use-client-auth      Require client certificate authentication when
+                               connecting to the embedded web server.
+      --user=<user>          The username. Default: input.
+      --verbose              Print additional information.
+      --windows              Force Windows script creation. Default: based on
+                               your actual system.
+```
+
+#### Bash and ZSH auto complete
+
+Bash and ZSH provide ways to auto-complete command line interfaces. To integrate with that functionality you have the option to generate the 'auto-complete' script:
+
+```shell
+./artemis auto-complete
+```
+
+This will generate a file named auto-complete-artemis.sh that should be used with:
+
+```shell
+source ./auto-complete-artemis.sh
+```
+
+After the auto completion installed in the bash session, bash would start to show auto-completion information upon the pressure of the key [TAB]:
+
+```shell
+ bin % ./artemis
+activation     browser        create         kill           perf-journal   run            transfer       version        
+address        check          data           mask           producer       shell          upgrade        
+auto-complete  consumer       help           perf           queue          stop           user
+```
+
+Same showing options:
+
+```
+ bin % ./artemis create --
+--addresses                             --jdbc-bindings-table-name              --paging
+--aio                                   --jdbc-connection-url                   --password
+--allow-anonymous                       --jdbc-driver-class-name                --ping
+--autocreate                            --jdbc-large-message-table-name         --port-offset
+--autodelete                            --jdbc-lock-expiration                  --queues
+--blocking                              --jdbc-lock-renew-period                --relax-jolokia
+--cluster-password                      --jdbc-message-table-name               --replicated
+--cluster-user                          --jdbc-network-timeout                  --require-login
+--clustered                             --jdbc-node-manager-table-name          --role
+
+```
+
+## Input required
+
+Some functionality on the CLI may require user input if not provided through a parameter in cases like connecting to a broker or creating the broker instance.
+
+For example:
+
+```shell
+bin % ./artemis queue stat
+Connection brokerURL = tcp://localhost:61616
+Connection failed::AMQ229031: Unable to validate user from /127.0.0.1:56320. Username: null; SSL certificate subject DN: unavailable
+
+--user:
+Type the username for a retry
+a
+
+--password: is mandatory with this configuration:
+Type the password for a retry
+```
+
+## Artemis Shell
+
+To initialize the shell session, type './artemis shell' (or just ./artemis if you prefer):
+
+```shell
+./artemis
+```
+
+The ActiveMQ Artemis Shell provides an interface that can be used to call the CLI commands directly without leaving the Java Virtual Machine.
+
+```shell
+     _        _               _
+    / \  ____| |_  ___ __  __(_) _____
+   / _ \|  _ \ __|/ _ \  \/  | |/  __/
+  / ___ \ | \/ |_/  __/ |\/| | |\___ \
+ /_/   \_\|   \__\____|_|  |_|_|/___ /
+ Apache ActiveMQ Artemis
+
+
+For a list of commands, type help or press <TAB>:
+Type exit or press <CTRL-D> to leave the session:
+Apache ActiveMQ Artemis >
+```
+
+### Connecting
+
+It is possible to authenticate your CLI client once to the server, and reuse the connection information for future commands being performed:
+
+```shell
+Apache ActiveMQ Artemis > connect --user=a --password=b --url tcp://localhost:61616
+Connection brokerURL = tcp://localhost:61616
+Connection Successful!
+```
+Now any command requiring authentication will reuse these parameters. 
+
+For example the sub-command 'queue stat' will reuse previous information to perform its connection to the broker.
+
+````shell
+Apache ActiveMQ Artemis > queue stat
+Connection brokerURL = tcp://localhost:61616
+|NAME                     |ADDRESS                  |CONSUMER_COUNT|MESSAGE_COUNT|MESSAGES_ADDED|DELIVERING_COUNT|MESSAGES_ACKED|SCHEDULED_COUNT|ROUTING_TYPE|
+|DLQ                      |DLQ                      |0             |0            |0             |0               |0             |0              |ANYCAST     |
+|ExpiryQueue              |ExpiryQueue              |0             |0            |0             |0               |0             |0              |ANYCAST     |
+|Order                    |Order                    |0             |4347         |4347          |0               |0             |0              |ANYCAST     |
+|activemq.management.0b...|activemq.management.0b...|1             |0            |0             |0               |0             |0              |MULTICAST   |
+````
+
+#### Connecting from Command Line
+
+To make the initial connection simpler, it is possible to start the shell with an initial connection from the startup:
+
+```shell
+./artemis shell --user <username> --password <password> --url <tcp://myserver:myport>
+```
+
+The CLI should not ask for an user/password for any further commands with this option being used:
+
+Example:
+
+```shell
+bin % ./artemis shell --user a --password b
+...
+
+
+Apache ActiveMQ Artemis > queue stat
+Connection brokerURL = tcp://localhost:61616
+|NAME                     |ADDRESS                  |CONSUMER_COUNT|MESSAGE_COUNT|MESSAGES_ADDED|DELIVERING_COUNT|MESSAGES_ACKED|SCHEDULED_COUNT|ROUTING_TYPE|
+|DLQ                      |DLQ                      |0             |0            |0             |0               |0             |0              |ANYCAST     |
+|ExpiryQueue              |ExpiryQueue              |0             |0            |0             |0               |0             |0              |ANYCAST     |
+|TEST                     |TEST                     |0             |8743         |8743          |0               |0             |0              |ANYCAST     |
+|activemq.management.2a...|activemq.management.2a...|1             |0            |0             |0               |0             |0              |MULTICAST   |
+```
\ No newline at end of file
diff --git a/docs/user-manual/en/using-server.md b/docs/user-manual/en/using-server.md
index 1c6b087c39..59681e2a0f 100644
--- a/docs/user-manual/en/using-server.md
+++ b/docs/user-manual/en/using-server.md
@@ -63,6 +63,11 @@ On Unix systems, it is a common convention to store this kind of runtime data
 under the `/var/lib` directory.  For example, to create an instance at
 `/var/lib/mybroker`, run the following commands in your command line shell:
 
+Before the broker is used, a broker *instance* must be created.
+This process requires the use of the [Command Line Interface](using-cli.md) which is better explained in its own chapter.
+
+In the following example a broker instance named mybroker will be created.
+
 ```sh
 cd /var/lib
 ${ARTEMIS_HOME}/bin/artemis create mybroker
diff --git a/pom.xml b/pom.xml
index 806c4611b8..347ac8226b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -130,7 +130,9 @@
       <johnzon.version>1.2.21</johnzon.version>
       <hawtbuff.version>1.11</hawtbuff.version>
       <hawtdispatch.version>1.22</hawtdispatch.version>
-      <airline.version>2.9.0</airline.version>
+      <picocli.version>4.7.4</picocli.version>
+      <jline.version>3.23.0</jline.version>
+      <jansi.version>2.4.0</jansi.version>
       <jakarta.activation-api.version>1.2.2</jakarta.activation-api.version>
       <jakarta.annotation-api.version>1.3.5</jakarta.annotation-api.version>
       <jakarta.ejb-api.version>3.2.6</jakarta.ejb-api.version>
@@ -586,15 +588,28 @@
             <!-- License: Apache 2.0 -->
          </dependency>
          <dependency>
-            <groupId>com.github.rvesse</groupId>
-            <artifactId>airline</artifactId>
-            <version>${airline.version}</version>
-            <exclusions>
-               <exclusion>
-                  <groupId>com.github.rvesse</groupId>
-                  <artifactId>airline-backcompat-javaxinject</artifactId>
-               </exclusion>
-            </exclusions>
+            <groupId>info.picocli</groupId>
+            <artifactId>picocli</artifactId>
+            <version>${picocli.version}</version>
+            <!-- License: Apache 2.0 -->
+         </dependency>
+         <dependency>
+            <groupId>info.picocli</groupId>
+            <artifactId>picocli-shell-jline3</artifactId>
+            <version>${picocli.version}</version>
+            <!-- License: Apache 2.0 -->
+         </dependency>
+         <dependency>
+            <groupId>org.jline</groupId>
+            <artifactId>jline</artifactId>
+            <version>${jline.version}</version>
+            <!-- License: BSD 3-Clause -->
+         </dependency>
+         <dependency>
+            <!-- used by jline to provide an ansi terminal -->
+            <groupId>org.fusesource.jansi</groupId>
+            <artifactId>jansi</artifactId>
+            <version>${jansi.version}</version>
             <!-- License: Apache 2.0 -->
          </dependency>
          <!--needed to compile transport jar-->
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/RecoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/RecoverTest.java
index 299e7e71c6..e6374f93a6 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/RecoverTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/RecoverTest.java
@@ -28,6 +28,7 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 
+import org.apache.activemq.artemis.cli.commands.ActionContext;
 import org.apache.activemq.artemis.cli.commands.tools.RecoverMessages;
 import org.apache.activemq.artemis.core.config.Configuration;
 import org.apache.activemq.artemis.core.server.JournalType;
@@ -173,7 +174,7 @@ public class RecoverTest extends JMSTestBase {
 
       File newJournalLocation = new File(server.getConfiguration().getJournalLocation().getParentFile(), "recovered");
 
-      RecoverMessages.recover(server.getConfiguration(), server.getConfiguration().getJournalRetentionDirectory(), newJournalLocation, server.getConfiguration().getLargeMessagesLocation(), false);
+      RecoverMessages.recover(new ActionContext(), server.getConfiguration(), server.getConfiguration().getJournalRetentionDirectory(), newJournalLocation, server.getConfiguration().getLargeMessagesLocation(), false);
 
       if (large) {
          File[] largeMessageFiles = server.getConfiguration().getLargeMessagesLocation().listFiles();
diff --git a/tests/smoke-tests/pom.xml b/tests/smoke-tests/pom.xml
index f790821530..8200629364 100644
--- a/tests/smoke-tests/pom.xml
+++ b/tests/smoke-tests/pom.xml
@@ -1220,6 +1220,7 @@
                      <password>artemis</password>
                      <allowAnonymous>true</allowAnonymous>
                      <noWeb>true</noWeb>
+                     <portOffset>100</portOffset>
                      <instance>${basedir}/target/clusteredLargeMessage/cluster2</instance>
                      <configuration>${basedir}/target/classes/servers/clusteredLargeMessage/cluster2</configuration>
                      <args>
@@ -1230,8 +1231,6 @@
                         <arg>tcp://localhost:61616</arg>
                         <arg>--max-hops</arg>
                         <arg>1</arg>
-                        <arg>--port-offset</arg>
-                        <arg>100</arg>
                         <arg>--queues</arg>
                         <arg>testQueue</arg>
                      </args>
diff --git a/tests/soak-tests/pom.xml b/tests/soak-tests/pom.xml
index 1cb11fcb0b..9559ad2491 100644
--- a/tests/soak-tests/pom.xml
+++ b/tests/soak-tests/pom.xml
@@ -387,8 +387,6 @@
                         <arg>--clustered</arg>
                         <arg>--staticCluster</arg>
                         <arg>tcp://localhost:61716</arg>
-                        <arg>--java-options</arg>
-                        <arg>-Djava.rmi.server.hostname=localhost</arg>
                         <arg>--queues</arg>
                         <arg>ClusteredLargeMessageInterruptTest</arg>
                         <arg>--name</arg>
@@ -417,8 +415,6 @@
                         <arg>--clustered</arg>
                         <arg>--staticCluster</arg>
                         <arg>tcp://localhost:61616</arg>
-                        <arg>--java-options</arg>
-                        <arg>-Djava.rmi.server.hostname=localhost</arg>
                         <arg>--queues</arg>
                         <arg>ClusteredLargeMessageInterruptTest</arg>
                         <arg>--name</arg>