This is an automated email from the ASF dual-hosted git repository. technoboy pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pulsar.git
The following commit(s) were added to refs/heads/master by this push: new 69c45ad5300 [improve][cli] PIP-343: Use picocli instead of jcommander in pulsar-perf (#22303) 69c45ad5300 is described below commit 69c45ad5300e36a62a923b8eaa58aab99c6e02fb Author: crossoverJie <crossover...@gmail.com> AuthorDate: Fri Mar 22 09:12:37 2024 +0800 [improve][cli] PIP-343: Use picocli instead of jcommander in pulsar-perf (#22303) Co-authored-by: Zixuan Liu <node...@gmail.com> --- .../cli/converters/ByteUnitToLongConverter.java | 39 --------- .../pulsar/cli/converters/ByteConversionTest.java | 9 +- pulsar-testclient/pom.xml | 4 +- .../proxy/socket/client/PerformanceClient.java | 65 +++++++-------- .../apache/pulsar/testclient/BrokerMonitor.java | 30 +++---- .../testclient/CmdGenerateDocumentation.java | 67 +++++++++------ .../pulsar/testclient/LoadSimulationClient.java | 34 ++++---- .../testclient/LoadSimulationController.java | 68 +++++++-------- .../pulsar/testclient/ManagedLedgerWriter.java | 57 ++++++------- .../testclient/PerformanceBaseArguments.java | 59 +++++++------ .../pulsar/testclient/PerformanceConsumer.java | 65 ++++++++------- .../pulsar/testclient/PerformanceProducer.java | 96 +++++++++++----------- .../pulsar/testclient/PerformanceReader.java | 19 +++-- .../testclient/PerformanceTopicListArguments.java | 10 ++- .../pulsar/testclient/PerformanceTransaction.java | 45 +++++----- ...or.java => PositiveNumberParameterConvert.java} | 15 ++-- .../pulsar/testclient/GenerateDocumentionTest.java | 37 +++++++++ 17 files changed, 377 insertions(+), 342 deletions(-) diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitToLongConverter.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitToLongConverter.java deleted file mode 100644 index 6170fb489d4..00000000000 --- a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitToLongConverter.java +++ /dev/null @@ -1,39 +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.pulsar.cli.converters; - -import static org.apache.pulsar.cli.ValueValidationUtil.emptyCheck; -import com.beust.jcommander.converters.BaseConverter; - -public class ByteUnitToLongConverter extends BaseConverter<Long> { - - public ByteUnitToLongConverter(String optionName) { - super(optionName); - } - - @Override - public Long convert(String argStr) { - return parseBytes(argStr); - } - - Long parseBytes(String argStr) { - emptyCheck(getOptionName(), argStr); - return ByteUnitUtil.validateSizeString(argStr); - } -} diff --git a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/ByteConversionTest.java b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/ByteConversionTest.java index 283e94bfb9c..6e7a2e6d7e3 100644 --- a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/ByteConversionTest.java +++ b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/ByteConversionTest.java @@ -21,6 +21,7 @@ package org.apache.pulsar.cli.converters; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import org.apache.pulsar.cli.converters.picocli.ByteUnitToIntegerConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import picocli.CommandLine.TypeConversionException; @@ -59,8 +60,8 @@ public class ByteConversionTest { } @Test(dataProvider = "successfulByteUnitUtilTestCases") - public void testSuccessfulByteUnitToLongConverter(String input, long expected) { - ByteUnitToLongConverter converter = new ByteUnitToLongConverter("optionName"); + public void testSuccessfulByteUnitToLongConverter(String input, long expected) throws Exception{ + ByteUnitToLongConverter converter = new ByteUnitToLongConverter(); assertEquals(converter.convert(input), Long.valueOf(expected)); } @@ -78,8 +79,8 @@ public class ByteConversionTest { @Test(dataProvider = "failingByteUnitUtilTestCases") public void testFailedByteUnitToLongConverter(String input) { - ByteUnitToLongConverter converter = new ByteUnitToLongConverter("optionName"); - assertThrows(IllegalArgumentException.class, () -> converter.convert(input)); + ByteUnitToLongConverter converter = new ByteUnitToLongConverter(); + assertThrows(TypeConversionException.class, () -> converter.convert(input)); } @Test(dataProvider = "failingByteUnitUtilTestCases") diff --git a/pulsar-testclient/pom.xml b/pulsar-testclient/pom.xml index db2b84356e6..ecc12b2e563 100644 --- a/pulsar-testclient/pom.xml +++ b/pulsar-testclient/pom.xml @@ -97,8 +97,8 @@ </dependency> <dependency> - <groupId>com.beust</groupId> - <artifactId>jcommander</artifactId> + <groupId>info.picocli</groupId> + <artifactId>picocli</artifactId> <scope>compile</scope> </dependency> diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/proxy/socket/client/PerformanceClient.java b/pulsar-testclient/src/main/java/org/apache/pulsar/proxy/socket/client/PerformanceClient.java index 596eb8d2c28..9d95d0b74a2 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/proxy/socket/client/PerformanceClient.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/proxy/socket/client/PerformanceClient.java @@ -20,10 +20,6 @@ package org.apache.pulsar.proxy.socket.client; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.util.concurrent.RateLimiter; import io.netty.util.concurrent.DefaultThreadFactory; import java.io.FileInputStream; @@ -55,13 +51,18 @@ import org.apache.pulsar.client.api.AuthenticationFactory; import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.testclient.IMessageFormatter; import org.apache.pulsar.testclient.PerfClientUtils; -import org.apache.pulsar.testclient.PositiveNumberParameterValidator; +import org.apache.pulsar.testclient.PositiveNumberParameterConvert; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.Parameters; public class PerformanceClient { @@ -70,87 +71,87 @@ public class PerformanceClient { private static final LongAdder totalMessagesSent = new LongAdder(); private static final LongAdder totalBytesSent = new LongAdder(); private static IMessageFormatter messageFormatter = null; - private JCommander jc; + private CommandLine commander; - @Parameters(commandDescription = "Test pulsar websocket producer performance.") + @Command(description = "Test pulsar websocket producer performance.") static class Arguments { - @Parameter(names = { "-h", "--help" }, description = "Help message", help = true) + @Option(names = { "-h", "--help" }, description = "Help message", help = true) boolean help; - @Parameter(names = { "-cf", "--conf-file" }, description = "Configuration file") + @Option(names = { "-cf", "--conf-file" }, description = "Configuration file") public String confFile; - @Parameter(names = { "-u", "--proxy-url" }, description = "Pulsar Proxy URL, e.g., \"ws://localhost:8080/\"") + @Option(names = { "-u", "--proxy-url" }, description = "Pulsar Proxy URL, e.g., \"ws://localhost:8080/\"") public String proxyURL; - @Parameter(description = "persistent://tenant/ns/my-topic", required = true) + @Parameters(description = "persistent://tenant/ns/my-topic", arity = "1") public List<String> topics; - @Parameter(names = { "-r", "--rate" }, description = "Publish rate msg/s across topics") + @Option(names = { "-r", "--rate" }, description = "Publish rate msg/s across topics") public int msgRate = 100; - @Parameter(names = { "-s", "--size" }, description = "Message size in byte") + @Option(names = { "-s", "--size" }, description = "Message size in byte") public int msgSize = 1024; - @Parameter(names = { "-t", "--num-topic" }, description = "Number of topics", - validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "-t", "--num-topic" }, description = "Number of topics", + converter = PositiveNumberParameterConvert.class + ) public int numTopics = 1; - @Parameter(names = { "--auth_plugin" }, description = "Authentication plugin class name", hidden = true) + @Option(names = { "--auth_plugin" }, description = "Authentication plugin class name", hidden = true) public String deprecatedAuthPluginClassName; - @Parameter(names = { "--auth-plugin" }, description = "Authentication plugin class name") + @Option(names = { "--auth-plugin" }, description = "Authentication plugin class name") public String authPluginClassName; - @Parameter( + @Option( names = { "--auth-params" }, description = "Authentication parameters, whose format is determined by the implementation " + "of method `configure` in authentication plugin class, for example \"key1:val1,key2:val2\" " + "or \"{\"key1\":\"val1\",\"key2\":\"val2\"}\".") public String authParams; - @Parameter(names = { "-m", + @Option(names = { "-m", "--num-messages" }, description = "Number of messages to publish in total. If <= 0, it will keep" + " publishing") public long numMessages = 0; - @Parameter(names = { "-f", "--payload-file" }, description = "Use payload from a file instead of empty buffer") + @Option(names = { "-f", "--payload-file" }, description = "Use payload from a file instead of empty buffer") public String payloadFilename = null; - @Parameter(names = { "-e", "--payload-delimiter" }, + @Option(names = { "-e", "--payload-delimiter" }, description = "The delimiter used to split lines when using payload from a file") // here escaping \n since default value will be printed with the help text public String payloadDelimiter = "\\n"; - @Parameter(names = { "-fp", "--format-payload" }, + @Option(names = { "-fp", "--format-payload" }, description = "Format %i as a message index in the stream from producer and/or %t as the timestamp" + " nanoseconds") public boolean formatPayload = false; - @Parameter(names = {"-fc", "--format-class"}, description = "Custom Formatter class name") + @Option(names = {"-fc", "--format-class"}, description = "Custom Formatter class name") public String formatterClass = "org.apache.pulsar.testclient.DefaultMessageFormatter"; - @Parameter(names = { "-time", + @Option(names = { "-time", "--test-duration" }, description = "Test duration in secs. If <= 0, it will keep publishing") public long testTime = 0; } public Arguments loadArguments(String[] args) { Arguments arguments = new Arguments(); - jc = new JCommander(arguments); - jc.setProgramName("pulsar-perf websocket-producer"); - + commander = new CommandLine(arguments); + commander.setCommandName("pulsar-perf websocket-producer"); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { System.out.println(e.getMessage()); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } if (arguments.help) { - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } @@ -160,7 +161,7 @@ public class PerformanceClient { if (arguments.topics.size() != 1) { System.err.println("Only one topic name is allowed"); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } @@ -171,7 +172,7 @@ public class PerformanceClient { prop.load(new FileInputStream(arguments.confFile)); } catch (IOException e) { log.error("Error in loading config file"); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/BrokerMonitor.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/BrokerMonitor.java index a3e5a14a416..d195e8fd456 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/BrokerMonitor.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/BrokerMonitor.java @@ -20,10 +20,6 @@ package org.apache.pulsar.testclient; import static org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl.BROKER_LOAD_DATA_STORE_TOPIC; import static org.apache.pulsar.broker.resources.LoadBalanceResources.BROKER_TIME_AVERAGE_BASE_PATH; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.gson.Gson; import java.util.ArrayList; import java.util.Arrays; @@ -50,6 +46,11 @@ import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ScopeType; /** * Monitors brokers and prints to the console information about their system resource usages, their topic and bundle @@ -434,17 +435,18 @@ public class BrokerMonitor { } } - // JCommander arguments class. - @Parameters(commandDescription = "Monitors brokers and prints to the console information about their system " - + "resource usages, \ntheir topic and bundle counts, their message rates, and other metrics.") + // picocli arguments class. + @Command(description = "Monitors brokers and prints to the console information about their system " + + "resource usages, \ntheir topic and bundle counts, their message rates, and other metrics.", + showDefaultValues = true, scope = ScopeType.INHERIT) private static class Arguments { - @Parameter(names = { "-h", "--help" }, description = "Help message", help = true) + @Option(names = { "-h", "--help" }, description = "Help message", help = true) boolean help; - @Parameter(names = { "--connect-string" }, description = "Zookeeper or broker connect string", required = true) + @Option(names = { "--connect-string" }, description = "Zookeeper or broker connect string", required = true) public String connectString = null; - @Parameter(names = { "--extensions" }, description = "true to monitor Load Balance Extensions.") + @Option(names = { "--extensions" }, description = "true to monitor Load Balance Extensions.") boolean extensions = false; } @@ -546,14 +548,14 @@ public class BrokerMonitor { */ public static void main(String[] args) throws Exception { final Arguments arguments = new Arguments(); - final JCommander jc = new JCommander(arguments); - jc.setProgramName("pulsar-perf monitor-brokers"); + final CommandLine commander = new CommandLine(arguments); + commander.setCommandName("pulsar-perf monitor-brokers"); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { System.out.println(e.getMessage()); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/CmdGenerateDocumentation.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/CmdGenerateDocumentation.java index e3aca988655..6ff0ab296a6 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/CmdGenerateDocumentation.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/CmdGenerateDocumentation.java @@ -18,46 +18,47 @@ */ package org.apache.pulsar.testclient; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterDescription; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ScopeType; @Slf4j public class CmdGenerateDocumentation { - @Parameters(commandDescription = "Generate documentation automatically.") + @Command(description = "Generate documentation automatically.", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments { - @Parameter(names = {"-h", "--help"}, description = "Help message", help = true) + @Option(names = {"-h", "--help"}, description = "Help message", help = true) boolean help; - @Parameter(names = {"-n", "--command-names"}, description = "List of command names") + @Option(names = {"-n", "--command-names"}, description = "List of command names") private List<String> commandNames = new ArrayList<>(); } public static void main(String[] args) throws Exception { final Arguments arguments = new Arguments(); - final JCommander jc = new JCommander(arguments); - jc.setProgramName("pulsar-perf gen-doc"); + CommandLine commander = new CommandLine(arguments); + commander.setCommandName("pulsar-perf gen-doc"); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { System.out.println(e.getMessage()); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } + if (arguments.help) { - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } @@ -80,38 +81,54 @@ public class CmdGenerateDocumentation { Class<?> clazz = entry.getValue(); Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); - jc.addCommand(cmd, constructor.newInstance()); + commander.addSubcommand(cmd, constructor.newInstance()); } if (arguments.commandNames.size() == 0) { - for (Map.Entry<String, JCommander> cmd : jc.getCommands().entrySet()) { - generateDocument(cmd.getKey(), jc); + for (Map.Entry<String, CommandLine> cmd : commander.getSubcommands().entrySet()) { + generateDocument(cmd.getKey(), commander); } } else { for (String commandName : arguments.commandNames) { - generateDocument(commandName, jc); + generateDocument(commandName, commander); } } } - private static String generateDocument(String module, JCommander parentCmd) { + private static String generateDocument(String module, CommandLine parentCmd) { StringBuilder sb = new StringBuilder(); - JCommander cmd = parentCmd.getCommands().get(module); + CommandLine cmd = parentCmd.getSubcommands().get(module); sb.append("## ").append(module).append("\n\n"); - sb.append(parentCmd.getUsageFormatter().getCommandDescription(module)).append("\n"); + sb.append(getCommandDescription(cmd)).append("\n"); sb.append("\n\n```shell\n") .append("$ pulsar-perf ").append(module).append(" [options]") .append("\n```"); sb.append("\n\n"); sb.append("|Flag|Description|Default|\n"); sb.append("|---|---|---|\n"); - List<ParameterDescription> options = cmd.getParameters(); - options.stream().filter(ele -> !ele.getParameterAnnotation().hidden()).forEach((option) -> - sb.append("| `").append(option.getNames()) - .append("` | ").append(option.getDescription().replace("\n", " ")) - .append("|").append(option.getDefault()).append("|\n") + List<CommandLine.Model.OptionSpec> options = cmd.getCommandSpec().options(); + options.stream().filter(ele -> !ele.hidden()).forEach((option) -> + sb.append("| `").append(String.join(", ", option.names())) + .append("` | ").append(getOptionDescription(option).replace("\n", " ")) + .append("|").append(option.defaultValueString()).append("|\n") ); System.out.println(sb.toString()); return sb.toString(); } + + public static String getCommandDescription(CommandLine commandLine) { + String[] description = commandLine.getCommandSpec().usageMessage().description(); + if (description != null && description.length != 0) { + return description[0]; + } + return ""; + } + + public static String getOptionDescription(CommandLine.Model.OptionSpec optionSpec) { + String[] description = optionSpec.description(); + if (description != null && description.length != 0) { + return description[0]; + } + return ""; + } } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationClient.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationClient.java index 982c71ce6a5..42d2f0dd514 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationClient.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationClient.java @@ -18,10 +18,6 @@ */ package org.apache.pulsar.testclient; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.util.concurrent.RateLimiter; import io.netty.util.concurrent.DefaultThreadFactory; import java.io.DataInputStream; @@ -37,7 +33,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.api.Consumer; @@ -48,6 +44,11 @@ import org.apache.pulsar.client.api.PulsarClient; import org.apache.pulsar.client.api.SizeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ScopeType; /** * LoadSimulationClient is used to simulate client load by maintaining producers and consumers for topics. Instances of @@ -170,19 +171,20 @@ public class LoadSimulationClient { } } - // JCommander arguments for starting a LoadSimulationClient. - @Parameters(commandDescription = "Simulate client load by maintaining producers and consumers for topics.") + // picocli arguments for starting a LoadSimulationClient. + @Command(description = "Simulate client load by maintaining producers and consumers for topics.", + showDefaultValues = true, scope = ScopeType.INHERIT) private static class MainArguments { - @Parameter(names = { "-h", "--help" }, description = "Help message", help = true) + @Option(names = { "-h", "--help" }, description = "Help message", help = true) boolean help; - @Parameter(names = { "--port" }, description = "Port to listen on for controller", required = true) + @Option(names = { "--port" }, description = "Port to listen on for controller", required = true) public int port; - @Parameter(names = { "--service-url" }, description = "Pulsar Service URL", required = true) + @Option(names = { "--service-url" }, description = "Pulsar Service URL", required = true) public String serviceURL; - @Parameter(names = { "-ml", "--memory-limit", }, description = "Configure the Pulsar client memory limit " + @Option(names = { "-ml", "--memory-limit", }, description = "Configure the Pulsar client memory limit " + "(eg: 32M, 64M)", converter = ByteUnitToLongConverter.class) public long memoryLimit = 0L; } @@ -310,7 +312,7 @@ public class LoadSimulationClient { private static final MessageListener<byte[]> ackListener = Consumer::acknowledgeAsync; /** - * Create a LoadSimulationClient with the given JCommander arguments. + * Create a LoadSimulationClient with the given picocli arguments. * * @param arguments * Arguments to configure this from. @@ -341,13 +343,13 @@ public class LoadSimulationClient { */ public static void main(String[] args) throws Exception { final MainArguments mainArguments = new MainArguments(); - final JCommander jc = new JCommander(mainArguments); - jc.setProgramName("pulsar-perf simulation-client"); + CommandLine commander = new CommandLine(mainArguments); + commander.setCommandName("pulsar-perf simulation-client"); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { System.out.println(e.getMessage()); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } PerfClientUtils.printJVMInformation(log); diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationController.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationController.java index e967ba9e517..94186c581eb 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationController.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/LoadSimulationController.java @@ -20,10 +20,6 @@ package org.apache.pulsar.testclient; import static org.apache.pulsar.broker.resources.LoadBalanceResources.BUNDLE_DATA_BASE_PATH; import static org.apache.pulsar.broker.resources.LoadBalanceResources.RESOURCE_QUOTA_BASE_PATH; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -56,6 +52,12 @@ import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.ScopeType; /** * This class provides a shell for the user to dictate how simulation clients should incur load. @@ -82,50 +84,50 @@ public class LoadSimulationController { private static final ExecutorService threadPool = Executors.newCachedThreadPool(); - // JCommander arguments for starting a controller via main. - @Parameters(commandDescription = "Provides a shell for the user to dictate how simulation clients should " - + "incur load.") + // picocli arguments for starting a controller via main. + @Command(description = "Provides a shell for the user to dictate how simulation clients should " + + "incur load.", showDefaultValues = true, scope = ScopeType.INHERIT) private static class MainArguments { - @Parameter(names = { "-h", "--help" }, description = "Help message", help = true) + @Option(names = { "-h", "--help" }, description = "Help message", help = true) boolean help; - @Parameter(names = { "--cluster" }, description = "Cluster to test on", required = true) + @Option(names = { "--cluster" }, description = "Cluster to test on", required = true) String cluster; - @Parameter(names = { "--clients" }, description = "Comma separated list of client hostnames", required = true) + @Option(names = { "--clients" }, description = "Comma separated list of client hostnames", required = true) String clientHostNames; - @Parameter(names = { "--client-port" }, description = "Port that the clients are listening on", required = true) + @Option(names = { "--client-port" }, description = "Port that the clients are listening on", required = true) int clientPort; } - // JCommander arguments for accepting user input. + // picocli arguments for accepting user input. private static class ShellArguments { - @Parameter(description = "Command arguments:\n" + "trade tenant namespace topic\n" + @Parameters(description = "Command arguments:\n" + "trade tenant namespace topic\n" + "change tenant namespace topic\n" + "stop tenant namespace topic\n" + "trade_group tenant group_name num_namespaces\n" + "change_group tenant group_name\n" + "stop_group tenant group_name\n" + "script script_name\n" + "copy tenant_name source_zk target_zk\n" - + "stream source_zk\n" + "simulate zk\n", required = true) + + "stream source_zk\n" + "simulate zk\n", arity = "1") List<String> commandArguments; - @Parameter(names = { "--rand-rate" }, description = "Choose message rate uniformly randomly from the next two " + @Option(names = { "--rand-rate" }, description = "Choose message rate uniformly randomly from the next two " + "comma separated values (overrides --rate)") String rangeString = ""; - @Parameter(names = { "--rate" }, description = "Messages per second") + @Option(names = { "--rate" }, description = "Messages per second") double rate = 1; - @Parameter(names = { "--rate-multiplier" }, description = "Multiplier to use for copying or streaming rates") + @Option(names = { "--rate-multiplier" }, description = "Multiplier to use for copying or streaming rates") double rateMultiplier = 1; - @Parameter(names = { "--separation" }, description = "Separation time in ms for trade_group actions " + @Option(names = { "--separation" }, description = "Separation time in ms for trade_group actions " + "(0 for no separation)") int separation = 0; - @Parameter(names = { "--size" }, description = "Message size in bytes") + @Option(names = { "--size" }, description = "Message size in bytes") int size = 1024; - @Parameter(names = { "--topics-per-namespace" }, description = "Number of topics to create per namespace in " + @Option(names = { "--topics-per-namespace" }, description = "Number of topics to create per namespace in " + "trade_group (total number of topics is num_namespaces X num_topics)") int topicsPerNamespace = 1; } @@ -212,7 +214,7 @@ public class LoadSimulationController { } /** - * Create a LoadSimulationController with the given JCommander arguments. + * Create a LoadSimulationController with the given picocli arguments. * * @param arguments * Arguments to create from. @@ -318,7 +320,7 @@ public class LoadSimulationController { outputStream.writeDouble(arguments.rate); } - // Change producer settings for a given topic and JCommander arguments. + // Change producer settings for a given topic and picocli arguments. private void change(final ShellArguments arguments, final String topic, final int client) throws Exception { outputStreams[client].write(LoadSimulationClient.CHANGE_COMMAND); writeProducerOptions(outputStreams[client], arguments, topic); @@ -360,7 +362,7 @@ public class LoadSimulationController { return clientWithTopic; } - // Trade using the arguments parsed via JCommander and the topic name. + // Trade using the arguments parsed via picocli and the topic name. private synchronized void trade(final ShellArguments arguments, final String topic, final int client) throws Exception { // Decide which client to send to randomly to preserve statelessness of @@ -632,9 +634,9 @@ public class LoadSimulationController { // Don't attempt to process blank input. if (args.length > 0 && !(args.length == 1 && args[0].isEmpty())) { final ShellArguments arguments = new ShellArguments(); - final JCommander jc = new JCommander(arguments); + final CommandLine commander = new CommandLine(arguments); try { - jc.parse(args); + commander.parseArgs(args); final String command = arguments.commandArguments.get(0); switch (command) { case "trade": @@ -687,8 +689,8 @@ public class LoadSimulationController { log.info("ERROR: Unknown command \"{}\"", command); } } catch (ParameterException ex) { - ex.printStackTrace(); - jc.usage(); + System.out.println(ex.getMessage()); + commander.usage(commander.getOut()); } catch (Exception ex) { ex.printStackTrace(); } @@ -716,13 +718,13 @@ public class LoadSimulationController { */ public static void main(String[] args) throws Exception { final MainArguments arguments = new MainArguments(); - final JCommander jc = new JCommander(arguments); - jc.setProgramName("pulsar-perf simulation-controller"); + final CommandLine commander = new CommandLine(arguments); + commander.setCommandName("pulsar-perf simulation-controller"); try { - jc.parse(args); - } catch (Exception ex) { - System.out.println(ex.getMessage()); - jc.usage(); + commander.parseArgs(args); + } catch (ParameterException e) { + System.out.println(e.getMessage()); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } (new LoadSimulationController(arguments)).run(); diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/ManagedLedgerWriter.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/ManagedLedgerWriter.java index 336461e7a68..bad8e56a638 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/ManagedLedgerWriter.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/ManagedLedgerWriter.java @@ -19,10 +19,6 @@ package org.apache.pulsar.testclient; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.util.concurrent.RateLimiter; @@ -64,6 +60,11 @@ import org.apache.pulsar.metadata.api.extended.MetadataStoreExtended; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ScopeType; public class ManagedLedgerWriter { @@ -78,61 +79,61 @@ public class ManagedLedgerWriter { private static Recorder recorder = new Recorder(TimeUnit.SECONDS.toMillis(120000), 5); private static Recorder cumulativeRecorder = new Recorder(TimeUnit.SECONDS.toMillis(120000), 5); - @Parameters(commandDescription = "Write directly on managed-ledgers") + @Command(description = "Write directly on managed-ledgers", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments { - @Parameter(names = { "-h", "--help" }, description = "Help message", help = true) + @Option(names = { "-h", "--help" }, description = "Help message", help = true) boolean help; - @Parameter(names = { "-r", "--rate" }, description = "Write rate msg/s across managed ledgers") + @Option(names = { "-r", "--rate" }, description = "Write rate msg/s across managed ledgers") public int msgRate = 100; - @Parameter(names = { "-s", "--size" }, description = "Message size") + @Option(names = { "-s", "--size" }, description = "Message size") public int msgSize = 1024; - @Parameter(names = { "-t", "--num-topic" }, - description = "Number of managed ledgers", validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "-t", "--num-topic" }, + description = "Number of managed ledgers", converter = PositiveNumberParameterConvert.class) public int numManagedLedgers = 1; - @Parameter(names = { "--threads" }, - description = "Number of threads writing", validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "--threads" }, + description = "Number of threads writing", converter = PositiveNumberParameterConvert.class) public int numThreads = 1; @Deprecated - @Parameter(names = {"-zk", "--zookeeperServers"}, + @Option(names = {"-zk", "--zookeeperServers"}, description = "ZooKeeper connection string", hidden = true) public String zookeeperServers; - @Parameter(names = {"-md", + @Option(names = {"-md", "--metadata-store"}, description = "Metadata store service URL. For example: zk:my-zk:2181") private String metadataStoreUrl; - @Parameter(names = { "-o", "--max-outstanding" }, description = "Max number of outstanding requests") + @Option(names = { "-o", "--max-outstanding" }, description = "Max number of outstanding requests") public int maxOutstanding = 1000; - @Parameter(names = { "-c", + @Option(names = { "-c", "--max-connections" }, description = "Max number of TCP connections to a single bookie") public int maxConnections = 1; - @Parameter(names = { "-m", + @Option(names = { "-m", "--num-messages" }, description = "Number of messages to publish in total. If <= 0, it will keep publishing") public long numMessages = 0; - @Parameter(names = { "-e", "--ensemble-size" }, description = "Ledger ensemble size") + @Option(names = { "-e", "--ensemble-size" }, description = "Ledger ensemble size") public int ensembleSize = 1; - @Parameter(names = { "-w", "--write-quorum" }, description = "Ledger write quorum") + @Option(names = { "-w", "--write-quorum" }, description = "Ledger write quorum") public int writeQuorum = 1; - @Parameter(names = { "-a", "--ack-quorum" }, description = "Ledger ack quorum") + @Option(names = { "-a", "--ack-quorum" }, description = "Ledger ack quorum") public int ackQuorum = 1; - @Parameter(names = { "-dt", "--digest-type" }, description = "BookKeeper digest type") + @Option(names = { "-dt", "--digest-type" }, description = "BookKeeper digest type") public DigestType digestType = DigestType.CRC32C; - @Parameter(names = { "-time", + @Option(names = { "-time", "--test-duration" }, description = "Test duration in secs. If <= 0, it will keep publishing") public long testTime = 0; @@ -141,25 +142,25 @@ public class ManagedLedgerWriter { public static void main(String[] args) throws Exception { final Arguments arguments = new Arguments(); - JCommander jc = new JCommander(arguments); - jc.setProgramName("pulsar-perf managed-ledger"); + CommandLine commander = new CommandLine(arguments); + commander.setCommandName("pulsar-perf managed-ledger"); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { System.out.println(e.getMessage()); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } if (arguments.help) { - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } if (arguments.metadataStoreUrl == null && arguments.zookeeperServers == null) { System.err.println("Metadata store address argument is required (--metadata-store)"); - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceBaseArguments.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceBaseArguments.java index bc4ab003c46..d320cafc1a0 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceBaseArguments.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceBaseArguments.java @@ -20,16 +20,16 @@ package org.apache.pulsar.testclient; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.pulsar.testclient.PerfClientUtils.exit; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; import java.io.File; import java.io.FileInputStream; import java.util.Properties; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; import org.apache.pulsar.client.api.ProxyProtocol; +import picocli.CommandLine; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; /** * PerformanceBaseArguments contains common CLI arguments and parsing logic available to all sub-commands. @@ -37,74 +37,74 @@ import org.apache.pulsar.client.api.ProxyProtocol; */ public abstract class PerformanceBaseArguments { - @Parameter(names = { "-h", "--help" }, description = "Print help message", help = true) + @Option(names = { "-h", "--help" }, description = "Print help message", help = true) boolean help; - @Parameter(names = { "-cf", "--conf-file" }, description = "Pulsar configuration file") + @Option(names = { "-cf", "--conf-file" }, description = "Pulsar configuration file") public String confFile; - @Parameter(names = { "-u", "--service-url" }, description = "Pulsar Service URL") + @Option(names = { "-u", "--service-url" }, description = "Pulsar Service URL") public String serviceURL; - @Parameter(names = { "--auth-plugin" }, description = "Authentication plugin class name") + @Option(names = { "--auth-plugin" }, description = "Authentication plugin class name") public String authPluginClassName; - @Parameter( + @Option( names = { "--auth-params" }, description = "Authentication parameters, whose format is determined by the implementation " + "of method `configure` in authentication plugin class, for example \"key1:val1,key2:val2\" " + "or \"{\"key1\":\"val1\",\"key2\":\"val2\"}\".") public String authParams; - @Parameter(names = { + @Option(names = { "--trust-cert-file" }, description = "Path for the trusted TLS certificate file") public String tlsTrustCertsFilePath = ""; - @Parameter(names = { + @Option(names = { "--tls-allow-insecure" }, description = "Allow insecure TLS connection") public Boolean tlsAllowInsecureConnection = null; - @Parameter(names = { + @Option(names = { "--tls-enable-hostname-verification" }, description = "Enable TLS hostname verification") public Boolean tlsHostnameVerificationEnable = null; - @Parameter(names = { "-c", + @Option(names = { "-c", "--max-connections" }, description = "Max number of TCP connections to a single broker") public int maxConnections = 1; - @Parameter(names = { "-i", + @Option(names = { "-i", "--stats-interval-seconds" }, description = "Statistics Interval Seconds. If 0, statistics will be disabled") public long statsIntervalSeconds = 0; - @Parameter(names = {"-ioThreads", "--num-io-threads"}, description = "Set the number of threads to be " + @Option(names = {"-ioThreads", "--num-io-threads"}, description = "Set the number of threads to be " + "used for handling connections to brokers. The default value is 1.") public int ioThreads = 1; - @Parameter(names = {"-bw", "--busy-wait"}, description = "Enable Busy-Wait on the Pulsar client") + @Option(names = {"-bw", "--busy-wait"}, description = "Enable Busy-Wait on the Pulsar client") public boolean enableBusyWait = false; - @Parameter(names = { "--listener-name" }, description = "Listener name for the broker.") + @Option(names = { "--listener-name" }, description = "Listener name for the broker.") public String listenerName = null; - @Parameter(names = {"-lt", "--num-listener-threads"}, description = "Set the number of threads" + @Option(names = {"-lt", "--num-listener-threads"}, description = "Set the number of threads" + " to be used for message listeners") public int listenerThreads = 1; - @Parameter(names = {"-mlr", "--max-lookup-request"}, description = "Maximum number of lookup requests allowed " + @Option(names = {"-mlr", "--max-lookup-request"}, description = "Maximum number of lookup requests allowed " + "on each broker connection to prevent overloading a broker") public int maxLookupRequest = 50000; - @Parameter(names = { "--proxy-url" }, description = "Proxy-server URL to which to connect.") + @Option(names = { "--proxy-url" }, description = "Proxy-server URL to which to connect.") String proxyServiceURL = null; - @Parameter(names = { "--proxy-protocol" }, description = "Proxy protocol to select type of routing at proxy.") + @Option(names = { "--proxy-protocol" }, description = "Proxy protocol to select type of routing at proxy.") ProxyProtocol proxyProtocol = null; - @Parameter(names = { "--auth_plugin" }, description = "Authentication plugin class name", hidden = true) + @Option(names = { "--auth_plugin" }, description = "Authentication plugin class name", hidden = true) public String deprecatedAuthPluginClassName; - @Parameter(names = { "-ml", "--memory-limit", }, description = "Configure the Pulsar client memory limit " + @Option(names = { "-ml", "--memory-limit", }, description = "Configure the Pulsar client memory limit " + "(eg: 32M, 64M)", converter = ByteUnitToLongConverter.class) public long memoryLimit; @@ -203,19 +203,18 @@ public abstract class PerformanceBaseArguments { * @throws ParameterException If there is a problem parsing the arguments */ public void parseCLI(String cmdName, String[] args) { - JCommander jc = new JCommander(this); - jc.setProgramName(cmdName); - + CommandLine commander = new CommandLine(this); + commander.setCommandName(cmdName); try { - jc.parse(args); + commander.parseArgs(args); } catch (ParameterException e) { - System.out.println("error: " + e.getMessage()); - jc.usage(); + System.out.println(e.getMessage()); + commander.usage(commander.getOut()); PerfClientUtils.exit(1); } if (help) { - jc.usage(); + commander.usage(commander.getOut()); PerfClientUtils.exit(0); } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceConsumer.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceConsumer.java index 7863bc49a15..7a2bc4382fd 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceConsumer.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceConsumer.java @@ -19,8 +19,6 @@ package org.apache.pulsar.testclient; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.util.concurrent.RateLimiter; @@ -57,6 +55,9 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; public class PerformanceConsumer { private static final LongAdder messagesReceived = new LongAdder(); @@ -82,105 +83,107 @@ public class PerformanceConsumer { private static final Recorder recorder = new Recorder(MAX_LATENCY, 5); private static final Recorder cumulativeRecorder = new Recorder(MAX_LATENCY, 5); - @Parameters(commandDescription = "Test pulsar consumer performance.") + @Command(description = "Test pulsar consumer performance.", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments extends PerformanceTopicListArguments { - @Parameter(names = { "-n", "--num-consumers" }, description = "Number of consumers (per subscription), only " + @Option(names = { "-n", "--num-consumers" }, description = "Number of consumers (per subscription), only " + "one consumer is allowed when subscriptionType is Exclusive", - validateWith = PositiveNumberParameterValidator.class) + converter = PositiveNumberParameterConvert.class + ) public int numConsumers = 1; - @Parameter(names = { "-ns", "--num-subscriptions" }, description = "Number of subscriptions (per topic)", - validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "-ns", "--num-subscriptions" }, description = "Number of subscriptions (per topic)", + converter = PositiveNumberParameterConvert.class + ) public int numSubscriptions = 1; - @Parameter(names = { "-s", "--subscriber-name" }, description = "Subscriber name prefix", hidden = true) + @Option(names = { "-s", "--subscriber-name" }, description = "Subscriber name prefix", hidden = true) public String subscriberName; - @Parameter(names = { "-ss", "--subscriptions" }, + @Option(names = { "-ss", "--subscriptions" }, description = "A list of subscriptions to consume (for example, sub1,sub2)") public List<String> subscriptions = Collections.singletonList("sub"); - @Parameter(names = { "-st", "--subscription-type" }, description = "Subscription type") + @Option(names = { "-st", "--subscription-type" }, description = "Subscription type") public SubscriptionType subscriptionType = SubscriptionType.Exclusive; - @Parameter(names = { "-sp", "--subscription-position" }, description = "Subscription position") + @Option(names = { "-sp", "--subscription-position" }, description = "Subscription position") private SubscriptionInitialPosition subscriptionInitialPosition = SubscriptionInitialPosition.Latest; - @Parameter(names = { "-r", "--rate" }, description = "Simulate a slow message consumer (rate in msg/s)") + @Option(names = { "-r", "--rate" }, description = "Simulate a slow message consumer (rate in msg/s)") public double rate = 0; - @Parameter(names = { "-q", "--receiver-queue-size" }, description = "Size of the receiver queue") + @Option(names = { "-q", "--receiver-queue-size" }, description = "Size of the receiver queue") public int receiverQueueSize = 1000; - @Parameter(names = { "-p", "--receiver-queue-size-across-partitions" }, + @Option(names = { "-p", "--receiver-queue-size-across-partitions" }, description = "Max total size of the receiver queue across partitions") public int maxTotalReceiverQueueSizeAcrossPartitions = 50000; - @Parameter(names = {"-aq", "--auto-scaled-receiver-queue-size"}, + @Option(names = {"-aq", "--auto-scaled-receiver-queue-size"}, description = "Enable autoScaledReceiverQueueSize") public boolean autoScaledReceiverQueueSize = false; - @Parameter(names = {"-rs", "--replicated" }, + @Option(names = {"-rs", "--replicated" }, description = "Whether the subscription status should be replicated") public boolean replicatedSubscription = false; - @Parameter(names = { "--acks-delay-millis" }, description = "Acknowledgements grouping delay in millis") + @Option(names = { "--acks-delay-millis" }, description = "Acknowledgements grouping delay in millis") public int acknowledgmentsGroupingDelayMillis = 100; - @Parameter(names = {"-m", + @Option(names = {"-m", "--num-messages"}, description = "Number of messages to consume in total. If <= 0, it will keep consuming") public long numMessages = 0; - @Parameter(names = { "-mc", "--max_chunked_msg" }, description = "Max pending chunk messages") + @Option(names = { "-mc", "--max_chunked_msg" }, description = "Max pending chunk messages") private int maxPendingChunkedMessage = 0; - @Parameter(names = { "-ac", + @Option(names = { "-ac", "--auto_ack_chunk_q_full" }, description = "Auto ack for oldest message on queue is full") private boolean autoAckOldestChunkedMessageOnQueueFull = false; - @Parameter(names = { "-e", + @Option(names = { "-e", "--expire_time_incomplete_chunked_messages" }, description = "Expire time in ms for incomplete chunk messages") private long expireTimeOfIncompleteChunkedMessageMs = 0; - @Parameter(names = { "-v", + @Option(names = { "-v", "--encryption-key-value-file" }, description = "The file which contains the private key to decrypt payload") public String encKeyFile = null; - @Parameter(names = { "-time", + @Option(names = { "-time", "--test-duration" }, description = "Test duration in secs. If <= 0, it will keep consuming") public long testTime = 0; - @Parameter(names = {"--batch-index-ack" }, description = "Enable or disable the batch index acknowledgment") + @Option(names = {"--batch-index-ack" }, description = "Enable or disable the batch index acknowledgment") public boolean batchIndexAck = false; - @Parameter(names = { "-pm", "--pool-messages" }, description = "Use the pooled message", arity = 1) + @Option(names = { "-pm", "--pool-messages" }, description = "Use the pooled message", arity = "1") private boolean poolMessages = true; - @Parameter(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + @Option(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + " and the time unit is second. (After --txn-enable setting to true, --txn-timeout takes effect)") public long transactionTimeout = 10; - @Parameter(names = {"-nmt", "--numMessage-perTransaction"}, + @Option(names = {"-nmt", "--numMessage-perTransaction"}, description = "The number of messages acknowledged by a transaction. " + "(After --txn-enable setting to true, -numMessage-perTransaction takes effect") public int numMessagesPerTransaction = 50; - @Parameter(names = {"-txn", "--txn-enable"}, description = "Enable or disable the transaction") + @Option(names = {"-txn", "--txn-enable"}, description = "Enable or disable the transaction") public boolean isEnableTransaction = false; - @Parameter(names = {"-ntxn"}, description = "The number of opened transactions, 0 means keeping open." + @Option(names = {"-ntxn"}, description = "The number of opened transactions, 0 means keeping open." + "(After --txn-enable setting to true, -ntxn takes effect.)") public long totalNumTxn = 0; - @Parameter(names = {"-abort"}, description = "Abort the transaction. (After --txn-enable " + @Option(names = {"-abort"}, description = "Abort the transaction. (After --txn-enable " + "setting to true, -abort takes effect)") public boolean isAbortTransaction = false; - @Parameter(names = { "--histogram-file" }, description = "HdrHistogram output file") + @Option(names = { "--histogram-file" }, description = "HdrHistogram output file") public String histogramFile = null; @Override diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceProducer.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceProducer.java index ac34bbd9f7a..0eb8d02f31e 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceProducer.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceProducer.java @@ -25,9 +25,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.pulsar.client.impl.conf.ProducerConfigurationData.DEFAULT_BATCHING_MAX_MESSAGES; import static org.apache.pulsar.client.impl.conf.ProducerConfigurationData.DEFAULT_MAX_PENDING_MESSAGES; import static org.apache.pulsar.client.impl.conf.ProducerConfigurationData.DEFAULT_MAX_PENDING_MESSAGES_ACROSS_PARTITIONS; -import com.beust.jcommander.IStringConverter; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.collect.Range; @@ -75,6 +72,11 @@ import org.apache.pulsar.common.partition.PartitionedTopicMetadata; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; +import picocli.CommandLine.TypeConversionException; /** * A client program to test pulsar producer performance. @@ -103,151 +105,153 @@ public class PerformanceProducer { private static IMessageFormatter messageFormatter = null; - @Parameters(commandDescription = "Test pulsar producer performance.") + @Command(description = "Test pulsar producer performance.", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments extends PerformanceTopicListArguments { - @Parameter(names = { "-threads", "--num-test-threads" }, description = "Number of test threads", - validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "-threads", "--num-test-threads" }, description = "Number of test threads", + converter = PositiveNumberParameterConvert.class + ) public int numTestThreads = 1; - @Parameter(names = { "-r", "--rate" }, description = "Publish rate msg/s across topics") + @Option(names = { "-r", "--rate" }, description = "Publish rate msg/s across topics") public int msgRate = 100; - @Parameter(names = { "-s", "--size" }, description = "Message size (bytes)") + @Option(names = { "-s", "--size" }, description = "Message size (bytes)") public int msgSize = 1024; - @Parameter(names = { "-n", "--num-producers" }, description = "Number of producers (per topic)", - validateWith = PositiveNumberParameterValidator.class) + @Option(names = { "-n", "--num-producers" }, description = "Number of producers (per topic)", + converter = PositiveNumberParameterConvert.class + ) public int numProducers = 1; - @Parameter(names = {"--separator"}, description = "Separator between the topic and topic number") + @Option(names = {"--separator"}, description = "Separator between the topic and topic number") public String separator = "-"; - @Parameter(names = {"--send-timeout"}, description = "Set the sendTimeout value default 0 to keep " + @Option(names = {"--send-timeout"}, description = "Set the sendTimeout value default 0 to keep " + "compatibility with previous version of pulsar-perf") public int sendTimeout = 0; - @Parameter(names = { "-pn", "--producer-name" }, description = "Producer Name") + @Option(names = { "-pn", "--producer-name" }, description = "Producer Name") public String producerName = null; - @Parameter(names = { "-au", "--admin-url" }, description = "Pulsar Admin URL") + @Option(names = { "-au", "--admin-url" }, description = "Pulsar Admin URL") public String adminURL; - @Parameter(names = { "-ch", + @Option(names = { "-ch", "--chunking" }, description = "Should split the message and publish in chunks if message size is " + "larger than allowed max size") private boolean chunkingAllowed = false; - @Parameter(names = { "-o", "--max-outstanding" }, description = "Max number of outstanding messages") + @Option(names = { "-o", "--max-outstanding" }, description = "Max number of outstanding messages") public int maxOutstanding = DEFAULT_MAX_PENDING_MESSAGES; - @Parameter(names = { "-p", "--max-outstanding-across-partitions" }, description = "Max number of outstanding " + @Option(names = { "-p", "--max-outstanding-across-partitions" }, description = "Max number of outstanding " + "messages across partitions") public int maxPendingMessagesAcrossPartitions = DEFAULT_MAX_PENDING_MESSAGES_ACROSS_PARTITIONS; - @Parameter(names = { "-np", "--partitions" }, description = "Create partitioned topics with the given number " + @Option(names = { "-np", "--partitions" }, description = "Create partitioned topics with the given number " + "of partitions, set 0 to not try to create the topic") public Integer partitions = null; - @Parameter(names = { "-m", + @Option(names = { "-m", "--num-messages" }, description = "Number of messages to publish in total. If <= 0, it will keep " + "publishing") public long numMessages = 0; - @Parameter(names = { "-z", "--compression" }, description = "Compress messages payload") + @Option(names = { "-z", "--compression" }, description = "Compress messages payload") public CompressionType compression = CompressionType.NONE; - @Parameter(names = { "-f", "--payload-file" }, description = "Use payload from an UTF-8 encoded text file and " + @Option(names = { "-f", "--payload-file" }, description = "Use payload from an UTF-8 encoded text file and " + "a payload will be randomly selected when publishing messages") public String payloadFilename = null; - @Parameter(names = { "-e", "--payload-delimiter" }, description = "The delimiter used to split lines when " + @Option(names = { "-e", "--payload-delimiter" }, description = "The delimiter used to split lines when " + "using payload from a file") // here escaping \n since default value will be printed with the help text public String payloadDelimiter = "\\n"; - @Parameter(names = { "-b", + @Option(names = { "-b", "--batch-time-window" }, description = "Batch messages in 'x' ms window (Default: 1ms)") public double batchTimeMillis = 1.0; - @Parameter(names = { "-db", + @Option(names = { "-db", "--disable-batching" }, description = "Disable batching if true") public boolean disableBatching; - @Parameter(names = { + @Option(names = { "-bm", "--batch-max-messages" }, description = "Maximum number of messages per batch") public int batchMaxMessages = DEFAULT_BATCHING_MAX_MESSAGES; - @Parameter(names = { + @Option(names = { "-bb", "--batch-max-bytes" }, description = "Maximum number of bytes per batch") public int batchMaxBytes = 4 * 1024 * 1024; - @Parameter(names = { "-time", + @Option(names = { "-time", "--test-duration" }, description = "Test duration in secs. If <= 0, it will keep publishing") public long testTime = 0; - @Parameter(names = "--warmup-time", description = "Warm-up time in seconds (Default: 1 sec)") + @Option(names = "--warmup-time", description = "Warm-up time in seconds (Default: 1 sec)") public double warmupTimeSeconds = 1.0; - @Parameter(names = { "-k", "--encryption-key-name" }, description = "The public key name to encrypt payload") + @Option(names = { "-k", "--encryption-key-name" }, description = "The public key name to encrypt payload") public String encKeyName = null; - @Parameter(names = { "-v", + @Option(names = { "-v", "--encryption-key-value-file" }, description = "The file which contains the public key to encrypt payload") public String encKeyFile = null; - @Parameter(names = { "-d", + @Option(names = { "-d", "--delay" }, description = "Mark messages with a given delay in seconds") public long delay = 0; - @Parameter(names = { "-dr", "--delay-range"}, description = "Mark messages with a given delay by a random" + @Option(names = { "-dr", "--delay-range"}, description = "Mark messages with a given delay by a random" + " number of seconds. this value between the specified origin (inclusive) and the specified bound" + " (exclusive). e.g. 1,300", converter = RangeConvert.class) public Range<Long> delayRange = null; - @Parameter(names = { "-set", + @Option(names = { "-set", "--set-event-time" }, description = "Set the eventTime on messages") public boolean setEventTime = false; - @Parameter(names = { "-ef", + @Option(names = { "-ef", "--exit-on-failure" }, description = "Exit from the process on publish failure (default: disable)") public boolean exitOnFailure = false; - @Parameter(names = {"-mk", "--message-key-generation-mode"}, description = "The generation mode of message key" + @Option(names = {"-mk", "--message-key-generation-mode"}, description = "The generation mode of message key" + ", valid options are: [autoIncrement, random]") public String messageKeyGenerationMode = null; - @Parameter(names = { "-am", "--access-mode" }, description = "Producer access mode") + @Option(names = { "-am", "--access-mode" }, description = "Producer access mode") public ProducerAccessMode producerAccessMode = ProducerAccessMode.Shared; - @Parameter(names = { "-fp", "--format-payload" }, - description = "Format %i as a message index in the stream from producer and/or %t as the timestamp" + @Option(names = { "-fp", "--format-payload" }, + description = "Format %%i as a message index in the stream from producer and/or %%t as the timestamp" + " nanoseconds.") public boolean formatPayload = false; - @Parameter(names = {"-fc", "--format-class"}, description = "Custom Formatter class name") + @Option(names = {"-fc", "--format-class"}, description = "Custom Formatter class name") public String formatterClass = "org.apache.pulsar.testclient.DefaultMessageFormatter"; - @Parameter(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + @Option(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + " and the time unit is second. (After --txn-enable setting to true, --txn-timeout takes effect)") public long transactionTimeout = 10; - @Parameter(names = {"-nmt", "--numMessage-perTransaction"}, + @Option(names = {"-nmt", "--numMessage-perTransaction"}, description = "The number of messages sent by a transaction. " + "(After --txn-enable setting to true, -nmt takes effect)") public int numMessagesPerTransaction = 50; - @Parameter(names = {"-txn", "--txn-enable"}, description = "Enable or disable the transaction") + @Option(names = {"-txn", "--txn-enable"}, description = "Enable or disable the transaction") public boolean isEnableTransaction = false; - @Parameter(names = {"-abort"}, description = "Abort the transaction. (After --txn-enable " + @Option(names = {"-abort"}, description = "Abort the transaction. (After --txn-enable " + "setting to true, -abort takes effect)") public boolean isAbortTransaction = false; - @Parameter(names = { "--histogram-file" }, description = "HdrHistogram output file") + @Option(names = { "--histogram-file" }, description = "HdrHistogram output file") public String histogramFile = null; @Override @@ -794,7 +798,7 @@ public class PerformanceProducer { autoIncrement, random } - static class RangeConvert implements IStringConverter<Range<Long>> { + static class RangeConvert implements ITypeConverter<Range<Long>> { @Override public Range<Long> convert(String rangeStr) { try { @@ -804,7 +808,7 @@ public class PerformanceProducer { final long max = Long.parseLong(facts[1].trim()); return Range.closedOpen(min, max); } catch (Throwable ex) { - throw new IllegalArgumentException("Unknown delay range interval," + throw new TypeConversionException("Unknown delay range interval," + " the format should be \"<origin>,<bound>\". error message: " + rangeStr); } } diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceReader.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceReader.java index ed5cc37644a..3572cbde43c 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceReader.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceReader.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.testclient; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.util.concurrent.RateLimiter; @@ -46,6 +44,9 @@ import org.apache.pulsar.common.util.FutureUtil; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; public class PerformanceReader { private static final LongAdder messagesReceived = new LongAdder(); @@ -59,30 +60,30 @@ public class PerformanceReader { private static Recorder recorder = new Recorder(TimeUnit.DAYS.toMillis(10), 5); private static Recorder cumulativeRecorder = new Recorder(TimeUnit.DAYS.toMillis(10), 5); - @Parameters(commandDescription = "Test pulsar reader performance.") + @Command(description = "Test pulsar reader performance.", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments extends PerformanceTopicListArguments { - @Parameter(names = { "-r", "--rate" }, description = "Simulate a slow message reader (rate in msg/s)") + @Option(names = { "-r", "--rate" }, description = "Simulate a slow message reader (rate in msg/s)") public double rate = 0; - @Parameter(names = { "-m", + @Option(names = { "-m", "--start-message-id" }, description = "Start message id. This can be either 'earliest', " + "'latest' or a specific message id by using 'lid:eid'") public String startMessageId = "earliest"; - @Parameter(names = { "-q", "--receiver-queue-size" }, description = "Size of the receiver queue") + @Option(names = { "-q", "--receiver-queue-size" }, description = "Size of the receiver queue") public int receiverQueueSize = 1000; - @Parameter(names = {"-n", + @Option(names = {"-n", "--num-messages"}, description = "Number of messages to consume in total. If <= 0, " + "it will keep consuming") public long numMessages = 0; - @Parameter(names = { + @Option(names = { "--use-tls" }, description = "Use TLS encryption on the connection") public boolean useTls; - @Parameter(names = { "-time", + @Option(names = { "-time", "--test-duration" }, description = "Test duration in secs. If <= 0, it will keep consuming") public long testTime = 0; diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTopicListArguments.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTopicListArguments.java index a2f8b6af082..9ac99d0abcc 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTopicListArguments.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTopicListArguments.java @@ -18,10 +18,11 @@ */ package org.apache.pulsar.testclient; -import com.beust.jcommander.Parameter; import java.util.ArrayList; import java.util.List; import org.apache.pulsar.common.naming.TopicName; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * PerformanceTopicListArguments provides common topic list arguments which are used @@ -29,12 +30,13 @@ import org.apache.pulsar.common.naming.TopicName; */ public abstract class PerformanceTopicListArguments extends PerformanceBaseArguments { - @Parameter(description = "persistent://prop/ns/my-topic", required = true) + @Parameters(description = "persistent://prop/ns/my-topic", arity = "1") public List<String> topics; - @Parameter(names = { "-t", "--num-topics", "--num-topic" }, description = "Number of topics. Must match" + @Option(names = { "-t", "--num-topics", "--num-topic" }, description = "Number of topics. Must match" + "the given number of topic arguments.", - validateWith = PositiveNumberParameterValidator.class) + converter = PositiveNumberParameterConvert.class + ) public int numTopics = 1; @Override diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTransaction.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTransaction.java index 0bfa216c459..02e50ab4e2b 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTransaction.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PerformanceTransaction.java @@ -19,8 +19,6 @@ package org.apache.pulsar.testclient; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.util.concurrent.RateLimiter; @@ -65,6 +63,9 @@ import org.apache.pulsar.common.partition.PartitionedTopicMetadata; import org.apache.pulsar.testclient.utils.PaddingDecimalFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; public class PerformanceTransaction { @@ -89,84 +90,84 @@ public class PerformanceTransaction { private static final Recorder messageSendRCumulativeRecorder = new Recorder(TimeUnit.SECONDS.toMicros(120000), 5); - @Parameters(commandDescription = "Test pulsar transaction performance.") + @Command(description = "Test pulsar transaction performance.", showDefaultValues = true, scope = ScopeType.INHERIT) static class Arguments extends PerformanceBaseArguments { - @Parameter(names = "--topics-c", description = "All topics that need ack for a transaction", required = + @Option(names = "--topics-c", description = "All topics that need ack for a transaction", required = true) public List<String> consumerTopic = Collections.singletonList("test-consume"); - @Parameter(names = "--topics-p", description = "All topics that need produce for a transaction", + @Option(names = "--topics-p", description = "All topics that need produce for a transaction", required = true) public List<String> producerTopic = Collections.singletonList("test-produce"); - @Parameter(names = {"-threads", "--num-test-threads"}, description = "Number of test threads." + @Option(names = {"-threads", "--num-test-threads"}, description = "Number of test threads." + "This thread is for a new transaction to ack messages from consumer topics and produce message to " + "producer topics, and then commit or abort this transaction. " + "Increasing the number of threads increases the parallelism of the performance test, " + "thereby increasing the intensity of the stress test.") public int numTestThreads = 1; - @Parameter(names = {"-au", "--admin-url"}, description = "Pulsar Admin URL") + @Option(names = {"-au", "--admin-url"}, description = "Pulsar Admin URL") public String adminURL; - @Parameter(names = {"-np", + @Option(names = {"-np", "--partitions"}, description = "Create partitioned topics with a given number of partitions, 0 means" + "not trying to create a topic") public Integer partitions = null; - @Parameter(names = {"-time", + @Option(names = {"-time", "--test-duration"}, description = "Test duration (in second). 0 means keeping publishing") public long testTime = 0; - @Parameter(names = {"-ss", + @Option(names = {"-ss", "--subscriptions"}, description = "A list of subscriptions to consume (for example, sub1,sub2)") public List<String> subscriptions = Collections.singletonList("sub"); - @Parameter(names = {"-ns", "--num-subscriptions"}, description = "Number of subscriptions (per topic)") + @Option(names = {"-ns", "--num-subscriptions"}, description = "Number of subscriptions (per topic)") public int numSubscriptions = 1; - @Parameter(names = {"-sp", "--subscription-position"}, description = "Subscription position") + @Option(names = {"-sp", "--subscription-position"}, description = "Subscription position") private SubscriptionInitialPosition subscriptionInitialPosition = SubscriptionInitialPosition.Earliest; - @Parameter(names = {"-st", "--subscription-type"}, description = "Subscription type") + @Option(names = {"-st", "--subscription-type"}, description = "Subscription type") public SubscriptionType subscriptionType = SubscriptionType.Shared; - @Parameter(names = {"-rs", "--replicated" }, + @Option(names = {"-rs", "--replicated" }, description = "Whether the subscription status should be replicated") private boolean replicatedSubscription = false; - @Parameter(names = {"-q", "--receiver-queue-size"}, description = "Size of the receiver queue") + @Option(names = {"-q", "--receiver-queue-size"}, description = "Size of the receiver queue") public int receiverQueueSize = 1000; - @Parameter(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + @Option(names = {"-tto", "--txn-timeout"}, description = "Set the time value of transaction timeout," + " and the time unit is second. (After --txn-enable setting to true, --txn-timeout takes effect)") public long transactionTimeout = 5; - @Parameter(names = {"-ntxn", + @Option(names = {"-ntxn", "--number-txn"}, description = "Set the number of transaction. 0 means keeping open." + "If transaction disabled, it means the number of tasks. The task or transaction produces or " + "consumes a specified number of messages.") public long numTransactions = 0; - @Parameter(names = {"-nmp", "--numMessage-perTransaction-produce"}, + @Option(names = {"-nmp", "--numMessage-perTransaction-produce"}, description = "Set the number of messages produced in a transaction." + "If transaction disabled, it means the number of messages produced in a task.") public int numMessagesProducedPerTransaction = 1; - @Parameter(names = {"-nmc", "--numMessage-perTransaction-consume"}, + @Option(names = {"-nmc", "--numMessage-perTransaction-consume"}, description = "Set the number of messages consumed in a transaction." + "If transaction disabled, it means the number of messages consumed in a task.") public int numMessagesReceivedPerTransaction = 1; - @Parameter(names = {"--txn-disable"}, description = "Disable transaction") + @Option(names = {"--txn-disable"}, description = "Disable transaction") public boolean isDisableTransaction = false; - @Parameter(names = {"-abort"}, description = "Abort the transaction. (After --txn-disEnable " + @Option(names = {"-abort"}, description = "Abort the transaction. (After --txn-disEnable " + "setting to false, -abort takes effect)") public boolean isAbortTransaction = false; - @Parameter(names = "-txnRate", description = "Set the rate of opened transaction or task. 0 means no limit") + @Option(names = "-txnRate", description = "Set the rate of opened transaction or task. 0 means no limit") public int openTxnRate = 0; @Override diff --git a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterValidator.java b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterConvert.java similarity index 68% rename from pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterValidator.java rename to pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterConvert.java index 7e8fe2181cd..fc045eb8aaf 100644 --- a/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterValidator.java +++ b/pulsar-testclient/src/main/java/org/apache/pulsar/testclient/PositiveNumberParameterConvert.java @@ -18,15 +18,16 @@ */ package org.apache.pulsar.testclient; -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.ParameterException; - -public class PositiveNumberParameterValidator implements IParameterValidator { +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; +public class PositiveNumberParameterConvert implements ITypeConverter<Integer> { @Override - public void validate(String name, String value) throws ParameterException { - if (Integer.parseInt(value) <= 0) { - throw new ParameterException("Parameter " + name + " should be > 0 (found " + value + ")"); + public Integer convert(String value) { + int result = Integer.parseInt(value); + if (result <= 0) { + throw new TypeConversionException("Parameter should be > 0 (found " + value + ")"); } + return result; } } diff --git a/pulsar-testclient/src/test/java/org/apache/pulsar/testclient/GenerateDocumentionTest.java b/pulsar-testclient/src/test/java/org/apache/pulsar/testclient/GenerateDocumentionTest.java index 936275bcd41..73d7751e333 100644 --- a/pulsar-testclient/src/test/java/org/apache/pulsar/testclient/GenerateDocumentionTest.java +++ b/pulsar-testclient/src/test/java/org/apache/pulsar/testclient/GenerateDocumentionTest.java @@ -18,7 +18,9 @@ */ package org.apache.pulsar.testclient; +import org.testng.Assert; import org.testng.annotations.Test; +import picocli.CommandLine; public class GenerateDocumentionTest { @@ -32,4 +34,39 @@ public class GenerateDocumentionTest { String[] args = new String[]{"-n", "produce", "-n", "consume"}; CmdGenerateDocumentation.main(args); } + + private static final String DESC = "desc"; + @Test + public void testGetCommandOptionDescription(){ + Arguments arguments = new Arguments(); + CommandLine commander = new CommandLine(arguments); + String desc = CmdGenerateDocumentation.getCommandDescription(commander); + Assert.assertEquals(desc, DESC); + + commander.getCommandSpec().options().forEach(option -> { + String desc1 = CmdGenerateDocumentation.getOptionDescription(option); + Assert.assertEquals(desc1, DESC); + }); + + ArgumentsWithoutDesc argumentsWithoutDesc = new ArgumentsWithoutDesc(); + commander = new CommandLine(argumentsWithoutDesc); + desc = CmdGenerateDocumentation.getCommandDescription(commander); + Assert.assertEquals(desc, ""); + + commander.getCommandSpec().options().forEach(option -> { + String desc1 = CmdGenerateDocumentation.getOptionDescription(option); + Assert.assertEquals(desc1, ""); + }); + } + + @CommandLine.Command(description = DESC) + static class Arguments { + @CommandLine.Option(names = {"-h", "--help"}, description = DESC, help = true) + boolean help; + } + @CommandLine.Command() + static class ArgumentsWithoutDesc { + @CommandLine.Option(names = {"-h", "--help"}, help = true) + boolean help; + } } \ No newline at end of file