Skip to content

Commit

Permalink
ARTEMIS-4372 Implement Pico-cli and script auto-complete
Browse files Browse the repository at this point in the history
  • Loading branch information
clebertsuconic committed Jul 26, 2023
1 parent e60bd1a commit 4e47aee
Show file tree
Hide file tree
Showing 80 changed files with 899 additions and 1,146 deletions.
4 changes: 2 additions & 2 deletions artemis-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@
</dependency>

<dependency>
<groupId>com.github.rvesse</groupId>
<artifactId>airline</artifactId>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
155 changes: 76 additions & 79 deletions artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,59 +22,34 @@
import java.io.PrintStream;
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.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.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;

/**
* Artemis is the main CLI entry point for managing/running a broker.
Expand All @@ -87,7 +62,24 @@
* 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 {
@CommandLine.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() {
commandLine.usage(System.out);
}

public static void main(String... args) throws Exception {
String home = System.getProperty("artemis.home");
Expand Down Expand Up @@ -179,10 +171,6 @@ public static Object execute(boolean inputEnabled, boolean useSystemOut, File ar
} catch (RuntimeException | InvalidOptionsError re) {
context.err.println(re.getMessage());
context.out.println();

Cli<Action> parser = builder(null).build();

parser.parse("help").execute(context);
return re;
} finally {
ActionContext.setSystem(new ActionContext());
Expand All @@ -194,62 +182,71 @@ public static Object execute(boolean inputEnabled, boolean useSystemOut, File ar
* 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);
CommandLine commandLine = builder(artemisInstance);
CommandLine.ParseResult parseResult = commandLine.parseArgs(args);
for (; parseResult.hasSubcommand(); parseResult = parseResult.subcommand()) {
}
Object userObject = parseResult.commandSpec().userObject();
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 + " ");
return action.execute(context);
} else {
if (userObject instanceof Runnable) {
((Runnable) userObject).run();
}
context.out.println();
context.out.println("Home::" + action.getBrokerHome() + ", Instance::" + action.getBrokerInstance());
}

action.checkOptions(args);
return action.execute(context);
return null;
}

private static CliBuilder<Action> builder(File artemisInstance) {
private static CommandLine 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);
Artemis artemis = new Artemis();

CommandLine commandLine = new CommandLine(artemis);
artemis.setCommandLine(commandLine);
commandLine.addSubcommand(new AutoCompletion(commandLine));

builder.withGroup("check").withDescription("Check tools group (node|queue) (example ./artemis check node)").
withDefaultCommand(HelpCheck.class).withCommands(NodeCheck.class, QueueCheck.class);
HelpAction help = new HelpAction();
help.setCommandLine(commandLine);

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);
commandLine.addSubcommand(help).addSubcommand(new Producer()).addSubcommand(new Transfer()).addSubcommand(new Consumer()).addSubcommand(new Browse()).addSubcommand(new Mask()).addSubcommand(new PrintVersion());

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);
commandLine.addSubcommand(new PerfGroup(commandLine));
commandLine.addSubcommand(new CheckGroup(commandLine));
commandLine.addSubcommand(new QueueGroup(commandLine));
commandLine.addSubcommand(new AddressGroup(commandLine));

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);
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());
} 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);
commandLine.addSubcommand(new DataGroup(commandLine));
commandLine.addSubcommand(new Create());
commandLine.addSubcommand(new Upgrade());
}

return builder;
return commandLine;
}

public static void printBanner(PrintStream out) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,4 @@ public interface Action {

String getBrokerHome();

void checkOptions(String[] options) throws InvalidOptionsError;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@
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;
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;

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.")
@CommandLine.Option(names = "--verbose", description = "Print additional information.")
public boolean verbose;

// this could be changed by a test accessor for testing purposes.
Expand Down Expand Up @@ -208,14 +208,16 @@ the ARTEMIS_HOME variable will include back slashes (An invalid file URI charact
@Override
public Object execute(ActionContext context) throws Exception {
this.actionContext = context;
ActionContext.setSystem(context);

return null;
}

@Override
public void checkOptions(String[] options) throws InvalidOptionsError {
OptionsUtil.checkCommandOptions(this.getClass(), options);
public void run() {
try {
execute(getActionContext());
} catch (Exception e) {
e.printStackTrace();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 picocli.AutoComplete;
import picocli.CommandLine;

@CommandLine.Command(name = "auto-complete", description = "Generates the auto complete helper file")
public class AutoCompletion implements Runnable {

public AutoCompletion(CommandLine parent) {
this.parent = parent;
}

CommandLine parent;

@CommandLine.Parameters (description = "The generated auto-complete script", defaultValue = "artemis-auto-complete.sh")
File autoCompleteFile;

@Override
public void run() {
try {
AutoComplete.bash("artemis", autoCompleteFile, null, parent);
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();
}
}
}
Loading

0 comments on commit 4e47aee

Please sign in to comment.