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.
This commit is contained in:
Clebert Suconic 2023-07-24 13:03:15 -04:00 committed by clebertsuconic
parent 1efb7e87d8
commit 93ee61e35c
94 changed files with 1955 additions and 1205 deletions

View File

@ -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>

View File

@ -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);
if (action.isVerbose()) {
context.out.print("Executing " + action.getClass().getName() + " ");
for (String arg : args) {
context.out.print(arg + " ");
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());
}
context.out.println();
context.out.println("Home::" + action.getBrokerHome() + ", Instance::" + action.getBrokerInstance());
}
action.checkOptions(args);
return action.execute(context);
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;
}
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);
/*
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();
}
}
if (parseResult == null) {
throw new RuntimeException("Cannot match arg::" + Arrays.toString(args));
}
return parseResult.commandSpec().userObject();
}
builder.withGroup("perf").withDescription("Perf tools group (example ./artemis perf client)")
.withDefaultCommand(PerfClientCommand.class)
.withCommands(PerfProducerCommand.class, PerfConsumerCommand.class, PerfClientCommand.class);
public static CommandLine buildCommand(boolean includeInstanceCommands, boolean includeHomeCommands) {
return buildCommand(includeInstanceCommands, includeHomeCommands, false);
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);
public static CommandLine buildCommand(boolean includeInstanceCommands, boolean includeHomeCommands, boolean fromShell) {
Artemis artemis = new Artemis();
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 commandLine = new CommandLine(artemis);
artemis.setCommandLine(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);
} 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);
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));
}
return builder;
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());
}
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];

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,39 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
import picocli.CommandLine;
@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);
try {
CONNECTION_INFORMATION.remove();
createConnectionFactory();
System.out.println("Connection Successful!");
} catch (Exception e) {
System.out.println("Connection Failure!");
e.printStackTrace();
}
return null;
}
}

View File

@ -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) {

View File

@ -0,0 +1,33 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.messages.ConnectionAbstract;
import picocli.CommandLine;
@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);
CONNECTION_INFORMATION.remove();
System.out.println("Connection information cleared!");
return null;
}
}

View File

@ -16,40 +16,48 @@
*/
package org.apache.activemq.artemis.cli.commands;
import java.io.File;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import com.github.rvesse.airline.help.Help;
@Command(name = "help", description = "use 'help <command>' for more information")
public class HelpAction implements Runnable {
public class HelpAction extends Help implements Action {
CommandLine commandLine;
@Override
public boolean isVerbose() {
return false;
@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);
}
}
public CommandLine getCommandLine() {
return commandLine;
}
public HelpAction setCommandLine(CommandLine commandLine) {
this.commandLine = commandLine;
return this;
}
@Override
public void setHomeValues(File brokerHome, File brokerInstance, File etcFolder) {
public void run() {
help(commandLine, args);
}
@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 {
super.run();
return null;
}
}

View File

@ -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() {

View File

@ -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;

View File

@ -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 {

View File

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

View File

@ -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 + "]");
}
}
}
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -0,0 +1,38 @@
/*
* 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.activation;
import org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@Command(name = "activation", description = "use 'help activation' for sub commands list", subcommands = {ActivationSequenceList.class, ActivationSequenceSet.class})
public class ActivationGroup implements Runnable {
CommandLine commandLine;
public ActivationGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "activation");
}
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -0,0 +1,37 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@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 {
CommandLine commandLine;
public AddressGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "address");
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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() {

View File

@ -0,0 +1,39 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@Command(name = "check", description = "use 'help check' for sub commands list", subcommands = {NodeCheck.class, QueueCheck.class})
public class CheckGroup implements Runnable {
CommandLine commandLine;
public CheckGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "check");
}
}

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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() {

View File

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

View File

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

View File

@ -0,0 +1,48 @@
/*
* 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.messages;
import picocli.CommandLine;
public enum ConnectionProtocol {
AMQP("AMQP"), CORE("CORE");
private final String name;
ConnectionProtocol(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public static ConnectionProtocol fromString(String value) {
return ConnectionProtocol.valueOf(value.toUpperCase());
}
public static class ProtocolConverter implements CommandLine.ITypeConverter<ConnectionProtocol> {
@Override
public ConnectionProtocol convert(String value) throws Exception {
return ConnectionProtocol.fromString(value);
}
}
}

View File

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

View File

@ -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;
}

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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() {

View File

@ -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;
}

View File

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

View File

@ -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;

View File

@ -0,0 +1,38 @@
/*
* 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.messages.perf;
import org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@Command(name = "perf", description = "use 'help perf' for sub commands list", subcommands = {PerfClientCommand.class, PerfProducerCommand.class, PerfConsumerCommand.class})
public class PerfGroup implements Runnable {
CommandLine commandLine;
public PerfGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "perf");
}
}

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {

View File

@ -0,0 +1,38 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@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 {
CommandLine commandLine;
public QueueGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "queue");
}
}

View File

@ -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 the queue name OR using a full filter '--field NAME --operation EQUALS --value X'.")
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;

View File

@ -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 {

View File

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

View File

@ -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 {

View File

@ -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");
}
}

View File

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

View File

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

View File

@ -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

View File

@ -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";

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}

View File

@ -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:

View File

@ -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() {

View File

@ -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:

View File

@ -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

View File

@ -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() {

View File

@ -0,0 +1,38 @@
/*
* 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 org.apache.activemq.artemis.cli.commands.HelpAction;
import picocli.CommandLine;
import picocli.CommandLine.Command;
@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 {
CommandLine commandLine;
public UserGroup(CommandLine commandLine) {
this.commandLine = commandLine;
}
@Override
public void run() {
HelpAction.help(commandLine, "user");
}
}

View File

@ -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");

View File

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

View File

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

View File

@ -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.
}
}
}

View File

@ -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:

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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 |
```

View File

@ -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

35
pom.xml
View File

@ -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-->

View File

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

View File

@ -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>

View File

@ -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>