diff --git a/distribution/server/src/assemble/LICENSE.bin.txt b/distribution/server/src/assemble/LICENSE.bin.txt index b96786f234bed..545292f2ba85a 100644 --- a/distribution/server/src/assemble/LICENSE.bin.txt +++ b/distribution/server/src/assemble/LICENSE.bin.txt @@ -244,6 +244,9 @@ This projects includes binary packages with the following licenses: The Apache Software License, Version 2.0 * JCommander -- com.beust-jcommander-1.82.jar + * Picocli + - info.picocli-picocli-4.7.5.jar + - info.picocli-picocli-shell-jline3-4.7.5.jar * High Performance Primitive Collections for Java -- com.carrotsearch-hppc-0.9.1.jar * Jackson - com.fasterxml.jackson.core-jackson-annotations-2.14.2.jar diff --git a/distribution/shell/src/assemble/LICENSE.bin.txt b/distribution/shell/src/assemble/LICENSE.bin.txt index 8ddcbcfb1600d..a9bbd541448fc 100644 --- a/distribution/shell/src/assemble/LICENSE.bin.txt +++ b/distribution/shell/src/assemble/LICENSE.bin.txt @@ -310,6 +310,9 @@ This projects includes binary packages with the following licenses: The Apache Software License, Version 2.0 * JCommander -- jcommander-1.82.jar + * Picocli + - picocli-4.7.5.jar + - picocli-shell-jline3-4.7.5.jar * Jackson - jackson-annotations-2.14.2.jar - jackson-core-2.14.2.jar diff --git a/distribution/shell/src/assemble/NOTICE.bin.txt b/distribution/shell/src/assemble/NOTICE.bin.txt index 7705416042a17..41c9bd7d217da 100644 --- a/distribution/shell/src/assemble/NOTICE.bin.txt +++ b/distribution/shell/src/assemble/NOTICE.bin.txt @@ -17,6 +17,9 @@ Copyright (c) 2005 Brian Goetz and Tim Peierls JCommander Copyright 2010 Cedric Beust cedric@beust.com +picocli (http://picocli.info) +Copyright 2017 Remko Popma + EA Agent Loader Copyright (C) 2015 Electronic Arts Inc. All rights reserved. diff --git a/pom.xml b/pom.xml index f161165b7b924..359ac8963cc8e 100644 --- a/pom.xml +++ b/pom.xml @@ -259,6 +259,7 @@ flexible messaging model and an intuitive client API. 1.34.1-alpha 1.32.1-alpha 1.23.1-alpha + 4.7.5 1.18.3 @@ -691,6 +692,18 @@ flexible messaging model and an intuitive client API. ${jcommander.version} + + info.picocli + picocli + ${picocli.version} + + + + info.picocli + picocli-shell-jline3 + ${picocli.version} + + com.google.guava guava diff --git a/pulsar-cli-utils/pom.xml b/pulsar-cli-utils/pom.xml index 896f464ccd5d6..ac442b4004e8b 100644 --- a/pulsar-cli-utils/pom.xml +++ b/pulsar-cli-utils/pom.xml @@ -40,7 +40,11 @@ jcommander compile - + + info.picocli + picocli + compile + org.apache.commons commons-lang3 diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/ValueValidationUtil.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/ValueValidationUtil.java index c2000e1c7bc5a..751f69b20c4dd 100644 --- a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/ValueValidationUtil.java +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/ValueValidationUtil.java @@ -18,7 +18,6 @@ */ package org.apache.pulsar.cli; -import com.beust.jcommander.ParameterException; import lombok.experimental.UtilityClass; import org.apache.commons.lang3.StringUtils; @@ -27,31 +26,31 @@ public class ValueValidationUtil { public static void maxValueCheck(String paramName, long value, long maxValue) { if (value > maxValue) { - throw new ParameterException(paramName + " cannot be bigger than <" + maxValue + ">!"); + throw new IllegalArgumentException(paramName + " cannot be bigger than <" + maxValue + ">!"); } } public static void positiveCheck(String paramName, long value) { if (value <= 0) { - throw new ParameterException(paramName + " cannot be less than or equal to <0>!"); + throw new IllegalArgumentException(paramName + " cannot be less than or equal to <0>!"); } } public static void positiveCheck(String paramName, int value) { if (value <= 0) { - throw new ParameterException(paramName + " cannot be less than or equal to <0>!"); + throw new IllegalArgumentException(paramName + " cannot be less than or equal to <0>!"); } } public static void emptyCheck(String paramName, String value) { if (StringUtils.isEmpty(value)) { - throw new ParameterException("The value of " + paramName + " can't be empty"); + throw new IllegalArgumentException("The value of " + paramName + " can't be empty"); } } public static void minValueCheck(String name, Long value, long min) { if (value < min) { - throw new ParameterException(name + " cannot be less than <" + min + ">!"); + throw new IllegalArgumentException(name + " cannot be less than <" + min + ">!"); } } } diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitUtil.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitUtil.java index cc6140dfced46..8b5a0aafcdb6a 100644 --- a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitUtil.java +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitUtil.java @@ -18,7 +18,6 @@ */ package org.apache.pulsar.cli.converters; -import com.beust.jcommander.ParameterException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -26,12 +25,12 @@ import lombok.experimental.UtilityClass; @UtilityClass -class ByteUnitUtil { +public class ByteUnitUtil { private static Set sizeUnit = Collections.unmodifiableSet( new HashSet<>(Arrays.asList('k', 'K', 'm', 'M', 'g', 'G', 't', 'T'))); - static long validateSizeString(String byteStr) { + public static long validateSizeString(String byteStr) { if (byteStr.isEmpty()) { throw new IllegalArgumentException("byte string cannot be empty"); } @@ -44,7 +43,7 @@ static long validateSizeString(String byteStr) { ? Long.parseLong(subStr) : Long.parseLong(byteStr); } catch (IllegalArgumentException e) { - throw new ParameterException(String.format("Invalid size '%s'. Valid formats are: %s", + throw new IllegalArgumentException(String.format("Invalid size '%s'. Valid formats are: %s", byteStr, "(4096, 100K, 10M, 16G, 2T)")); } switch (last) { diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitIntegerConverter.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToIntegerConverter.java similarity index 60% rename from pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitIntegerConverter.java rename to pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToIntegerConverter.java index b148d238b149d..2e5a15c9d9c41 100644 --- a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/ByteUnitIntegerConverter.java +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToIntegerConverter.java @@ -16,26 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.cli.converters; +package org.apache.pulsar.cli.converters.picocli; -import static org.apache.pulsar.cli.ValueValidationUtil.emptyCheck; import static org.apache.pulsar.cli.converters.ByteUnitUtil.validateSizeString; -import com.beust.jcommander.converters.BaseConverter; - -public class ByteUnitIntegerConverter extends BaseConverter { - - public ByteUnitIntegerConverter(String optionName) { - super(optionName); - } +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; +public class ByteUnitToIntegerConverter implements ITypeConverter { @Override - public Integer convert(String argStr) { - return parseBytes(argStr).intValue(); - } - - Long parseBytes(String argStr) { - emptyCheck(getOptionName(), argStr); - long valueInBytes = validateSizeString(argStr); - return valueInBytes; + public Integer convert(String value) throws Exception { + try { + long l = validateSizeString(value); + return (int) l; + } catch (Exception e) { + throw new TypeConversionException(e.getMessage()); + } } } diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToLongConverter.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToLongConverter.java new file mode 100644 index 0000000000000..519cf3dc4c32b --- /dev/null +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/ByteUnitToLongConverter.java @@ -0,0 +1,34 @@ +/* + * 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.picocli; + +import static org.apache.pulsar.cli.converters.ByteUnitUtil.validateSizeString; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; + +public class ByteUnitToLongConverter implements ITypeConverter { + @Override + public Long convert(String value) throws Exception { + try { + return validateSizeString(value); + } catch (Exception e) { + throw new TypeConversionException(e.getMessage()); + } + } +} diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/TimeUnitToMillisConverter.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToMillisConverter.java similarity index 54% rename from pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/TimeUnitToMillisConverter.java rename to pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToMillisConverter.java index 38ff4f501a67a..008467a23e6d8 100644 --- a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/TimeUnitToMillisConverter.java +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToMillisConverter.java @@ -16,27 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.cli.converters; +package org.apache.pulsar.cli.converters.picocli; -import static org.apache.pulsar.cli.ValueValidationUtil.emptyCheck; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.converters.BaseConverter; import java.util.concurrent.TimeUnit; +import org.apache.pulsar.cli.converters.RelativeTimeUtil; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; -public class TimeUnitToMillisConverter extends BaseConverter { - - public TimeUnitToMillisConverter(String optionName) { - super(optionName); - } - +public class TimeUnitToMillisConverter implements ITypeConverter { @Override - public Long convert(String str) { - emptyCheck(getOptionName(), str); + public Long convert(String value) throws Exception { try { - return TimeUnit.SECONDS.toMillis( - RelativeTimeUtil.parseRelativeTimeInSeconds(str.trim())); - } catch (IllegalArgumentException exception) { - throw new ParameterException("For input " + getOptionName() + ": " + exception.getMessage()); + return TimeUnit.SECONDS.toMillis(RelativeTimeUtil.parseRelativeTimeInSeconds(value)); + } catch (Exception e) { + throw new TypeConversionException(e.getMessage()); } } } diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToSecondsConverter.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToSecondsConverter.java new file mode 100644 index 0000000000000..231fa19bdd56c --- /dev/null +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/TimeUnitToSecondsConverter.java @@ -0,0 +1,35 @@ +/* + * 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.picocli; + +import java.util.concurrent.TimeUnit; +import org.apache.pulsar.cli.converters.RelativeTimeUtil; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; + +public class TimeUnitToSecondsConverter implements ITypeConverter { + @Override + public Long convert(String value) throws Exception { + try { + return TimeUnit.SECONDS.toSeconds(RelativeTimeUtil.parseRelativeTimeInSeconds(value)); + } catch (Exception e) { + throw new TypeConversionException(e.getMessage()); + } + } +} diff --git a/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/package-info.java b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/package-info.java new file mode 100644 index 0000000000000..bdb3e1e85dd19 --- /dev/null +++ b/pulsar-cli-utils/src/main/java/org/apache/pulsar/cli/converters/picocli/package-info.java @@ -0,0 +1,19 @@ +/* + * 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.picocli; diff --git a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/ValueValidationUtilTest.java b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/ValueValidationUtilTest.java index 9d44ee41a2e25..06db820819970 100644 --- a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/ValueValidationUtilTest.java +++ b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/ValueValidationUtilTest.java @@ -19,14 +19,13 @@ package org.apache.pulsar.cli; import static org.testng.Assert.assertThrows; -import com.beust.jcommander.ParameterException; import org.testng.annotations.Test; public class ValueValidationUtilTest { @Test public void testMaxValueCheck() { - assertThrows(ParameterException.class, () -> ValueValidationUtil.maxValueCheck("param1", 11L, 10L)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.maxValueCheck("param1", 11L, 10L)); ValueValidationUtil.maxValueCheck("param2", 10L, 10L); ValueValidationUtil.maxValueCheck("param3", 9L, 10L); } @@ -34,34 +33,34 @@ public void testMaxValueCheck() { @Test public void testPositiveCheck() { // Long - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param1", 0L)); - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param2", -1L)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param1", 0L)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param2", -1L)); ValueValidationUtil.positiveCheck("param3", 1L); // Integer - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param4", 0)); - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param5", -1)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param4", 0)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param5", -1)); ValueValidationUtil.positiveCheck("param6", 1); } @Test public void testEmptyCheck() { - assertThrows(ParameterException.class, () -> ValueValidationUtil.emptyCheck("param1", "")); - assertThrows(ParameterException.class, () -> ValueValidationUtil.emptyCheck("param2", null)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.emptyCheck("param1", "")); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.emptyCheck("param2", null)); ValueValidationUtil.emptyCheck("param3", "nonEmpty"); } @Test public void testMinValueCheck() { - assertThrows(ParameterException.class, () -> ValueValidationUtil.minValueCheck("param1", 9L, 10L)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.minValueCheck("param1", 9L, 10L)); ValueValidationUtil.minValueCheck("param2", 10L, 10L); ValueValidationUtil.minValueCheck("param3", 11L, 10L); } @Test public void testPositiveCheckInt() { - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param1", 0)); - assertThrows(ParameterException.class, () -> ValueValidationUtil.positiveCheck("param2", -1)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param1", 0)); + assertThrows(IllegalArgumentException.class, () -> ValueValidationUtil.positiveCheck("param2", -1)); ValueValidationUtil.positiveCheck("param3", 1); } } 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 d669d455df1eb..283e94bfb9c9e 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 @@ -18,12 +18,12 @@ */ package org.apache.pulsar.cli.converters; -import com.beust.jcommander.ParameterException; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToIntegerConverter; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import picocli.CommandLine.TypeConversionException; public class ByteConversionTest { @@ -65,31 +65,27 @@ public void testSuccessfulByteUnitToLongConverter(String input, long expected) { } @Test(dataProvider = "successfulByteUnitUtilTestCases") - public void testSuccessfulByteUnitIntegerConverter(String input, long expected) { - ByteUnitIntegerConverter converter = new ByteUnitIntegerConverter("optionName"); + public void testSuccessfulByteUnitIntegerConverter(String input, long expected) throws Exception { + ByteUnitToIntegerConverter converter = new ByteUnitToIntegerConverter(); // Since the converter returns an Integer, we need to cast expected to int assertEquals(converter.convert(input), Integer.valueOf((int) expected)); } @Test(dataProvider = "failingByteUnitUtilTestCases") public void testFailedByteUnitUtilConversion(String input) { - if (input.isEmpty()) { - assertThrows(IllegalArgumentException.class, () -> ByteUnitUtil.validateSizeString(input)); - } else { - assertThrows(ParameterException.class, () -> ByteUnitUtil.validateSizeString(input)); - } + assertThrows(IllegalArgumentException.class, () -> ByteUnitUtil.validateSizeString(input)); } @Test(dataProvider = "failingByteUnitUtilTestCases") public void testFailedByteUnitToLongConverter(String input) { ByteUnitToLongConverter converter = new ByteUnitToLongConverter("optionName"); - assertThrows(ParameterException.class, () -> converter.convert(input)); + assertThrows(IllegalArgumentException.class, () -> converter.convert(input)); } @Test(dataProvider = "failingByteUnitUtilTestCases") public void testFailedByteUnitIntegerConverter(String input) { - ByteUnitIntegerConverter converter = new ByteUnitIntegerConverter("optionName"); - assertThrows(ParameterException.class, () -> converter.convert(input)); + ByteUnitToIntegerConverter converter = new ByteUnitToIntegerConverter(); + assertThrows(TypeConversionException.class, () -> converter.convert(input)); } } diff --git a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/TimeConversionTest.java b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/TimeConversionTest.java index f7adeee0423ae..cc50eed4d03e4 100644 --- a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/TimeConversionTest.java +++ b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/converters/TimeConversionTest.java @@ -18,13 +18,13 @@ */ package org.apache.pulsar.cli.converters; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertThrows; import java.util.concurrent.TimeUnit; - +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import static org.testng.Assert.*; - public class TimeConversionTest { @DataProvider @@ -63,8 +63,8 @@ public void testSuccessfulTimeUnitToSecondsConverter(String input, long expected } @Test(dataProvider = "successfulRelativeTimeUtilTestCases") - public void testSuccessfulTimeUnitToMillisConverter(String input, long expected) { - TimeUnitToMillisConverter millisConverter = new TimeUnitToMillisConverter("optionName"); + public void testSuccessfulTimeUnitToMillisConverter(String input, long expected) throws Exception { + TimeUnitToMillisConverter millisConverter = new TimeUnitToMillisConverter(); // We multiply the expected by 1000 to convert the seconds into milliseconds assertEquals(millisConverter.convert(input), Long.valueOf(expected * 1000)); } diff --git a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/validators/CliUtilValidatorsTest.java b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/validators/CliUtilValidatorsTest.java index da1f6ec66bd9c..ba7de23373892 100644 --- a/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/validators/CliUtilValidatorsTest.java +++ b/pulsar-cli-utils/src/test/java/org/apache/pulsar/cli/validators/CliUtilValidatorsTest.java @@ -19,7 +19,6 @@ package org.apache.pulsar.cli.validators; import static org.testng.Assert.assertThrows; -import com.beust.jcommander.ParameterException; import org.testng.annotations.Test; public class CliUtilValidatorsTest { @@ -27,23 +26,23 @@ public class CliUtilValidatorsTest { @Test public void testPositiveLongValueValidator() { PositiveLongValueValidator validator = new PositiveLongValueValidator(); - assertThrows(ParameterException.class, () -> validator.validate("param", -1L)); - assertThrows(ParameterException.class, () -> validator.validate("param", 0L)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", -1L)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", 0L)); validator.validate("param", 1L); } @Test public void testPositiveIntegerValueValidator() { PositiveIntegerValueValidator validator = new PositiveIntegerValueValidator(); - assertThrows(ParameterException.class, () -> validator.validate("param", -1)); - assertThrows(ParameterException.class, () -> validator.validate("param", 0)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", -1)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", 0)); validator.validate("param", 1); } @Test public void testNonNegativeValueValidator() { NonNegativeValueValidator validator = new NonNegativeValueValidator(); - assertThrows(ParameterException.class, () -> validator.validate("param", -1L)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", -1L)); validator.validate("param", 0L); validator.validate("param", 1L); } @@ -51,7 +50,7 @@ public void testNonNegativeValueValidator() { @Test public void testMinNegativeOneValidator() { MinNegativeOneValidator validator = new MinNegativeOneValidator(); - assertThrows(ParameterException.class, () -> validator.validate("param", -2L)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", -2L)); validator.validate("param", -1L); validator.validate("param", 0L); } @@ -59,7 +58,7 @@ public void testMinNegativeOneValidator() { @Test public void testIntegerMaxValueLongValidator() { IntegerMaxValueLongValidator validator = new IntegerMaxValueLongValidator(); - assertThrows(ParameterException.class, () -> validator.validate("param", Integer.MAX_VALUE + 1L)); + assertThrows(IllegalArgumentException.class, () -> validator.validate("param", Integer.MAX_VALUE + 1L)); validator.validate("param", (long) Integer.MAX_VALUE); } } diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/CmdFunctionsTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/CmdFunctionsTest.java index 39ede3bb7aef1..4d906af9424f5 100644 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/CmdFunctionsTest.java +++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/CmdFunctionsTest.java @@ -29,13 +29,9 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.List; +import java.io.PrintWriter; +import java.io.StringWriter; +import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.admin.cli.CmdFunctions.CreateFunction; import org.apache.pulsar.admin.cli.CmdFunctions.DeleteFunction; @@ -55,6 +51,7 @@ import org.apache.pulsar.functions.api.utils.IdentityFunction; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import picocli.CommandLine; /** * Unit test of {@link CmdFunctions}. @@ -542,11 +539,12 @@ public void testCreateWithoutOutputTopicWithSkipFlag() throws Exception { @Test - public void testCreateWithoutOutputTopic() { - - ConsoleOutputCapturer consoleOutputCapturer = new ConsoleOutputCapturer(); - consoleOutputCapturer.start(); - + public void testCreateWithoutOutputTopic() throws Exception { + @Cleanup + StringWriter stringWriter = new StringWriter(); + @Cleanup + PrintWriter printWriter = new PrintWriter(stringWriter); + cmd.getCommander().setOut(printWriter); cmd.run(new String[] { "create", "--inputs", INPUT_TOPIC_NAME, @@ -557,9 +555,8 @@ public void testCreateWithoutOutputTopic() { }); CreateFunction creater = cmd.getCreater(); - consoleOutputCapturer.stop(); assertNull(creater.getFunctionConfig().getOutput()); - assertTrue(consoleOutputCapturer.getStdout().contains("Created successfully")); + assertTrue(stringWriter.toString().contains("Created successfully")); } @Test @@ -655,17 +652,19 @@ public void testStateGetter() throws Exception { @Test public void testStateGetterWithoutKey() throws Exception { - ConsoleOutputCapturer consoleOutputCapturer = new ConsoleOutputCapturer(); - consoleOutputCapturer.start(); - cmd.run(new String[] { + CommandLine commander = cmd.getCommander(); + @Cleanup + StringWriter stringWriter = new StringWriter(); + @Cleanup + PrintWriter printWriter = new PrintWriter(stringWriter); + commander.setErr(printWriter); + cmd.run(new String[]{ "querystate", "--tenant", TENANT, "--namespace", NAMESPACE, "--name", FN_NAME, }); - consoleOutputCapturer.stop(); - String output = consoleOutputCapturer.getStderr(); - assertTrue(output.replace("\n", "").contains("State key needs to be specified")); + assertTrue(stringWriter.toString().startsWith(("State key needs to be specified"))); StateGetter stateGetter = cmd.getStateGetter(); assertEquals(TENANT, stateGetter.getTenant()); assertEquals(NAMESPACE, stateGetter.getNamespace()); @@ -896,79 +895,4 @@ public void testDownloadTransformFunction() throws Exception { verify(functions, times(1)) .downloadFunction(JAR_NAME, TENANT, NAMESPACE, FN_NAME, true); } - - - public static class ConsoleOutputCapturer { - private ByteArrayOutputStream stdout; - private ByteArrayOutputStream stderr; - private PrintStream previous; - private boolean capturing; - - public void start() { - if (capturing) { - return; - } - - capturing = true; - previous = System.out; - stdout = new ByteArrayOutputStream(); - stderr = new ByteArrayOutputStream(); - - OutputStream outputStreamCombinerstdout = - new OutputStreamCombiner(Arrays.asList(previous, stdout)); - PrintStream stdoutStream = new PrintStream(outputStreamCombinerstdout); - - OutputStream outputStreamCombinerStderr = - new OutputStreamCombiner(Arrays.asList(previous, stderr)); - PrintStream stderrStream = new PrintStream(outputStreamCombinerStderr); - - System.setOut(stdoutStream); - System.setErr(stderrStream); - } - - public void stop() { - if (!capturing) { - return; - } - - System.setOut(previous); - - previous = null; - capturing = false; - } - - public String getStdout() { - return stdout.toString(); - } - - public String getStderr() { - return stderr.toString(); - } - - private static class OutputStreamCombiner extends OutputStream { - private List outputStreams; - - public OutputStreamCombiner(List outputStreams) { - this.outputStreams = outputStreams; - } - - public void write(int b) throws IOException { - for (OutputStream os : outputStreams) { - os.write(b); - } - } - - public void flush() throws IOException { - for (OutputStream os : outputStreams) { - os.flush(); - } - } - - public void close() throws IOException { - for (OutputStream os : outputStreams) { - os.close(); - } - } - } - } } diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/DeprecatedCommanderTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/DeprecatedCommanderTest.java deleted file mode 100644 index 3112344bedcda..0000000000000 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/DeprecatedCommanderTest.java +++ /dev/null @@ -1,84 +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.admin.cli; - - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; -import com.beust.jcommander.DefaultUsageFormatter; -import org.apache.pulsar.client.admin.PulsarAdmin; -import org.apache.pulsar.client.admin.Schemas; -import org.apache.pulsar.client.admin.Topics; -import org.mockito.Mockito; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class DeprecatedCommanderTest { - PulsarAdmin admin; - Topics mockTopics; - Schemas mockSchemas; - CmdTopics cmdTopics; - - @BeforeMethod - public void setup() { - admin = Mockito.mock(PulsarAdmin.class); - mockTopics = mock(Topics.class); - when(admin.topics()).thenReturn(mockTopics); - mockSchemas = mock(Schemas.class); - when(admin.schemas()).thenReturn(mockSchemas); - cmdTopics = new CmdTopics(() -> admin); - } - - @Test - public void testDeprecatedCommanderWorks() throws Exception { - - DefaultUsageFormatter defaultUsageFormatter = new DefaultUsageFormatter(cmdTopics.jcommander); - StringBuilder builder = new StringBuilder(); - defaultUsageFormatter.usage(builder); - String defaultOutput = builder.toString(); - - StringBuilder builder2 = new StringBuilder(); - cmdTopics.jcommander.getUsageFormatter().usage(builder2); - String outputWithFiltered = builder2.toString(); - - assertNotEquals(outputWithFiltered, defaultOutput); - assertFalse(outputWithFiltered.contains("enable-deduplication")); - assertTrue(defaultOutput.contains("enable-deduplication")); - assertFalse(outputWithFiltered.contains("get-max-unacked-messages-on-consumer")); - assertTrue(defaultOutput.contains("get-max-unacked-messages-on-consumer")); - assertFalse(outputWithFiltered.contains("get-deduplication")); - assertTrue(defaultOutput.contains("get-deduplication")); - - // annotation was changed to hidden, reset it. - cmdTopics = new CmdTopics(() -> admin); - CmdUsageFormatter formatter = (CmdUsageFormatter)cmdTopics.jcommander.getUsageFormatter(); - formatter.clearDeprecatedCommand(); - StringBuilder builder3 = new StringBuilder(); - cmdTopics.jcommander.getUsageFormatter().usage(builder3); - String outputAfterClean = builder3.toString(); - - assertEquals(outputAfterClean, defaultOutput); - - } - -} diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java index 2d567a7528dcc..fd1bdf4799848 100644 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java +++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java @@ -18,6 +18,8 @@ */ package org.apache.pulsar.admin.cli; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.longThat; @@ -33,14 +35,13 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertNotNull; - -import com.beust.jcommander.JCommander; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; +import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; @@ -122,6 +123,7 @@ import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.Test; +import picocli.CommandLine; @Slf4j public class PulsarAdminToolTest { @@ -167,6 +169,8 @@ public void brokers() throws Exception { brokers.run(split("version")); verify(mockBrokers).getVersion(); + doReturn(CompletableFuture.completedFuture(null)).when(mockBrokers) + .shutDownBrokerGracefully(anyInt(), anyBoolean()); brokers.run(split("shutdown -m 10 -f")); verify(mockBrokers).shutDownBrokerGracefully(10,true); } @@ -179,15 +183,19 @@ public void brokerStats() throws Exception { CmdBrokerStats brokerStats = new CmdBrokerStats(() -> admin); + doReturn("null").when(mockBrokerStats).getTopics(); brokerStats.run(split("topics")); verify(mockBrokerStats).getTopics(); + doReturn(null).when(mockBrokerStats).getLoadReport(); brokerStats.run(split("load-report")); verify(mockBrokerStats).getLoadReport(); + doReturn("null").when(mockBrokerStats).getMBeans(); brokerStats.run(split("mbeans")); verify(mockBrokerStats).getMBeans(); + doReturn("null").when(mockBrokerStats).getMetrics(); brokerStats.run(split("monitoring-metrics")); verify(mockBrokerStats).getMetrics(); } @@ -1569,7 +1577,7 @@ public void topics() throws Exception { verify(mockLookup).lookupPartitionedTopic("persistent://myprop/clust/ns1/ds1"); cmdTopics.run(split("partitioned-lookup persistent://myprop/clust/ns1/ds1 --sort-by-broker")); - verify(mockLookup).lookupPartitionedTopic("persistent://myprop/clust/ns1/ds1"); + verify(mockLookup, times(2)).lookupPartitionedTopic("persistent://myprop/clust/ns1/ds1"); cmdTopics.run(split("bundle-range persistent://myprop/clust/ns1/ds1")); verify(mockLookup).getBundleRange("persistent://myprop/clust/ns1/ds1"); @@ -2496,21 +2504,20 @@ public void customCommands() throws Exception { assertTrue(logs.contains("customgroup")); assertTrue(logs.contains("Custom group 1 description")); + // missing subcommand logs = runCustomCommand(new String[]{"customgroup"}); - assertTrue(logs.contains("command1")); + assertTrue(logs.contains("Missing required subcommand")); assertTrue(logs.contains("Command 1 description")); - assertTrue(logs.contains("command2")); assertTrue(logs.contains("Command 2 description")); + // missing required parameter logs = runCustomCommand(new String[]{"customgroup", "command1"}); + assertTrue(logs.contains("Missing required options and parameters")); assertTrue(logs.contains("Command 1 description")); - assertTrue(logs.contains("Usage: command1 [options] Topic")); - // missing required parameter logs = runCustomCommand(new String[]{"customgroup", "command1", "mytopic"}); assertTrue(logs.contains("Command 1 description")); - assertTrue(logs.contains("Usage: command1 [options] Topic")); - assertTrue(logs.contains("The following option is required")); + assertTrue(logs.contains("Missing required option")); // run a comand that uses PulsarAdmin API logs = runCustomCommand(new String[]{"customgroup", "command1", "--type", "stats", "mytopic"}); @@ -2578,39 +2585,26 @@ public void customCommandsFactoryImmutable() throws Exception { } @Test - public void testHelpFlag() { - PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); + public void testHelpFlag() throws Exception { + Properties properties = new Properties(); + properties.put("webServiceUrl", "http://localhost:8080"); + + PulsarAdminTool pulsarAdminTool = new PulsarAdminTool(properties); { - CmdSchemas cmdSchemas = new CmdSchemas(() -> admin); - cmdSchemas.run(split("-h")); - assertTrue(cmdSchemas.isHelp()); + assertTrue(pulsarAdminTool.run(split("schemas -h"))); } { - CmdSchemas cmdSchemas = new CmdSchemas(() -> admin); - cmdSchemas.run(split("--help")); - assertTrue(cmdSchemas.isHelp()); + assertTrue(pulsarAdminTool.run(split("schemas --help"))); } { - CmdSchemas cmdSchemas = new CmdSchemas(() -> admin); - cmdSchemas.run(split("delete --help")); - assertFalse(cmdSchemas.isHelp()); - JCommander commander = cmdSchemas.getJcommander(); - JCommander subCommander = commander.getCommands().get("delete"); - CliCommand subcommand = (CliCommand) subCommander.getObjects().get(0); - assertTrue(subcommand.isHelp()); + assertTrue(pulsarAdminTool.run(split("schemas delete -h"))); } { - CmdSchemas cmdSchemas = new CmdSchemas(() -> admin); - cmdSchemas.run(split("delete -h")); - assertFalse(cmdSchemas.isHelp()); - JCommander commander = cmdSchemas.getJcommander(); - JCommander subCommander = commander.getCommands().get("delete"); - CliCommand subcommand = (CliCommand) subCommander.getObjects().get(0); - assertTrue(subcommand.isHelp()); + assertTrue(pulsarAdminTool.run(split("schemas delete --help"))); } } @@ -2633,11 +2627,9 @@ private static String runCustomCommand(String[] args) throws Exception { properties.put("cliExtensionsDirectory", narFile.getParentFile().getAbsolutePath()); properties.put("customCommandFactories", "dummy"); PulsarAdminTool tool = new PulsarAdminTool(properties); - tool.setPulsarAdminSupplier(new PulsarAdminSupplier(builder, tool.getRootParams())); - - // see the custom command help in the main help + tool.getPulsarAdminSupplier().setAdminBuilder(builder); StringBuilder logs = new StringBuilder(); - try (CaptureStdOut capture = new CaptureStdOut(logs)){ + try (CaptureStdOut capture = new CaptureStdOut(tool.commander, logs)) { tool.run(args); } log.info("Captured out: {}", logs); @@ -2647,13 +2639,18 @@ private static String runCustomCommand(String[] args) throws Exception { private static class CaptureStdOut implements AutoCloseable { final PrintStream currentOut = System.out; final PrintStream currentErr = System.err; - final ByteArrayOutputStream logs = new ByteArrayOutputStream(); - final PrintStream capturedOut = new PrintStream(logs, true); + final ByteArrayOutputStream logs; + final PrintStream capturedOut; final StringBuilder receiver; - public CaptureStdOut(StringBuilder receiver) { + public CaptureStdOut(CommandLine commandLine, StringBuilder receiver) { + logs = new ByteArrayOutputStream(); + capturedOut = new PrintStream(logs, true); this.receiver = receiver; - System.setOut(capturedOut); - System.setErr(capturedOut); + PrintWriter printWriter = new PrintWriter(logs); + commandLine.setErr(printWriter); + commandLine.setOut(printWriter); + System.setOut(new PrintStream(logs)); + System.setErr(new PrintStream(logs)); } public void close() { capturedOut.flush(); diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/DocumentTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/DocumentTest.java index c565fadd2fc9a..84d423c6072b6 100644 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/DocumentTest.java +++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/DocumentTest.java @@ -20,16 +20,13 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; - -import com.beust.jcommander.JCommander; +import java.util.Map; +import java.util.Properties; import org.apache.pulsar.broker.service.BrokerTestBase; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; - -import java.util.Map; -import java.util.Properties; - +import picocli.CommandLine; public class DocumentTest extends BrokerTestBase { @@ -59,11 +56,12 @@ public void testSpecifyModuleName() { @Test public void testGenerator() { PulsarClientTool pulsarClientTool = new PulsarClientTool(new Properties()); - JCommander commander = pulsarClientTool.jcommander; + CommandLine commander = pulsarClientTool.getCommander(); CmdGenerateDocumentation document = new CmdGenerateDocumentation(); - for (Map.Entry cmd : commander.getCommands().entrySet()) { - String res = document.generateDocument(cmd.getKey(), commander); - assertTrue(res.contains("pulsar-client " + cmd.getKey() + " [options]")); - } + Map subcommands = commander.getSubcommands(); + subcommands.forEach((subcommandName, subCommander) -> { + String res = document.generateDocument(subcommandName, subCommander); + assertTrue(res.contains("pulsar-client " + subcommandName + " [options]")); + }); } } diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolForceBatchNum.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolForceBatchNum.java index e296ab0e0357a..896bee0e030af 100644 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolForceBatchNum.java +++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolForceBatchNum.java @@ -53,11 +53,6 @@ public PulsarClientToolForceBatchNum(Properties properties, String topic, int ba super(properties); this.topic = topic; this.batchNum = batchNum; - } - - @Override - protected void initJCommander() { - super.initJCommander(); produceCommand = new CmdProduce() { @Override public void updateConfig(ClientBuilder newBuilder, Authentication authentication, String serviceURL) { @@ -68,7 +63,7 @@ public void updateConfig(ClientBuilder newBuilder, Authentication authentication } } }; - jcommander.addCommand("produce", produceCommand); + replaceProducerCommand(produceCommand); } private ClientBuilder mockClientBuilder(ClientBuilder newBuilder) throws Exception { diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java index 84b8060b3115d..9edee30d8fee8 100644 --- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java +++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java @@ -357,7 +357,7 @@ public void testArgs() throws Exception { "--memory-limit", memoryLimitArg, "produce", "-m", message, "-n", Integer.toString(numberOfMessages), topicName}; - pulsarClientTool.jcommander.parse(args); + pulsarClientTool.getCommander().parseArgs(args); assertEquals(pulsarClientTool.rootParams.getTlsTrustCertsFilePath(), CA_CERT_FILE_PATH); assertEquals(pulsarClientTool.rootParams.getAuthParams(), authParams); assertEquals(pulsarClientTool.rootParams.getAuthPluginClassName(), authPlugin); @@ -386,8 +386,7 @@ public void testMemoryLimitArgShortName() throws Exception { "-ml", memoryLimitArg, "produce", "-m", message, "-n", Integer.toString(numberOfMessages), topicName}; - - pulsarClientTool.jcommander.parse(args); + pulsarClientTool.getCommander().parseArgs(args); assertEquals(pulsarClientTool.rootParams.getMemoryLimit(), 10 * 1024 * 1024); } @@ -405,7 +404,7 @@ public void testParsingProxyServiceUrlAndProxyProtocolFromProperties() throws Ex String[] args = {"--url", url, "produce", "-m", message, "-n", Integer.toString(numberOfMessages), topicName}; - pulsarClientTool.jcommander.parse(args); + pulsarClientTool.getCommander().parseArgs(args); assertEquals(pulsarClientTool.rootParams.getServiceURL(), url); assertEquals(pulsarClientTool.rootParams.getProxyServiceURL(), "pulsar+ssl://my-proxy-pulsar:4443"); assertEquals(pulsarClientTool.rootParams.getProxyProtocol(), ProxyProtocol.SNI); diff --git a/pulsar-client-tools/pom.xml b/pulsar-client-tools/pom.xml index 75bedca2b3f67..ba3d2b3a4a254 100644 --- a/pulsar-client-tools/pom.xml +++ b/pulsar-client-tools/pom.xml @@ -34,8 +34,13 @@ - com.beust - jcommander + info.picocli + picocli + compile + + + info.picocli + picocli-shell-jline3 compile diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CliCommand.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CliCommand.java index e984f114c27b1..8a8019cbe8ccc 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CliCommand.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CliCommand.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import java.util.Arrays; @@ -27,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.Callable; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.api.MessageId; import org.apache.pulsar.client.impl.MessageIdImpl; @@ -35,44 +34,42 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.util.ObjectMapperFactory; - -public abstract class CliCommand { - - @Parameter(names = { "--help", "-h" }, help = true, hidden = true) - private boolean help = false; - - public boolean isHelp() { - return help; - } - - static String[] validatePropertyCluster(List params) { - return splitParameter(params, 2); +import picocli.CommandLine; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Spec; + +public abstract class CliCommand implements Callable { + @Spec + private CommandSpec commandSpec; + + static String[] validatePropertyCluster(String params) { + String[] parts = params.split("/"); + if (parts.length != 2) { + throw new IllegalArgumentException("Parameter format is incorrect"); + } + return parts; } - static String validateNamespace(List params) { - String namespace = checkArgument(params); + static String validateNamespace(String namespace) { return NamespaceName.get(namespace).toString(); } - static String validateTopicName(List params) { - String topic = checkArgument(params); + static String validateTopicName(String topic) { return TopicName.get(topic).toString(); } - static String validatePersistentTopic(List params) { - String topic = checkArgument(params); + static String validatePersistentTopic(String topic) { TopicName topicName = TopicName.get(topic); if (topicName.getDomain() != TopicDomain.persistent) { - throw new ParameterException("Need to provide a persistent topic name"); + throw new IllegalArgumentException("Need to provide a persistent topic name"); } return topicName.toString(); } - static String validateNonPersistentTopic(List params) { - String topic = checkArgument(params); + static String validateNonPersistentTopic(String topic) { TopicName topicName = TopicName.get(topic); if (topicName.getDomain() != TopicDomain.non_persistent) { - throw new ParameterException("Need to provide a non-persistent topic name"); + throw new IllegalArgumentException("Need to provide a non-persistent topic name"); } return topicName.toString(); } @@ -92,54 +89,7 @@ static MessageId validateMessageIdString(String resetMessageIdStr, int partition } } - static String checkArgument(List arguments) { - if (arguments.size() != 1) { - throw new ParameterException("Need to provide just 1 parameter"); - } - - return arguments.get(0); - } - - private static String[] splitParameter(List params, int n) { - if (params.size() != 1) { - throw new ParameterException("Need to provide just 1 parameter"); - } - - String[] parts = params.get(0).split("/"); - if (parts.length != n) { - throw new ParameterException("Parameter format is incorrect"); - } - - return parts; - } - - static String getOneArgument(List params) { - if (params.size() != 1) { - throw new ParameterException("Need to provide just 1 parameter"); - } - - return params.get(0); - } - - /** - * - * @param params - * List of positional arguments - * @param pos - * Positional arguments start with index as 1 - * @param maxArguments - * Validate against max arguments - * @return - */ - static String getOneArgument(List params, int pos, int maxArguments) { - if (params.size() != maxArguments) { - throw new ParameterException(String.format("Need to provide %s parameters", maxArguments)); - } - - return params.get(pos); - } - - static Set getAuthActions(List actions) { + Set getAuthActions(List actions) { Set res = new TreeSet<>(); AuthAction authAction; for (String action : actions) { @@ -170,7 +120,7 @@ void print(Map items) { void print(T item) { try { if (item instanceof String) { - System.out.println(item); + commandSpec.commandLine().getOut().println(item); } else { prettyPrint(item); } @@ -181,7 +131,7 @@ void print(T item) { void prettyPrint(T item) { try { - System.out.println(WRITER.writeValueAsString(item)); + commandSpec.commandLine().getOut().println(WRITER.writeValueAsString(item)); } catch (Exception e) { throw new RuntimeException(e); } @@ -190,5 +140,22 @@ void prettyPrint(T item) { private static final ObjectMapper MAPPER = ObjectMapperFactory.create(); private static final ObjectWriter WRITER = MAPPER.writerWithDefaultPrettyPrinter(); + // Picocli entrypoint. + @Override + public Integer call() throws Exception { + run(); + return 0; + } + abstract void run() throws Exception; + + protected class ParameterException extends CommandLine.ParameterException { + public ParameterException(String msg) { + super(commandSpec.commandLine(), msg); + } + + public ParameterException(String msg, Throwable e) { + super(commandSpec.commandLine(), msg, e); + } + } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java index 381bc8abcaa3f..07e8a8b5df63b 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java @@ -19,11 +19,6 @@ package org.apache.pulsar.admin.cli; import static org.apache.pulsar.client.admin.internal.BaseResource.getApiException; -import com.beust.jcommander.DefaultUsageFormatter; -import com.beust.jcommander.IUsageFormatter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,14 +29,12 @@ import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; -import org.apache.pulsar.client.admin.PulsarAdminException.ConnectException; import org.apache.pulsar.client.admin.internal.PulsarAdminImpl; +import picocli.CommandLine; public abstract class CmdBase { - protected final JCommander jcommander; + private final CommandLine commander; private final Supplier adminSupplier; - private PulsarAdmin admin; - private IUsageFormatter usageFormatter; /** * Default read timeout in milliseconds. @@ -49,91 +42,18 @@ public abstract class CmdBase { */ private static final long DEFAULT_READ_TIMEOUT_MILLIS = 60000; - @Parameter(names = { "--help", "-h" }, help = true, hidden = true) - private boolean help = false; - - public boolean isHelp() { - return help; - } - public CmdBase(String cmdName, Supplier adminSupplier) { this.adminSupplier = adminSupplier; - jcommander = new JCommander(this); - usageFormatter = new CmdUsageFormatter(jcommander); - jcommander.setProgramName("pulsar-admin " + cmdName); - jcommander.setUsageFormatter(usageFormatter); - } - - protected IUsageFormatter getUsageFormatter() { - if (usageFormatter == null) { - usageFormatter = new DefaultUsageFormatter(jcommander); - } - return usageFormatter; - } - - private void tryShowCommandUsage() { - try { - String chosenCommand = jcommander.getParsedCommand(); - getUsageFormatter().usage(chosenCommand); - } catch (Exception e) { - // it is caused by an invalid command, the invalid command can not be parsed - System.err.println("Invalid command, please use `pulsar-admin --help` to check out how to use"); - } + commander = new CommandLine(this); + commander.setCommandName(cmdName); } public boolean run(String[] args) { - try { - jcommander.parse(args); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.err.println(); - tryShowCommandUsage(); - return false; - } - - String cmd = jcommander.getParsedCommand(); - if (cmd == null) { - jcommander.usage(); - return help; - } - - JCommander obj = jcommander.getCommands().get(cmd); - CliCommand cmdObj = (CliCommand) obj.getObjects().get(0); - - if (cmdObj.isHelp()) { - obj.setProgramName(jcommander.getProgramName() + " " + cmd); - obj.usage(); - return true; - } - - try { - cmdObj.run(); - return true; - } catch (ParameterException e) { - System.err.println(e.getMessage()); - System.err.println(); - return false; - } catch (ConnectException e) { - System.err.println(e.getMessage()); - System.err.println(); - System.err.println("Error connecting to: " + getAdmin().getServiceUrl()); - return false; - } catch (PulsarAdminException e) { - System.err.println(e.getHttpError()); - System.err.println(); - System.err.println("Reason: " + e.getMessage()); - return false; - } catch (Exception e) { - e.printStackTrace(); - return false; - } + return commander.execute(args) == 0; } protected PulsarAdmin getAdmin() { - if (admin == null) { - admin = adminSupplier.get(); - } - return admin; + return adminSupplier.get(); } protected long getReadTimeoutMs() { @@ -159,7 +79,7 @@ protected T sync(Supplier> executor) throws PulsarAdmin } } - static Map parseListKeyValueMap(List metadata) { + Map parseListKeyValueMap(List metadata) { Map map = null; if (metadata != null && !metadata.isEmpty()) { map = new HashMap<>(); @@ -175,7 +95,26 @@ static Map parseListKeyValueMap(List metadata) { return map; } - public JCommander getJcommander() { - return jcommander; + // Used to register the subcomand. + protected CommandLine getCommander() { + return commander; + } + + protected void addCommand(String name, Object cmd) { + commander.addSubcommand(name, cmd); + } + + protected void addCommand(String name, Object cmd, String... aliases) { + commander.addSubcommand(name, cmd, aliases); + } + + protected class ParameterException extends CommandLine.ParameterException { + public ParameterException(String msg) { + super(commander, msg); + } + + public ParameterException(String msg, Throwable e) { + super(commander, msg, e); + } } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBookies.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBookies.java index 27502a305ac4d..09389d474ff5e 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBookies.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBookies.java @@ -18,19 +18,18 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.base.Strings; import java.util.function.Supplier; import lombok.NonNull; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.common.policies.data.BookieInfo; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -@Parameters(commandDescription = "Operations about bookies rack placement") +@Command(description = "Operations about bookies rack placement") public class CmdBookies extends CmdBase { - @Parameters(commandDescription = "Gets the rack placement information for all the bookies in the cluster") + @Command(description = "Gets the rack placement information for all the bookies in the cluster") private class GetAll extends CliCommand { @Override @@ -39,10 +38,10 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Gets the rack placement information for a specific bookie in the cluster") + @Command(description = "Gets the rack placement information for a specific bookie in the cluster") private class GetBookie extends CliCommand { - @Parameter(names = { "-b", "--bookie" }, + @Option(names = {"-b", "--bookie"}, description = "Bookie address (format: `address:port`)", required = true) private String bookieAddress; @@ -52,7 +51,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "List bookies") + @Command(description = "List bookies") private class ListBookies extends CliCommand { @Override @@ -61,10 +60,10 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Remove rack placement information for a specific bookie in the cluster") + @Command(description = "Remove rack placement information for a specific bookie in the cluster") private class RemoveBookie extends CliCommand { - @Parameter(names = { "-b", "--bookie" }, + @Option(names = {"-b", "--bookie"}, description = "Bookie address (format: `address:port`)", required = true) private String bookieAddress; @@ -74,19 +73,19 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Updates the rack placement information for a specific bookie in the cluster " + @Command(description = "Updates the rack placement information for a specific bookie in the cluster " + "(note. bookie address format:`address:port`)") private class UpdateBookie extends CliCommand { private static final String PATH_SEPARATOR = "/"; - @Parameter(names = { "-g", "--group" }, description = "Bookie group name", required = false) + @Option(names = {"-g", "--group"}, description = "Bookie group name", required = false) private String group = "default"; - @Parameter(names = { "-b", "--bookie" }, + @Option(names = {"-b", "--bookie"}, description = "Bookie address (format: `address:port`)", required = true) private String bookieAddress; - @Parameter(names = { "-r", "--rack" }, description = "Bookie rack name. " + @Option(names = {"-r", "--rack"}, description = "Bookie rack name. " + "If you set a bookie rack name to slash (/) " + "or an empty string (\"\"): " + "when using Pulsar earlier than 2.7.5, 2.8.3, and 2.9.2, " @@ -104,7 +103,7 @@ private class UpdateBookie extends CliCommand { + "but /region0rack0 and /region0/rack/0 are not allowed.", required = true) private String bookieRack; - @Parameter(names = {"-hn", "--hostname"}, description = "Bookie host name", required = false) + @Option(names = {"-hn", "--hostname"}, description = "Bookie host name", required = false) private String bookieHost; @Override @@ -128,10 +127,10 @@ private void checkArgument(boolean expression, @NonNull Object errorMessage) { public CmdBookies(Supplier admin) { super("bookies", admin); - jcommander.addCommand("racks-placement", new GetAll()); - jcommander.addCommand("list-bookies", new ListBookies()); - jcommander.addCommand("get-bookie-rack", new GetBookie()); - jcommander.addCommand("delete-bookie-rack", new RemoveBookie()); - jcommander.addCommand("set-bookie-rack", new UpdateBookie()); + addCommand("racks-placement", new GetAll()); + addCommand("list-bookies", new ListBookies()); + addCommand("get-bookie-rack", new GetBookie()); + addCommand("delete-bookie-rack", new RemoveBookie()); + addCommand("set-bookie-rack", new UpdateBookie()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokerStats.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokerStats.java index c83beec330f39..b9f7bdabd7c7f 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokerStats.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokerStats.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.admin.cli; -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.gson.Gson; @@ -29,19 +27,21 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; -import java.util.List; import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.common.stats.AllocatorStats; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations to collect broker statistics") +@Command(description = "Operations to collect broker statistics") public class CmdBrokerStats extends CmdBase { private static final String DEFAULT_INDENTATION = " "; - @Parameters(commandDescription = "dump metrics for Monitoring") + @Command(description = "dump metrics for Monitoring") private class CmdMonitoringMetrics extends CliCommand { - @Parameter(names = { "-i", "--indent" }, description = "Indent JSON output", required = false) + @Option(names = {"-i", "--indent"}, description = "Indent JSON output", required = false) private boolean indent = false; @Override @@ -67,9 +67,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "dump mbean stats") + @Command(description = "dump mbean stats") private class CmdDumpMBeans extends CliCommand { - @Parameter(names = { "-i", "--indent" }, description = "Indent JSON output", required = false) + @Option(names = {"-i", "--indent"}, description = "Indent JSON output", required = false) private boolean indent = false; @Override @@ -88,7 +88,7 @@ void run() throws Exception { } - @Parameters(commandDescription = "dump broker load-report") + @Command(description = "dump broker load-report") private class CmdLoadReport extends CliCommand { @Override @@ -97,9 +97,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "dump topics stats") + @Command(description = "dump topics stats") private class CmdTopics extends CliCommand { - @Parameter(names = { "-i", "--indent" }, description = "Indent JSON output", required = false) + @Option(names = {"-i", "--indent"}, description = "Indent JSON output", required = false) private boolean indent = false; @Override @@ -118,14 +118,14 @@ void run() throws Exception { } - @Parameters(commandDescription = "dump allocator stats") + @Command(description = "dump allocator stats") private class CmdAllocatorStats extends CliCommand { - @Parameter(description = "allocator-name", required = true) - private List params; + @Parameters(description = "allocator-name", arity = "1") + private String allocatorName; @Override void run() throws Exception { - AllocatorStats stats = getAdmin().brokerStats().getAllocatorStats(params.get(0)); + AllocatorStats stats = getAdmin().brokerStats().getAllocatorStats(allocatorName); ObjectMapper mapper = ObjectMapperFactory.create(); ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); try (Writer out = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) { @@ -138,11 +138,11 @@ void run() throws Exception { public CmdBrokerStats(Supplier admin) { super("broker-stats", admin); - jcommander.addCommand("monitoring-metrics", new CmdMonitoringMetrics()); - jcommander.addCommand("mbeans", new CmdDumpMBeans()); - jcommander.addCommand("topics", new CmdTopics(), "destinations"); - jcommander.addCommand("allocator-stats", new CmdAllocatorStats()); - jcommander.addCommand("load-report", new CmdLoadReport()); + addCommand("monitoring-metrics", new CmdMonitoringMetrics()); + addCommand("mbeans", new CmdDumpMBeans()); + addCommand("topics", new CmdTopics(), "destinations"); + addCommand("allocator-stats", new CmdAllocatorStats()); + addCommand("load-report", new CmdLoadReport()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokers.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokers.java index f1571e96c65c9..b85a784c3c2b8 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokers.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBrokers.java @@ -18,28 +18,28 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.common.naming.TopicVersion; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about brokers") +@Command(description = "Operations about brokers") public class CmdBrokers extends CmdBase { - @Parameters(commandDescription = "List active brokers of the cluster") + @Command(description = "List active brokers of the cluster") private class List extends CliCommand { - @Parameter(description = "cluster-name") - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; @Override void run() throws Exception { - String cluster = params == null ? null : getOneArgument(params); print(getAdmin().brokers().getActiveBrokers(cluster)); } } - @Parameters(commandDescription = "Get the information of the leader broker") + @Command(description = "Get the information of the leader broker") private class LeaderBroker extends CliCommand { @Override @@ -48,25 +48,25 @@ void run() throws Exception { } } - @Parameters(commandDescription = "List namespaces owned by the broker") + @Command(description = "List namespaces owned by the broker") private class Namespaces extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; - @Parameter(names = {"-u", "--url"}, description = "broker-url", required = true) + @Parameters(description = "cluster-name", arity = "1") + private String cluster; + + @Option(names = {"-u", "--url"}, description = "broker-url", required = true) private String brokerUrl; @Override void run() throws Exception { - String cluster = getOneArgument(params); print(getAdmin().brokers().getOwnedNamespaces(cluster, brokerUrl)); } } - @Parameters(commandDescription = "Update dynamic-serviceConfiguration of broker") + @Command(description = "Update dynamic-serviceConfiguration of broker") private class UpdateConfigurationCmd extends CliCommand { - @Parameter(names = {"-c", "--config"}, description = "service-configuration name", required = true) + @Option(names = {"-c", "--config"}, description = "service-configuration name", required = true) private String configName; - @Parameter(names = {"-v", "--value"}, description = "service-configuration value", required = true) + @Option(names = {"-v", "--value"}, description = "service-configuration value", required = true) private String configValue; @Override @@ -75,9 +75,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Delete dynamic-serviceConfiguration of broker") + @Command(description = "Delete dynamic-serviceConfiguration of broker") private class DeleteConfigurationCmd extends CliCommand { - @Parameter(names = {"-c", "--config"}, description = "service-configuration name", required = true) + @Option(names = {"-c", "--config"}, description = "service-configuration name", required = true) private String configName; @Override @@ -86,7 +86,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get all overridden dynamic-configuration values") + @Command(description = "Get all overridden dynamic-configuration values") private class GetAllConfigurationsCmd extends CliCommand { @Override @@ -95,7 +95,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get list of updatable configuration name") + @Command(description = "Get list of updatable configuration name") private class GetUpdatableConfigCmd extends CliCommand { @Override @@ -104,7 +104,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get runtime configuration values") + @Command(description = "Get runtime configuration values") private class GetRuntimeConfigCmd extends CliCommand { @Override @@ -113,7 +113,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get internal configuration information") + @Command(description = "Get internal configuration information") private class GetInternalConfigurationCmd extends CliCommand { @Override @@ -123,10 +123,10 @@ void run() throws Exception { } - @Parameters(commandDescription = "Run a health check against the broker") + @Command(description = "Run a health check against the broker") private class HealthcheckCmd extends CliCommand { - @Parameter(names = {"-tv", "--topic-version"}, description = "topic version V1 is default") + @Option(names = {"-tv", "--topic-version"}, description = "topic version V1 is default") private TopicVersion topicVersion; @Override @@ -137,15 +137,15 @@ void run() throws Exception { } - @Parameters(commandDescription = "Shutdown broker gracefully.") + @Command(description = "Shutdown broker gracefully.") private class ShutDownBrokerGracefully extends CliCommand { - @Parameter(names = {"--max-concurrent-unload-per-sec", "-m"}, + @Option(names = {"--max-concurrent-unload-per-sec", "-m"}, description = "Max concurrent unload per second, " + "if the value absent(value=0) means no concurrent limitation") private int maxConcurrentUnloadPerSec; - @Parameter(names = {"--forced-terminate-topic", "-f"}, description = "Force terminate all topics on Broker") + @Option(names = {"--forced-terminate-topic", "-f"}, description = "Force terminate all topics on Broker") private boolean forcedTerminateTopic; @Override @@ -156,7 +156,7 @@ void run() throws Exception { } - @Parameters(commandDescription = "Manually trigger backlogQuotaCheck") + @Command(description = "Manually trigger backlogQuotaCheck") private class BacklogQuotaCheckCmd extends CliCommand { @Override @@ -167,7 +167,7 @@ void run() throws Exception { } - @Parameters(commandDescription = "Get the version of the currently connected broker") + @Command(description = "Get the version of the currently connected broker") private class PulsarVersion extends CliCommand { @Override @@ -178,18 +178,18 @@ void run() throws Exception { public CmdBrokers(Supplier admin) { super("brokers", admin); - jcommander.addCommand("list", new List()); - jcommander.addCommand("leader-broker", new LeaderBroker()); - jcommander.addCommand("namespaces", new Namespaces()); - jcommander.addCommand("update-dynamic-config", new UpdateConfigurationCmd()); - jcommander.addCommand("delete-dynamic-config", new DeleteConfigurationCmd()); - jcommander.addCommand("list-dynamic-config", new GetUpdatableConfigCmd()); - jcommander.addCommand("get-all-dynamic-config", new GetAllConfigurationsCmd()); - jcommander.addCommand("get-internal-config", new GetInternalConfigurationCmd()); - jcommander.addCommand("get-runtime-config", new GetRuntimeConfigCmd()); - jcommander.addCommand("healthcheck", new HealthcheckCmd()); - jcommander.addCommand("backlog-quota-check", new BacklogQuotaCheckCmd()); - jcommander.addCommand("version", new PulsarVersion()); - jcommander.addCommand("shutdown", new ShutDownBrokerGracefully()); + addCommand("list", new List()); + addCommand("leader-broker", new LeaderBroker()); + addCommand("namespaces", new Namespaces()); + addCommand("update-dynamic-config", new UpdateConfigurationCmd()); + addCommand("delete-dynamic-config", new DeleteConfigurationCmd()); + addCommand("list-dynamic-config", new GetUpdatableConfigCmd()); + addCommand("get-all-dynamic-config", new GetAllConfigurationsCmd()); + addCommand("get-internal-config", new GetInternalConfigurationCmd()); + addCommand("get-runtime-config", new GetRuntimeConfigCmd()); + addCommand("healthcheck", new HealthcheckCmd()); + addCommand("backlog-quota-check", new BacklogQuotaCheckCmd()); + addCommand("version", new PulsarVersion()); + addCommand("shutdown", new ShutDownBrokerGracefully()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdClusters.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdClusters.java index 0ea56e4430951..14f9eeadbffb5 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdClusters.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdClusters.java @@ -19,13 +19,11 @@ package org.apache.pulsar.admin.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.google.common.collect.Sets; +import java.io.IOException; import java.util.Arrays; import java.util.function.Supplier; import java.util.stream.Collectors; -import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.admin.cli.utils.CmdUtils; import org.apache.pulsar.client.admin.PulsarAdmin; @@ -36,18 +34,21 @@ import org.apache.pulsar.common.policies.data.ClusterPolicies.ClusterUrl; import org.apache.pulsar.common.policies.data.FailureDomain; import org.apache.pulsar.common.policies.data.FailureDomainImpl; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about clusters") +@Command(description = "Operations about clusters") public class CmdClusters extends CmdBase { - @Parameters(commandDescription = "List the existing clusters") + @Command(description = "List the existing clusters") private class List extends CliCommand { + @Option(names = {"-c", "--current"}, + description = "Print the current cluster with (*)", required = false, defaultValue = "false") + private boolean current; - @Parameter(names = { "-c", "--current" }, - description = "Print the current cluster with (*)", required = false) - private boolean current = false; - - void run() throws PulsarAdminException { + void run() throws Exception { java.util.List clusters = getAdmin().clusters().getClusters(); String clusterName = getAdmin().brokers().getRuntimeConfigurations().get("clusterName"); final java.util.List result = clusters.stream().map(c -> @@ -57,29 +58,30 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the configuration data for the specified cluster") + @Command(description = "Get the configuration data for the specified cluster") private class Get extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - void run() throws PulsarAdminException { - String cluster = getOneArgument(params); + @Override + void run() throws Exception { print(getAdmin().clusters().getCluster(cluster)); } } - @Parameters(commandDescription = "Provisions a new cluster. This operation requires Pulsar super-user privileges") - private class Create extends ClusterDetailsCommand { + @Command(description = "Provisions a new cluster. This operation requires Pulsar super-user privileges") + private class Create extends CliCommand { + @ArgGroup(exclusive = false) + ClusterDetails clusterDetails = new ClusterDetails(); @Override - void runCmd() throws Exception { - String cluster = getOneArgument(params); - getAdmin().clusters().createCluster(cluster, clusterData); + void run() throws PulsarAdminException, IOException { + getAdmin().clusters().createCluster(clusterDetails.clusterName, clusterDetails.getClusterData()); } } - protected void validateClusterData(ClusterData clusterData) { + protected static void validateClusterData(ClusterData clusterData) { if (clusterData.isBrokerClientTlsEnabled()) { if (clusterData.isBrokerClientTlsEnabledWithKeyStore()) { if (StringUtils.isAnyBlank(clusterData.getBrokerClientTlsTrustStoreType(), @@ -93,29 +95,29 @@ protected void validateClusterData(ClusterData clusterData) { } } - @Parameters(commandDescription = "Update the configuration for a cluster") - private class Update extends ClusterDetailsCommand { + @Command(description = "Update the configuration for a cluster") + private class Update extends CliCommand { + @ArgGroup(exclusive = false) + ClusterDetails clusterDetails = new ClusterDetails(); @Override - void runCmd() throws Exception { - String cluster = getOneArgument(params); - getAdmin().clusters().updateCluster(cluster, clusterData); + void run() throws PulsarAdminException, IOException { + getAdmin().clusters().updateCluster(clusterDetails.clusterName, clusterDetails.getClusterData()); } } - @Parameters(commandDescription = "Deletes an existing cluster") + @Command(description = "Deletes an existing cluster") private class Delete extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", index = "0", arity = "1") + private String cluster; - @Parameter(names = { "-a", "--all" }, - description = "Delete all data (tenants) of the cluster", required = false) - private boolean deleteAll = false; + @Option(names = {"-a", "--all"}, + description = "Delete all data (tenants) of the cluster", required = false, defaultValue = "false") + private boolean deleteAll; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); - if (deleteAll) { for (String tenant : getAdmin().tenants().getTenants()) { for (String namespace : getAdmin().namespaces().getNamespaces(tenant)) { @@ -137,88 +139,88 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Update peer cluster names") + @Command(description = "Update peer cluster names") private class UpdatePeerClusters extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--peer-clusters", description = "Comma separated peer-cluster names " + @Option(names = "--peer-clusters", description = "Comma separated peer-cluster names " + "[Pass empty string \"\" to delete list]", required = true) private String peerClusterNames; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); java.util.LinkedHashSet clusters = StringUtils.isBlank(peerClusterNames) ? null : Sets.newLinkedHashSet(Arrays.asList(peerClusterNames.split(","))); getAdmin().clusters().updatePeerClusterNames(cluster, clusters); } } - @Parameters(commandDescription = "Get the cluster migration configuration data for the specified cluster") + @Command(description = "Get the cluster migration configuration data for the specified cluster") private class GetClusterMigration extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); - print(getAdmin().clusters().getClusterMigration(cluster)); + getAdmin().clusters().getClusterMigration(cluster); } } - @Parameters(commandDescription = "Update cluster migration") + @Command(description = "Update cluster migration") private class UpdateClusterMigration extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--migrated", description = "Is cluster migrated") + @Option(names = "--migrated", description = "Is cluster migrated") private boolean migrated; - @Parameter(names = "--service-url", description = "New migrated cluster service url") + @Option(names = "--service-url", description = "New migrated cluster service url") private String serviceUrl; - @Parameter(names = "--service-url-secure", + @Option(names = "--service-url-secure", description = "New migrated cluster service url secure") private String serviceUrlTls; - @Parameter(names = "--broker-url", description = "New migrated cluster broker service url") + @Option(names = "--broker-url", description = "New migrated cluster broker service url") private String brokerServiceUrl; - @Parameter(names = "--broker-url-secure", description = "New migrated cluster broker service url secure") + @Option(names = "--broker-url-secure", description = "New migrated cluster broker service url secure") private String brokerServiceUrlTls; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); ClusterUrl clusterUrl = new ClusterUrl(serviceUrl, serviceUrlTls, brokerServiceUrl, brokerServiceUrlTls); getAdmin().clusters().updateClusterMigration(cluster, migrated, clusterUrl); } } - @Parameters(commandDescription = "Get list of peer-clusters") + @Command(description = "Get list of peer-clusters") private class GetPeerClusters extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); print(getAdmin().clusters().getPeerClusterNames(cluster)); } } - @Parameters(commandDescription = "Create a new failure-domain for a cluster. updates it if already created.") + @Command(description = "Create a new failure-domain for a cluster. updates it if already created.") private class CreateFailureDomain extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--domain-name", description = "domain-name", required = true) + @Option(names = "--domain-name", description = "domain-name", required = true) private String domainName; - @Parameter(names = "--broker-list", description = "Comma separated broker list", required = false) + @Option(names = "--broker-list", description = "Comma separated broker list", required = false) private String brokerList; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); FailureDomain domain = FailureDomainImpl.builder() .brokers((isNotBlank(brokerList) ? Sets.newHashSet(brokerList.split(",")) : null)) .build(); @@ -226,19 +228,19 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Update failure-domain for a cluster. Creates a new one if not exist.") + @Command(description = "Update failure-domain for a cluster. Creates a new one if not exist.") private class UpdateFailureDomain extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--domain-name", description = "domain-name", required = true) + @Option(names = "--domain-name", description = "domain-name", required = true) private String domainName; - @Parameter(names = "--broker-list", description = "Comma separated broker list", required = false) + @Option(names = "--broker-list", description = "Comma separated broker list", required = false) private String brokerList; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); FailureDomain domain = FailureDomainImpl.builder() .brokers((isNotBlank(brokerList) ? Sets.newHashSet(brokerList.split(",")) : null)) .build(); @@ -246,160 +248,131 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Deletes an existing failure-domain") + @Command(description = "Deletes an existing failure-domain") private class DeleteFailureDomain extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--domain-name", description = "domain-name", required = true) + @Option(names = "--domain-name", description = "domain-name", required = true) private String domainName; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); getAdmin().clusters().deleteFailureDomain(cluster, domainName); } } - @Parameters(commandDescription = "List the existing failure-domains for a cluster") + @Command(description = "List the existing failure-domains for a cluster") private class ListFailureDomains extends CliCommand { + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(description = "cluster-name", required = true) - private java.util.List params; - + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); print(getAdmin().clusters().getFailureDomains(cluster)); } } - @Parameters(commandDescription = "Get the configuration brokers of a failure-domain") + @Command(description = "Get the configuration brokers of a failure-domain") private class GetFailureDomain extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private java.util.List params; + @Parameters(description = "cluster-name", arity = "1") + private String cluster; - @Parameter(names = "--domain-name", description = "domain-name", required = true) + @Option(names = "--domain-name", description = "domain-name", required = true) private String domainName; + @Override void run() throws PulsarAdminException { - String cluster = getOneArgument(params); print(getAdmin().clusters().getFailureDomain(cluster, domainName)); } } - /** - * Base command. - */ - @Getter - abstract class BaseCommand extends CliCommand { - @Override - void run() throws Exception { - try { - processArguments(); - } catch (Exception e) { - String chosenCommand = jcommander.getParsedCommand(); - getUsageFormatter().usage(chosenCommand); - throw e; - } - runCmd(); - } - - void processArguments() throws Exception { - } + private static class ClusterDetails { + @Parameters(description = "cluster-name", arity = "1") + protected String clusterName; - abstract void runCmd() throws Exception; - } - - abstract class ClusterDetailsCommand extends BaseCommand { - @Parameter(description = "cluster-name", required = true) - protected java.util.List params; - - @Parameter(names = "--url", description = "service-url", required = false) + @Option(names = "--url", description = "service-url", required = false) protected String serviceUrl; - @Parameter(names = "--url-secure", description = "service-url for secure connection", required = false) + @Option(names = "--url-secure", description = "service-url for secure connection", required = false) protected String serviceUrlTls; - @Parameter(names = "--broker-url", description = "broker-service-url", required = false) + @Option(names = "--broker-url", description = "broker-service-url", required = false) protected String brokerServiceUrl; - @Parameter(names = "--broker-url-secure", + @Option(names = "--broker-url-secure", description = "broker-service-url for secure connection", required = false) protected String brokerServiceUrlTls; - @Parameter(names = "--proxy-url", + @Option(names = "--proxy-url", description = "Proxy-service url when client would like to connect to broker via proxy.") protected String proxyServiceUrl; - @Parameter(names = "--auth-plugin", description = "authentication plugin", required = false) + @Option(names = "--auth-plugin", description = "authentication plugin", required = false) protected String authenticationPlugin; - @Parameter(names = "--auth-parameters", description = "authentication parameters", required = false) + @Option(names = "--auth-parameters", description = "authentication parameters", required = false) protected String authenticationParameters; - @Parameter(names = "--proxy-protocol", + @Option(names = "--proxy-protocol", description = "protocol to decide type of proxy routing eg: SNI", required = false) protected ProxyProtocol proxyProtocol; - @Parameter(names = "--tls-enable", description = "Enable tls connection", required = false) + @Option(names = "--tls-enable", description = "Enable tls connection", required = false) protected Boolean brokerClientTlsEnabled; - @Parameter(names = "--tls-allow-insecure", description = "Allow insecure tls connection", required = false) + @Option(names = "--tls-allow-insecure", description = "Allow insecure tls connection", required = false) protected Boolean tlsAllowInsecureConnection; - @Parameter(names = "--tls-enable-keystore", + @Option(names = "--tls-enable-keystore", description = "Whether use KeyStore type to authenticate", required = false) protected Boolean brokerClientTlsEnabledWithKeyStore; - @Parameter(names = "--tls-trust-store-type", + @Option(names = "--tls-trust-store-type", description = "TLS TrustStore type configuration for internal client eg: JKS", required = false) protected String brokerClientTlsTrustStoreType; - @Parameter(names = "--tls-trust-store", + @Option(names = "--tls-trust-store", description = "TLS TrustStore path for internal client", required = false) protected String brokerClientTlsTrustStore; - @Parameter(names = "--tls-trust-store-pwd", + @Option(names = "--tls-trust-store-pwd", description = "TLS TrustStore password for internal client", required = false) protected String brokerClientTlsTrustStorePassword; - @Parameter(names = "--tls-key-store-type", + @Option(names = "--tls-key-store-type", description = "TLS TrustStore type configuration for internal client eg: JKS", required = false) protected String brokerClientTlsKeyStoreType; - @Parameter(names = "--tls-key-store", + @Option(names = "--tls-key-store", description = "TLS KeyStore path for internal client", required = false) protected String brokerClientTlsKeyStore; - @Parameter(names = "--tls-key-store-pwd", + @Option(names = "--tls-key-store-pwd", description = "TLS KeyStore password for internal client", required = false) protected String brokerClientTlsKeyStorePassword; - @Parameter(names = "--tls-trust-certs-filepath", + @Option(names = "--tls-trust-certs-filepath", description = "path for the trusted TLS certificate file", required = false) protected String brokerClientTrustCertsFilePath; - @Parameter(names = "--tls-key-filepath", + @Option(names = "--tls-key-filepath", description = "path for the TLS private key file", required = false) protected String brokerClientKeyFilePath; - @Parameter(names = "--tls-certs-filepath", + @Option(names = "--tls-certs-filepath", description = "path for the TLS certificate file", required = false) protected String brokerClientCertificateFilePath; - @Parameter(names = "--listener-name", + @Option(names = "--listener-name", description = "listenerName when client would like to connect to cluster", required = false) protected String listenerName; - @Parameter(names = "--cluster-config-file", description = "The path to a YAML config file specifying the " + @Option(names = "--cluster-config-file", description = "The path to a YAML config file specifying the " + "cluster's configuration") protected String clusterConfigFile; - protected ClusterData clusterData; - - @Override - void processArguments() throws Exception { - super.processArguments(); - + protected ClusterData getClusterData() throws IOException { ClusterData.Builder builder; if (null != clusterConfigFile) { builder = CmdUtils.loadConfig(clusterConfigFile, ClusterDataImpl.ClusterDataImplBuilder.class); @@ -472,27 +445,29 @@ void processArguments() throws Exception { builder.listenerName(listenerName); } - this.clusterData = builder.build(); + ClusterData clusterData = builder.build(); validateClusterData(clusterData); + + return clusterData; } } public CmdClusters(Supplier admin) { super("clusters", admin); - jcommander.addCommand("get", new Get()); - jcommander.addCommand("create", new Create()); - jcommander.addCommand("update", new Update()); - jcommander.addCommand("delete", new Delete()); - jcommander.addCommand("list", new List()); - jcommander.addCommand("update-peer-clusters", new UpdatePeerClusters()); - jcommander.addCommand("get-cluster-migration", new GetClusterMigration()); - jcommander.addCommand("update-cluster-migration", new UpdateClusterMigration()); - jcommander.addCommand("get-peer-clusters", new GetPeerClusters()); - jcommander.addCommand("get-failure-domain", new GetFailureDomain()); - jcommander.addCommand("create-failure-domain", new CreateFailureDomain()); - jcommander.addCommand("update-failure-domain", new UpdateFailureDomain()); - jcommander.addCommand("delete-failure-domain", new DeleteFailureDomain()); - jcommander.addCommand("list-failure-domains", new ListFailureDomains()); + addCommand("get", new Get()); + addCommand("create", new Create()); + addCommand("update", new Update()); + addCommand("delete", new Delete()); + addCommand("list", new List()); + addCommand("update-peer-clusters", new UpdatePeerClusters()); + addCommand("get-cluster-migration", new GetClusterMigration()); + addCommand("update-cluster-migration", new UpdateClusterMigration()); + addCommand("get-peer-clusters", new GetPeerClusters()); + addCommand("get-failure-domain", new GetFailureDomain()); + addCommand("create-failure-domain", new CreateFailureDomain()); + addCommand("update-failure-domain", new UpdateFailureDomain()); + addCommand("delete-failure-domain", new DeleteFailureDomain()); + addCommand("list-failure-domains", new ListFailureDomains()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctionWorker.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctionWorker.java index 8fa2cbad955ef..cfb7f142d2458 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctionWorker.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctionWorker.java @@ -18,15 +18,15 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameters; import java.util.function.Supplier; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.api.PulsarClientException; +import picocli.CommandLine.Command; @Slf4j -@Parameters(commandDescription = "Operations to collect function-worker statistics") +@Command(description = "Operations to collect function-worker statistics") public class CmdFunctionWorker extends CmdBase { /** @@ -46,7 +46,7 @@ void processArguments() throws Exception { abstract void runCmd() throws Exception; } - @Parameters(commandDescription = "Dump all functions stats running on this broker") + @Command(description = "Dump all functions stats running on this broker") class FunctionsStats extends BaseCommand { @Override @@ -55,7 +55,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Dump metrics for Monitoring") + @Command(description = "Dump metrics for Monitoring") class CmdMonitoringMetrics extends BaseCommand { @Override @@ -64,7 +64,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get all workers belonging to this cluster") + @Command(description = "Get all workers belonging to this cluster") class GetCluster extends BaseCommand { @Override @@ -73,7 +73,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get the leader of the worker cluster") + @Command(description = "Get the leader of the worker cluster") class GetClusterLeader extends BaseCommand { @Override @@ -82,7 +82,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get the assignments of the functions across the worker cluster") + @Command(description = "Get the assignments of the functions across the worker cluster") class GetFunctionAssignments extends BaseCommand { @@ -92,7 +92,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Triggers a rebalance of functions to workers") + @Command(description = "Triggers a rebalance of functions to workers") class Rebalance extends BaseCommand { @Override @@ -104,12 +104,12 @@ void runCmd() throws Exception { public CmdFunctionWorker(Supplier admin) throws PulsarClientException { super("functions-worker", admin); - jcommander.addCommand("function-stats", new FunctionsStats()); - jcommander.addCommand("monitoring-metrics", new CmdMonitoringMetrics()); - jcommander.addCommand("get-cluster", new GetCluster()); - jcommander.addCommand("get-cluster-leader", new GetClusterLeader()); - jcommander.addCommand("get-function-assignments", new GetFunctionAssignments()); - jcommander.addCommand("rebalance", new Rebalance()); + addCommand("function-stats", new FunctionsStats()); + addCommand("monitoring-metrics", new CmdMonitoringMetrics()); + addCommand("get-cluster", new GetCluster()); + addCommand("get-cluster-leader", new GetClusterLeader()); + addCommand("get-function-assignments", new GetFunctionAssignments()); + addCommand("rebalance", new Rebalance()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctions.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctions.java index c94041df0ffaf..15b8fca076104 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctions.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdFunctions.java @@ -22,10 +22,6 @@ import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.apache.pulsar.common.naming.TopicName.DEFAULT_NAMESPACE; import static org.apache.pulsar.common.naming.TopicName.PUBLIC_TENANT; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.StringConverter; import com.google.common.annotations.VisibleForTesting; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -58,9 +54,11 @@ import org.apache.pulsar.common.functions.Utils; import org.apache.pulsar.common.functions.WindowConfig; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Slf4j -@Parameters(commandDescription = "Interface for managing Pulsar Functions " +@Command(description = "Interface for managing Pulsar Functions " + "(lightweight, Lambda-style compute processes that work with Pulsar)") public class CmdFunctions extends CmdBase { private final LocalRunner localRunner; @@ -88,13 +86,7 @@ public class CmdFunctions extends CmdBase { abstract class BaseCommand extends CliCommand { @Override void run() throws Exception { - try { - processArguments(); - } catch (Exception e) { - String chosenCommand = jcommander.getParsedCommand(); - getUsageFormatter().usage(chosenCommand); - throw e; - } + processArguments(); runCmd(); } @@ -108,21 +100,13 @@ void processArguments() throws Exception {} */ @Getter abstract class NamespaceCommand extends BaseCommand { - @Parameter(names = "--tenant", description = "The tenant of a Pulsar Function") + @Option(names = "--tenant", description = "The tenant of a Pulsar Function", + defaultValue = PUBLIC_TENANT) protected String tenant; - @Parameter(names = "--namespace", description = "The namespace of a Pulsar Function") + @Option(names = "--namespace", description = "The namespace of a Pulsar Function", + defaultValue = DEFAULT_NAMESPACE) protected String namespace; - - @Override - public void processArguments() { - if (tenant == null) { - tenant = PUBLIC_TENANT; - } - if (namespace == null) { - namespace = DEFAULT_NAMESPACE; - } - } } /** @@ -130,22 +114,22 @@ public void processArguments() { */ @Getter abstract class FunctionCommand extends BaseCommand { - @Parameter(names = "--fqfn", description = "The Fully Qualified Function Name (FQFN) for the function") + @Option(names = "--fqfn", description = "The Fully Qualified Function Name (FQFN) for the function") protected String fqfn; - @Parameter(names = "--tenant", description = "The tenant of a Pulsar Function") + @Option(names = "--tenant", description = "The tenant of a Pulsar Function", + defaultValue = PUBLIC_TENANT) protected String tenant; - @Parameter(names = "--namespace", description = "The namespace of a Pulsar Function") + @Option(names = "--namespace", description = "The namespace of a Pulsar Function", + defaultValue = DEFAULT_NAMESPACE) protected String namespace; - @Parameter(names = "--name", description = "The name of a Pulsar Function") + @Option(names = "--name", description = "The name of a Pulsar Function") protected String functionName; @Override void processArguments() throws Exception { - super.processArguments(); - boolean usesSetters = (null != tenant || null != namespace || null != functionName); boolean usesFqfn = (null != fqfn); @@ -183,214 +167,214 @@ void processArguments() throws Exception { */ @Getter abstract class FunctionDetailsCommand extends BaseCommand { - @Parameter(names = "--fqfn", description = "The Fully Qualified Function Name (FQFN) for the function" + @Option(names = "--fqfn", description = "The Fully Qualified Function Name (FQFN) for the function" + " #Java, Python") protected String fqfn; - @Parameter(names = "--tenant", description = "The tenant of a Pulsar Function #Java, Python, Go") + @Option(names = "--tenant", description = "The tenant of a Pulsar Function #Java, Python, Go", + defaultValue = PUBLIC_TENANT) protected String tenant; - @Parameter(names = "--namespace", description = "The namespace of a Pulsar Function #Java, Python, Go") + @Option(names = "--namespace", description = "The namespace of a Pulsar Function #Java, Python, Go", + defaultValue = DEFAULT_NAMESPACE) protected String namespace; - @Parameter(names = "--name", description = "The name of a Pulsar Function #Java, Python, Go") + @Option(names = "--name", description = "The name of a Pulsar Function #Java, Python, Go") protected String functionName; // for backwards compatibility purposes - @Parameter(names = "--className", description = "The class name of a Pulsar Function", hidden = true) + @Option(names = "--className", description = "The class name of a Pulsar Function", hidden = true) protected String deprecatedClassName; - @Parameter(names = "--classname", description = "The class name of a Pulsar Function #Java, Python") + @Option(names = "--classname", description = "The class name of a Pulsar Function #Java, Python") protected String className; - @Parameter(names = { "-t", "--function-type" }, description = "The built-in Pulsar Function type") + @Option(names = { "-t", "--function-type" }, description = "The built-in Pulsar Function type") protected String functionType; - @Parameter(names = "--cleanup-subscription", description = "Whether delete the subscription " + @Option(names = "--cleanup-subscription", description = "Whether delete the subscription " + "when function is deleted") protected Boolean cleanupSubscription; - @Parameter(names = "--jar", description = "Path to the JAR file for the function " + @Option(names = "--jar", description = "Path to the JAR file for the function " + "(if the function is written in Java). It also supports URL path [http/https/file " + "(file protocol assumes that file already exists on worker host)/function " - + "(package URL from packages management service)] from which worker can download the package. #Java", - listConverter = StringConverter.class) + + "(package URL from packages management service)] from which worker can download the package. #Java") protected String jarFile; - @Parameter(names = "--py", description = "Path to the main Python file/Python Wheel file for the function " + @Option(names = "--py", description = "Path to the main Python file/Python Wheel file for the function " + "(if the function is written in Python). It also supports URL path [http/https/file " + "(file protocol assumes that file already exists on worker host)/function " - + "(package URL from packages management service)] from which worker can download the package. #Python", - listConverter = StringConverter.class) + + "(package URL from packages management service)] from which worker can download the package. #Python") protected String pyFile; - @Parameter(names = "--go", description = "Path to the main Go executable binary for the function " + @Option(names = "--go", description = "Path to the main Go executable binary for the function " + "(if the function is written in Go). It also supports URL path [http/https/file " + "(file protocol assumes that file already exists on worker host)/function " + "(package URL from packages management service)] from which worker can download the package. #Go") protected String goFile; - @Parameter(names = {"-i", "--inputs"}, description = "The input topic or " + @Option(names = {"-i", "--inputs"}, description = "The input topic or " + "topics (multiple topics can be specified as a comma-separated list) of a Pulsar Function" + " #Java, Python, Go") protected String inputs; // for backwards compatibility purposes - @Parameter(names = "--topicsPattern", description = "TopicsPattern to consume from list of topics " + @Option(names = "--topicsPattern", description = "TopicsPattern to consume from list of topics " + "under a namespace that match the pattern. [--input] and [--topic-pattern] are mutually exclusive. " + "Add SerDe class name for a pattern in --custom-serde-inputs (supported for java fun only)", hidden = true) protected String deprecatedTopicsPattern; - @Parameter(names = "--topics-pattern", description = "The topic pattern to consume from a list of topics " + @Option(names = "--topics-pattern", description = "The topic pattern to consume from a list of topics " + "under a namespace that matches the pattern. [--input] and [--topics-pattern] are mutually " + "exclusive. Add SerDe class name for a pattern in --custom-serde-inputs (supported for java " + "functions only) #Java, Python") protected String topicsPattern; - @Parameter(names = {"-o", "--output"}, + @Option(names = {"-o", "--output"}, description = "The output topic of a Pulsar Function (If none is specified, no output is written)" + " #Java, Python, Go") protected String output; - @Parameter(names = "--producer-config", description = "The custom producer configuration (as a JSON string)" + @Option(names = "--producer-config", description = "The custom producer configuration (as a JSON string)" + " #Java") protected String producerConfig; // for backwards compatibility purposes - @Parameter(names = "--logTopic", + @Option(names = "--logTopic", description = "The topic to which the logs of a Pulsar Function are produced", hidden = true) protected String deprecatedLogTopic; - @Parameter(names = "--log-topic", description = "The topic to which the logs of a Pulsar Function are produced" + @Option(names = "--log-topic", description = "The topic to which the logs of a Pulsar Function are produced" + " #Java, Python, Go") protected String logTopic; - @Parameter(names = {"-st", "--schema-type"}, description = "The builtin schema type or " + @Option(names = {"-st", "--schema-type"}, description = "The builtin schema type or " + "custom schema class name to be used for messages output by the function #Java") protected String schemaType = ""; // for backwards compatibility purposes - @Parameter(names = "--customSerdeInputs", + @Option(names = "--customSerdeInputs", description = "The map of input topics to SerDe class names (as a JSON string)", hidden = true) protected String deprecatedCustomSerdeInputString; - @Parameter(names = "--custom-serde-inputs", + @Option(names = "--custom-serde-inputs", description = "The map of input topics to SerDe class names (as a JSON string) #Java, Python") protected String customSerdeInputString; - @Parameter(names = "--custom-schema-inputs", + @Option(names = "--custom-schema-inputs", description = "The map of input topics to Schema properties (as a JSON string) #Java, Python") protected String customSchemaInputString; - @Parameter(names = "--custom-schema-outputs", + @Option(names = "--custom-schema-outputs", description = "The map of input topics to Schema properties (as a JSON string) #Java") protected String customSchemaOutputString; - @Parameter(names = "--input-specs", + @Option(names = "--input-specs", description = "The map of inputs to custom configuration (as a JSON string) #Java, Python, Go") protected String inputSpecs; - @Parameter(names = "--input-type-class-name", + @Option(names = "--input-type-class-name", description = "The class name of input type class #Java, Python, Go") protected String inputTypeClassName; // for backwards compatibility purposes - @Parameter(names = "--outputSerdeClassName", + @Option(names = "--outputSerdeClassName", description = "The SerDe class to be used for messages output by the function", hidden = true) protected String deprecatedOutputSerdeClassName; - @Parameter(names = "--output-serde-classname", + @Option(names = "--output-serde-classname", description = "The SerDe class to be used for messages output by the function #Java, Python") protected String outputSerdeClassName; - @Parameter(names = "--output-type-class-name", + @Option(names = "--output-type-class-name", description = "The class name of output type class #Java, Python, Go") protected String outputTypeClassName; // for backwards compatibility purposes - @Parameter(names = "--functionConfigFile", description = "The path to a YAML config file that specifies " + @Option(names = "--functionConfigFile", description = "The path to a YAML config file that specifies " + "the configuration of a Pulsar Function", hidden = true) protected String deprecatedFnConfigFile; - @Parameter(names = "--function-config-file", + @Option(names = "--function-config-file", description = "The path to a YAML config file that specifies the configuration of a Pulsar Function" + " #Java, Python, Go") protected String fnConfigFile; // for backwards compatibility purposes - @Parameter(names = "--processingGuarantees", description = "The processing guarantees (aka delivery semantics) " + @Option(names = "--processingGuarantees", description = "The processing guarantees (aka delivery semantics) " + "applied to the function", hidden = true) protected FunctionConfig.ProcessingGuarantees deprecatedProcessingGuarantees; - @Parameter(names = "--processing-guarantees", + @Option(names = "--processing-guarantees", description = "The processing guarantees (as known as delivery semantics) applied to the function." + " Available values are: `ATLEAST_ONCE`, `ATMOST_ONCE`, `EFFECTIVELY_ONCE`." + " If it is not specified, the `ATLEAST_ONCE` delivery guarantee is used." + " #Java, Python, Go") protected FunctionConfig.ProcessingGuarantees processingGuarantees; // for backwards compatibility purposes - @Parameter(names = "--userConfig", description = "User-defined config key/values", hidden = true) + @Option(names = "--userConfig", description = "User-defined config key/values", hidden = true) protected String deprecatedUserConfigString; - @Parameter(names = "--user-config", description = "User-defined config key/values #Java, Python, Go") + @Option(names = "--user-config", description = "User-defined config key/values #Java, Python, Go") protected String userConfigString; - @Parameter(names = "--retainOrdering", + @Option(names = "--retainOrdering", description = "Function consumes and processes messages in order", hidden = true) protected Boolean deprecatedRetainOrdering; - @Parameter(names = "--retain-ordering", description = "Function consumes and processes messages in order #Java") + @Option(names = "--retain-ordering", description = "Function consumes and processes messages in order #Java") protected Boolean retainOrdering; - @Parameter(names = "--retain-key-ordering", + @Option(names = "--retain-key-ordering", description = "Function consumes and processes messages in key order #Java") protected Boolean retainKeyOrdering; - @Parameter(names = "--batch-builder", description = "BatcherBuilder provides two types of " + @Option(names = "--batch-builder", description = "BatcherBuilder provides two types of " + "batch construction methods, DEFAULT and KEY_BASED. The default value is: DEFAULT") protected String batchBuilder; - @Parameter(names = "--forward-source-message-property", description = "Forwarding input message's properties " - + "to output topic when processing (use false to disable it) #Java", arity = 1) + @Option(names = "--forward-source-message-property", description = "Forwarding input message's properties " + + "to output topic when processing (use false to disable it) #Java", arity = "1") protected Boolean forwardSourceMessageProperty = true; - @Parameter(names = "--subs-name", description = "Pulsar source subscription name if user wants a specific " + @Option(names = "--subs-name", description = "Pulsar source subscription name if user wants a specific " + "subscription-name for input-topic consumer #Java, Python, Go") protected String subsName; - @Parameter(names = "--subs-position", description = "Pulsar source subscription position if user wants to " + @Option(names = "--subs-position", description = "Pulsar source subscription position if user wants to " + "consume messages from the specified location #Java") protected SubscriptionInitialPosition subsPosition; - @Parameter(names = "--skip-to-latest", description = "Whether or not the consumer skip to latest message " - + "upon function instance restart", arity = 1) + @Option(names = "--skip-to-latest", description = "Whether or not the consumer skip to latest message " + + "upon function instance restart", arity = "1") protected Boolean skipToLatest; - @Parameter(names = "--parallelism", description = "The parallelism factor of a Pulsar Function " + @Option(names = "--parallelism", description = "The parallelism factor of a Pulsar Function " + "(i.e. the number of function instances to run) #Java") protected Integer parallelism; - @Parameter(names = "--cpu", description = "The cpu in cores that need to be allocated " + @Option(names = "--cpu", description = "The cpu in cores that need to be allocated " + "per function instance(applicable only to docker runtime) #Java(Process & K8s),Python(K8s),Go(K8s)") protected Double cpu; - @Parameter(names = "--ram", description = "The ram in bytes that need to be allocated " + @Option(names = "--ram", description = "The ram in bytes that need to be allocated " + "per function instance(applicable only to process/docker runtime)" + " #Java(Process & K8s),Python(K8s),Go(K8s)") protected Long ram; - @Parameter(names = "--disk", description = "The disk in bytes that need to be allocated " + @Option(names = "--disk", description = "The disk in bytes that need to be allocated " + "per function instance(applicable only to docker runtime) #Java(Process & K8s),Python(K8s),Go(K8s)") protected Long disk; // for backwards compatibility purposes - @Parameter(names = "--windowLengthCount", description = "The number of messages per window", hidden = true) + @Option(names = "--windowLengthCount", description = "The number of messages per window", hidden = true) protected Integer deprecatedWindowLengthCount; - @Parameter(names = "--window-length-count", description = "The number of messages per window #Java") + @Option(names = "--window-length-count", description = "The number of messages per window #Java") protected Integer windowLengthCount; // for backwards compatibility purposes - @Parameter(names = "--windowLengthDurationMs", + @Option(names = "--windowLengthDurationMs", description = "The time duration of the window in milliseconds", hidden = true) protected Long deprecatedWindowLengthDurationMs; - @Parameter(names = "--window-length-duration-ms", + @Option(names = "--window-length-duration-ms", description = "The time duration of the window in milliseconds #Java") protected Long windowLengthDurationMs; // for backwards compatibility purposes - @Parameter(names = "--slidingIntervalCount", + @Option(names = "--slidingIntervalCount", description = "The number of messages after which the window slides", hidden = true) protected Integer deprecatedSlidingIntervalCount; - @Parameter(names = "--sliding-interval-count", + @Option(names = "--sliding-interval-count", description = "The number of messages after which the window slides #Java") protected Integer slidingIntervalCount; // for backwards compatibility purposes - @Parameter(names = "--slidingIntervalDurationMs", + @Option(names = "--slidingIntervalDurationMs", description = "The time duration after which the window slides", hidden = true) protected Long deprecatedSlidingIntervalDurationMs; - @Parameter(names = "--sliding-interval-duration-ms", + @Option(names = "--sliding-interval-duration-ms", description = "The time duration after which the window slides #Java") protected Long slidingIntervalDurationMs; // for backwards compatibility purposes - @Parameter(names = "--autoAck", + @Option(names = "--autoAck", description = "Whether or not the framework acknowledges messages automatically", hidden = true) protected Boolean deprecatedAutoAck = null; - @Parameter(names = "--auto-ack", + @Option(names = "--auto-ack", description = "Whether or not the framework acknowledges messages automatically" - + " #Java, Python, Go", arity = 1) + + " #Java, Python, Go", arity = "1") protected Boolean autoAck; // for backwards compatibility purposes - @Parameter(names = "--timeoutMs", description = "The message timeout in milliseconds", hidden = true) + @Option(names = "--timeoutMs", description = "The message timeout in milliseconds", hidden = true) protected Long deprecatedTimeoutMs; - @Parameter(names = "--timeout-ms", description = "The message timeout in milliseconds #Java, Python") + @Option(names = "--timeout-ms", description = "The message timeout in milliseconds #Java, Python") protected Long timeoutMs; - @Parameter(names = "--max-message-retries", + @Option(names = "--max-message-retries", description = "How many times should we try to process a message before giving up #Java") protected Integer maxMessageRetries; - @Parameter(names = "--custom-runtime-options", description = "A string that encodes options to " + @Option(names = "--custom-runtime-options", description = "A string that encodes options to " + "customize the runtime, see docs for configured runtime for details #Java") protected String customRuntimeOptions; - @Parameter(names = "--secrets", description = "The map of secretName to an object that encapsulates " + @Option(names = "--secrets", description = "The map of secretName to an object that encapsulates " + "how the secret is fetched by the underlying secrets provider #Java, Python") protected String secretsString; - @Parameter(names = "--dead-letter-topic", + @Option(names = "--dead-letter-topic", description = "The topic where messages that are not processed successfully are sent to #Java") protected String deadLetterTopic; protected FunctionConfig functionConfig; @@ -447,7 +431,6 @@ private void mergeArgs() { @Override void processArguments() throws Exception { - super.processArguments(); // merge deprecated args with new args mergeArgs(); @@ -459,7 +442,15 @@ void processArguments() throws Exception { } if (null != fqfn) { - parseFullyQualifiedFunctionName(fqfn, functionConfig); + String[] args = fqfn.split("/"); + if (args.length != 3) { + throw new ParameterException("Fully qualified function names (FQFNs) must " + + "be of the form tenant/namespace/name"); + } else { + functionConfig.setTenant(args[0]); + functionConfig.setNamespace(args[1]); + functionConfig.setName(args[2]); + } } else { if (null != tenant) { functionConfig.setTenant(tenant); @@ -738,73 +729,73 @@ && isBlank(functionConfig.getGo())) { } } - @Parameters(commandDescription = "Run a Pulsar Function locally, rather than deploy to a Pulsar cluster)") + @Command(description = "Run a Pulsar Function locally, rather than deploy to a Pulsar cluster)") class LocalRunner extends FunctionDetailsCommand { // TODO: this should become BookKeeper URL and it should be fetched from Pulsar client. // for backwards compatibility purposes - @Parameter(names = "--stateStorageServiceUrl", description = "The URL for the state storage service " + @Option(names = "--stateStorageServiceUrl", description = "The URL for the state storage service " + "(the default is Apache BookKeeper)", hidden = true) protected String deprecatedStateStorageServiceUrl; - @Parameter(names = "--state-storage-service-url", description = "The URL for the state storage service " + @Option(names = "--state-storage-service-url", description = "The URL for the state storage service " + "(the default is Apache BookKeeper) #Java, Python") protected String stateStorageServiceUrl; // for backwards compatibility purposes - @Parameter(names = "--brokerServiceUrl", description = "The URL for Pulsar broker", hidden = true) + @Option(names = "--brokerServiceUrl", description = "The URL for Pulsar broker", hidden = true) protected String deprecatedBrokerServiceUrl; - @Parameter(names = "--broker-service-url", description = "The URL for Pulsar broker #Java, Python, Go") + @Option(names = "--broker-service-url", description = "The URL for Pulsar broker #Java, Python, Go") protected String brokerServiceUrl; - @Parameter(names = "--web-service-url", description = "The URL for Pulsar web service #Java, Python") + @Option(names = "--web-service-url", description = "The URL for Pulsar web service #Java, Python") protected String webServiceUrl = null; // for backwards compatibility purposes - @Parameter(names = "--clientAuthPlugin", description = "Client authentication plugin using " + @Option(names = "--clientAuthPlugin", description = "Client authentication plugin using " + "which function-process can connect to broker", hidden = true) protected String deprecatedClientAuthPlugin; - @Parameter(names = "--client-auth-plugin", + @Option(names = "--client-auth-plugin", description = "Client authentication plugin using which function-process can connect to broker" + " #Java, Python") protected String clientAuthPlugin; // for backwards compatibility purposes - @Parameter(names = "--clientAuthParams", description = "Client authentication param", hidden = true) + @Option(names = "--clientAuthParams", description = "Client authentication param", hidden = true) protected String deprecatedClientAuthParams; - @Parameter(names = "--client-auth-params", description = "Client authentication param #Java, Python") + @Option(names = "--client-auth-params", description = "Client authentication param #Java, Python") protected String clientAuthParams; // for backwards compatibility purposes - @Parameter(names = "--use_tls", description = "Use tls connection", hidden = true) + @Option(names = "--use_tls", description = "Use tls connection", hidden = true) protected Boolean deprecatedUseTls = null; - @Parameter(names = "--use-tls", description = "Use tls connection #Java, Python") + @Option(names = "--use-tls", description = "Use tls connection #Java, Python") protected boolean useTls; // for backwards compatibility purposes - @Parameter(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) + @Option(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) protected Boolean deprecatedTlsAllowInsecureConnection = null; - @Parameter(names = "--tls-allow-insecure", description = "Allow insecure tls connection #Java, Python") + @Option(names = "--tls-allow-insecure", description = "Allow insecure tls connection #Java, Python") protected boolean tlsAllowInsecureConnection; // for backwards compatibility purposes - @Parameter(names = "--hostname_verification_enabled", + @Option(names = "--hostname_verification_enabled", description = "Enable hostname verification", hidden = true) protected Boolean deprecatedTlsHostNameVerificationEnabled = null; - @Parameter(names = "--hostname-verification-enabled", description = "Enable hostname verification" + @Option(names = "--hostname-verification-enabled", description = "Enable hostname verification" + " #Java, Python") protected boolean tlsHostNameVerificationEnabled; // for backwards compatibility purposes - @Parameter(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) + @Option(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) protected String deprecatedTlsTrustCertFilePath; - @Parameter(names = "--tls-trust-cert-path", description = "tls trust cert file path #Java, Python") + @Option(names = "--tls-trust-cert-path", description = "tls trust cert file path #Java, Python") protected String tlsTrustCertFilePath; // for backwards compatibility purposes - @Parameter(names = "--instanceIdOffset", description = "Start the instanceIds from this offset", hidden = true) + @Option(names = "--instanceIdOffset", description = "Start the instanceIds from this offset", hidden = true) protected Integer deprecatedInstanceIdOffset = null; - @Parameter(names = "--instance-id-offset", description = "Start the instanceIds from this offset #Java, Python") + @Option(names = "--instance-id-offset", description = "Start the instanceIds from this offset #Java, Python") protected Integer instanceIdOffset = 0; - @Parameter(names = "--runtime", description = "either THREAD or PROCESS. Only applies for Java functions #Java") + @Option(names = "--runtime", description = "either THREAD or PROCESS. Only applies for Java functions #Java") protected String runtime; - @Parameter(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider" + @Option(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider" + " #Java, Python") protected String secretsProviderClassName; - @Parameter(names = "--secrets-provider-config", + @Option(names = "--secrets-provider-config", description = "Config that needs to be passed to secrets provider #Java, Python") protected String secretsProviderConfig; - @Parameter(names = "--metrics-port-start", description = "The starting port range for metrics server" + @Option(names = "--metrics-port-start", description = "The starting port range for metrics server" + " #Java, Python, Go") protected String metricsPortStart; @@ -847,7 +838,7 @@ void runCmd() throws Exception { localRunArgs.add("--functionConfig"); localRunArgs.add(new Gson().toJson(functionConfig)); for (Field field : this.getClass().getDeclaredFields()) { - if (field.getName().startsWith("DEPRECATED")) { + if (field.getName().toUpperCase().startsWith("DEPRECATED")) { continue; } if (field.getName().contains("$")) { @@ -865,7 +856,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Create a Pulsar Function in cluster mode (deploy it on a Pulsar cluster)") + @Command(description = "Create a Pulsar Function in cluster mode (deploy it on a Pulsar cluster)") class CreateFunction extends FunctionDetailsCommand { @Override void runCmd() throws Exception { @@ -883,7 +874,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Fetch information about a Pulsar Function") + @Command(description = "Fetch information about a Pulsar Function") class GetFunction extends FunctionCommand { @Override void runCmd() throws Exception { @@ -893,10 +884,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Check the current status of a Pulsar Function") + @Command(aliases = "getstatus", description = "Check the current status of a Pulsar Function") class GetFunctionStatus extends FunctionCommand { - @Parameter(names = "--instance-id", description = "The function instanceId " + @Option(names = "--instance-id", description = "The function instanceId " + "(Get-status of all instances if instance-id is not provided)") protected String instanceId; @@ -911,10 +902,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get the current stats of a Pulsar Function") + @Command(description = "Get the current stats of a Pulsar Function") class GetFunctionStats extends FunctionCommand { - @Parameter(names = "--instance-id", description = "The function instanceId " + @Option(names = "--instance-id", description = "The function instanceId " + "(Get-stats of all instances if instance-id is not provided)") protected String instanceId; @@ -930,10 +921,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Restart function instance") + @Command(description = "Restart function instance") class RestartFunction extends FunctionCommand { - @Parameter(names = "--instance-id", description = "The function instanceId " + @Option(names = "--instance-id", description = "The function instanceId " + "(restart all instances if instance-id is not provided)") protected String instanceId; @@ -953,10 +944,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Stops function instance") + @Command(description = "Stops function instance") class StopFunction extends FunctionCommand { - @Parameter(names = "--instance-id", description = "The function instanceId " + @Option(names = "--instance-id", description = "The function instanceId " + "(stop all instances if instance-id is not provided)") protected String instanceId; @@ -975,10 +966,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Starts a stopped function instance") + @Command(description = "Starts a stopped function instance") class StartFunction extends FunctionCommand { - @Parameter(names = "--instance-id", description = "The function instanceId " + @Option(names = "--instance-id", description = "The function instanceId " + "(start all instances if instance-id is not provided)") protected String instanceId; @@ -997,7 +988,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Delete a Pulsar Function that is running on a Pulsar cluster") + @Command(description = "Delete a Pulsar Function that is running on a Pulsar cluster") class DeleteFunction extends FunctionCommand { @Override void runCmd() throws Exception { @@ -1006,10 +997,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Update a Pulsar Function that has been deployed to a Pulsar cluster") + @Command(description = "Update a Pulsar Function that has been deployed to a Pulsar cluster") class UpdateFunction extends FunctionDetailsCommand { - @Parameter(names = "--update-auth-data", description = "Whether or not to update the auth data #Java, Python") + @Option(names = "--update-auth-data", description = "Whether or not to update the auth data #Java, Python") protected boolean updateAuthData; @Override @@ -1046,7 +1037,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "List all Pulsar Functions running under a specific tenant and namespace") + @Command(description = "List all Pulsar Functions running under a specific tenant and namespace") class ListFunctions extends NamespaceCommand { @Override void runCmd() throws Exception { @@ -1054,13 +1045,13 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Fetch the current state associated with a Pulsar Function") + @Command(description = "Fetch the current state associated with a Pulsar Function") class StateGetter extends FunctionCommand { - @Parameter(names = { "-k", "--key" }, description = "Key name of State") + @Option(names = {"-k", "--key"}, description = "Key name of State") private String key = null; - @Parameter(names = { "-w", "--watch" }, description = "Watch for changes in the value associated with a key " + @Option(names = {"-w", "--watch"}, description = "Watch for changes in the value associated with a key " + "for a Pulsar Function") private boolean watch = false; @@ -1089,10 +1080,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Put the state associated with a Pulsar Function") + @Command(description = "Put the state associated with a Pulsar Function") class StatePutter extends FunctionCommand { - @Parameter(names = { "-s", "--state" }, description = "The FunctionState that needs to be put", required = true) + @Option(names = {"-s", "--state"}, description = "The FunctionState that needs to be put", required = true) private String state = null; @Override @@ -1104,22 +1095,22 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Trigger the specified Pulsar Function with a supplied value") + @Command(description = "Trigger the specified Pulsar Function with a supplied value") class TriggerFunction extends FunctionCommand { // for backward compatibility purposes - @Parameter(names = "--triggerValue", + @Option(names = "--triggerValue", description = "The value with which you want to trigger the function", hidden = true) protected String deprecatedTriggerValue; - @Parameter(names = "--trigger-value", description = "The value with which you want to trigger the function") + @Option(names = "--trigger-value", description = "The value with which you want to trigger the function") protected String triggerValue; // for backward compatibility purposes - @Parameter(names = "--triggerFile", description = "The path to the file that contains the data with which " + @Option(names = "--triggerFile", description = "The path to the file that contains the data with which " + "you want to trigger the function", hidden = true) protected String deprecatedTriggerFile; - @Parameter(names = "--trigger-file", description = "The path to the file that contains the data with which " + @Option(names = "--trigger-file", description = "The path to the file that contains the data with which " + "you want to trigger the function") protected String triggerFile; - @Parameter(names = "--topic", description = "The specific topic name that the function consumes from that" + @Option(names = "--topic", description = "The specific topic name that the function consumes from that" + " you want to inject the data to") protected String topic; @@ -1145,23 +1136,20 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Upload File Data to Pulsar", hidden = true) + @Command(description = "Upload File Data to Pulsar") class UploadFunction extends BaseCommand { // for backward compatibility purposes - @Parameter( + @Option( names = "--sourceFile", - description = "The file whose contents need to be uploaded", - listConverter = StringConverter.class, hidden = true) + description = "The file whose contents need to be uploaded", hidden = true) protected String deprecatedSourceFile; - @Parameter( + @Option( names = "--source-file", - description = "The file whose contents need to be uploaded", - listConverter = StringConverter.class) + description = "The file whose contents need to be uploaded") protected String sourceFile; - @Parameter( + @Option( names = "--path", - description = "Path or functionPkgUrl where the contents need to be stored", - listConverter = StringConverter.class, required = true) + description = "Path or functionPkgUrl where the contents need to be stored", required = true) protected String path; private void mergeArgs() { @@ -1182,25 +1170,22 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Download File Data from Pulsar", hidden = true) + @Command(description = "Download File Data from Pulsar") class DownloadFunction extends FunctionCommand { // for backward compatibility purposes - @Parameter( + @Option( names = "--destinationFile", - description = "The file to store downloaded content", - listConverter = StringConverter.class, hidden = true) + description = "The file to store downloaded content", hidden = true) protected String deprecatedDestinationFile; - @Parameter( + @Option( names = "--destination-file", - description = "The file to store downloaded content", - listConverter = StringConverter.class) + description = "The file to store downloaded content") protected String destinationFile; - @Parameter( + @Option( names = "--path", - description = "Path or functionPkgUrl to store the content", - listConverter = StringConverter.class, required = false, hidden = true) + description = "Path or functionPkgUrl to store the content", required = false, hidden = true) protected String path; - @Parameter( + @Option( names = "--transform-function", description = "Download the transform Function of the connector") protected Boolean transformFunction = false; @@ -1235,7 +1220,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Reload the available built-in functions") + @Command(description = "Reload the available built-in functions") public class ReloadBuiltInFunctions extends CmdFunctions.BaseCommand { @Override @@ -1262,25 +1247,25 @@ public CmdFunctions(Supplier admin) throws PulsarClientException { restart = new RestartFunction(); stop = new StopFunction(); start = new StartFunction(); - jcommander.addCommand("localrun", getLocalRunner()); - jcommander.addCommand("create", getCreater()); - jcommander.addCommand("delete", getDeleter()); - jcommander.addCommand("update", getUpdater()); - jcommander.addCommand("get", getGetter()); - jcommander.addCommand("restart", getRestarter()); - jcommander.addCommand("stop", getStopper()); - jcommander.addCommand("start", getStarter()); + addCommand("localrun", getLocalRunner()); + addCommand("create", getCreater()); + addCommand("delete", getDeleter()); + addCommand("update", getUpdater()); + addCommand("get", getGetter()); + addCommand("restart", getRestarter()); + addCommand("stop", getStopper()); + addCommand("start", getStarter()); // TODO depecreate getstatus - jcommander.addCommand("status", getStatuser(), "getstatus"); - jcommander.addCommand("stats", getFunctionStats()); - jcommander.addCommand("list", getLister()); - jcommander.addCommand("querystate", getStateGetter()); - jcommander.addCommand("putstate", getStatePutter()); - jcommander.addCommand("trigger", getTriggerer()); - jcommander.addCommand("upload", getUploader()); - jcommander.addCommand("download", getDownloader()); - jcommander.addCommand("reload", new ReloadBuiltInFunctions()); - jcommander.addCommand("available-functions", new ListBuiltInFunctions()); + addCommand("status", getStatuser(), "getstatus"); + addCommand("stats", getFunctionStats()); + addCommand("list", getLister()); + addCommand("querystate", getStateGetter()); + addCommand("putstate", getStatePutter()); + addCommand("trigger", getTriggerer()); + addCommand("upload", getUploader()); + addCommand("download", getDownloader()); + addCommand("reload", new ReloadBuiltInFunctions()); + addCommand("available-functions", new ListBuiltInFunctions()); } @VisibleForTesting @@ -1358,27 +1343,15 @@ StartFunction getStarter() { return start; } - private void parseFullyQualifiedFunctionName(String fqfn, FunctionConfig functionConfig) { - String[] args = fqfn.split("/"); - if (args.length != 3) { - throw new ParameterException("Fully qualified function names (FQFNs) must " - + "be of the form tenant/namespace/name"); - } else { - functionConfig.setTenant(args[0]); - functionConfig.setNamespace(args[1]); - functionConfig.setName(args[2]); - } - } - - @Parameters(commandDescription = "Get the list of Pulsar Functions supported by Pulsar cluster") + @Command(description = "Get the list of Pulsar Functions supported by Pulsar cluster") public class ListBuiltInFunctions extends BaseCommand { @Override void runCmd() throws Exception { getAdmin().functions().getBuiltInFunctions() .forEach(function -> { - System.out.println(function.getName()); - System.out.println(WordUtils.wrap(function.getDescription(), 80)); - System.out.println("----------------------------------------"); + print(function.getName()); + print(WordUtils.wrap(function.getDescription(), 80)); + print("----------------------------------------"); }); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdGenerateDocument.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdGenerateDocument.java index cab037faf8ffc..3f728ca73ea69 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdGenerateDocument.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdGenerateDocument.java @@ -18,64 +18,42 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.DefaultUsageFormatter; -import com.beust.jcommander.IUsageFormatter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterDescription; -import com.beust.jcommander.Parameters; +import static org.apache.pulsar.internal.CommandDescriptionUtil.getArgDescription; +import static org.apache.pulsar.internal.CommandDescriptionUtil.getCommandDescription; import java.util.Arrays; +import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Properties; +import java.util.Set; import java.util.function.Supplier; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Model.ArgSpec; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Model.OptionSpec; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.Spec; @Getter -@Parameters(commandDescription = "Generate documents automatically.") +@Command(description = "Generate documents automatically.") @Slf4j public class CmdGenerateDocument extends CmdBase { - private final JCommander baseJcommander; - private final IUsageFormatter usageFormatter; - - private PulsarAdminTool tool; + @Spec + private CommandSpec pulsarAdminCommandSpec; public CmdGenerateDocument(Supplier admin) { super("documents", admin); - baseJcommander = new JCommander(); - usageFormatter = new DefaultUsageFormatter(baseJcommander); - try { - tool = new PulsarAdminTool(new Properties()); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.err.println(); - baseJcommander.usage(); - return; - } - for (Map.Entry> c : tool.commandMap.entrySet()) { - try { - if (!c.getKey().equals("documents") && c.getValue() != null) { - baseJcommander.addCommand( - c.getKey(), c.getValue().getConstructor(Supplier.class).newInstance(admin)); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.err.println(); - baseJcommander.usage(); - return; - } - } - jcommander.addCommand("generate", new GenerateDocument()); + addCommand("generate", new GenerateDocument()); } - @Parameters(commandDescription = "Generate document for modules") + @Command(description = "Generate document for modules") private class GenerateDocument extends CliCommand { - @Parameter(description = "Please specify the module name, if not, documents will be generated for all modules." + @Parameters(description = "Please specify the module name, if not, documents will be generated for all modules." + "Optional modules(clusters, tenants, brokers, broker-stats, namespaces, topics, schemas, bookies," + "functions, ns-isolation-policy, resource-quotas, functions, sources, sinks)") private java.util.List modules; @@ -84,13 +62,17 @@ private class GenerateDocument extends CliCommand { void run() throws PulsarAdminException { StringBuilder sb = new StringBuilder(); if (modules == null || modules.isEmpty()) { - baseJcommander.getCommands().forEach((k, v) -> - this.generateDocument(sb, k, v) + pulsarAdminCommandSpec.parent().subcommands().forEach((k, v) -> + this.generateDocument(sb, k, v) ); } else { - String module = getOneArgument(modules); - JCommander obj = baseJcommander.getCommands().get(module); - this.generateDocument(sb, module, obj); + modules.forEach(module -> { + CommandLine commandLine = pulsarAdminCommandSpec.parent().subcommands().get(module); + if (commandLine == null) { + return; + } + this.generateDocument(sb, module, commandLine); + }); } } @@ -99,21 +81,29 @@ private boolean needsLangSupport(String module, String subK) { return module.equals("functions") && Arrays.asList(langSupport).contains(subK); } - private void generateDocument(StringBuilder sb, String module, JCommander obj) { + private final Set generatedModule = new HashSet<>(); + + private void generateDocument(StringBuilder sb, String module, CommandLine obj) { + // Filter the deprecated command + if (generatedModule.contains(module)) { + return; + } + String commandName = obj.getCommandName(); + generatedModule.add(commandName); + sb.append("# ").append(module).append("\n\n"); - sb.append(usageFormatter.getCommandDescription(module)).append("\n"); + sb.append(getCommandDescription(obj)).append("\n"); sb.append("\n\n```shell\n") .append("$ pulsar-admin ").append(module).append(" subcommand") .append("\n```"); sb.append("\n\n"); - CmdBase cmdObj = (CmdBase) obj.getObjects().get(0); - cmdObj.jcommander.getCommands().forEach((subK, subV) -> { + obj.getSubcommands().forEach((subK, subV) -> { sb.append("\n\n## ").append(subK).append("\n\n"); - sb.append(cmdObj.getUsageFormatter().getCommandDescription(subK)).append("\n\n"); + sb.append(getCommandDescription(subV)).append("\n\n"); sb.append("**Command:**\n\n"); sb.append("```shell\n$ pulsar-admin ").append(module).append(" ") .append(subK).append(" options").append("\n```\n\n"); - List options = cmdObj.jcommander.getCommands().get(subK).getParameters(); + List options = obj.getCommandSpec().args(); if (options.size() > 0) { sb.append("**Options:**\n\n"); sb.append("|Flag|Description|Default|"); @@ -124,22 +114,23 @@ private void generateDocument(StringBuilder sb, String module, JCommander obj) { sb.append("\n|---|---|---|\n"); } } - options.stream().filter( - ele -> ele.getParameterAnnotation() == null - || !ele.getParameterAnnotation().hidden() - ).forEach((option) -> { - String[] descriptions = option.getDescription().replace("\n", " ").split(" #"); - sb.append("| `").append(option.getNames()) - .append("` | ").append(descriptions[0]) - .append("|").append(option.getDefault()).append("|"); - if (needsLangSupport(module, subK) && descriptions.length > 1) { - sb.append(descriptions[1]); - } - sb.append("|\n"); - } - ); + options.forEach(ele -> { + if (ele.hidden() || !(ele instanceof OptionSpec)) { + return; + } + + String argDescription = getArgDescription(ele); + String[] descriptions = argDescription.replace("\n", " ").split(" #"); + sb.append("| `").append(Arrays.toString(((OptionSpec) ele).names())) + .append("` | ").append(descriptions[0]) + .append("|").append(ele.defaultValue()).append("|"); + if (needsLangSupport(module, subK) && descriptions.length > 1) { + sb.append(descriptions[1]); + } + sb.append("|\n"); + }); + System.out.println(sb); }); - System.out.println(sb); } } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaceIsolationPolicy.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaceIsolationPolicy.java index 8de7ef500eaa0..e9896decd8c96 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaceIsolationPolicy.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaceIsolationPolicy.java @@ -18,17 +18,12 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.CommaParameterSplitter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.admin.cli.utils.NameValueParameterSplitter; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.policies.data.AutoFailoverPolicyData; @@ -37,46 +32,48 @@ import org.apache.pulsar.common.policies.data.BrokerNamespaceIsolationDataImpl; import org.apache.pulsar.common.policies.data.NamespaceIsolationData; import org.apache.pulsar.common.policies.data.NamespaceIsolationDataImpl; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about namespace isolation policy") +@Command(description = "Operations about namespace isolation policy") public class CmdNamespaceIsolationPolicy extends CmdBase { - @Parameters(commandDescription = "Create/Update a namespace isolation policy for a cluster. " + @Command(description = "Create/Update a namespace isolation policy for a cluster. " + "This operation requires Pulsar super-user privileges") private class SetPolicy extends CliCommand { - @Parameter(description = "cluster-name policy-name", required = true) - private List params; + @Parameters(description = "cluster-name", index = "0", arity = "1") + private String clusterName; + @Parameters(description = "policy-name", index = "1", arity = "1") + private String policyName; - @Parameter(names = "--namespaces", description = "comma separated namespaces-regex list", - required = true, splitter = CommaParameterSplitter.class) + @Option(names = "--namespaces", description = "comma separated namespaces-regex list", + required = true, split = ",") private List namespaces; - @Parameter(names = "--primary", description = "comma separated primary-broker-regex list. " + @Option(names = "--primary", description = "comma separated primary-broker-regex list. " + "In Pulsar, when namespaces (more specifically, namespace bundles) are assigned dynamically to " + "brokers, the namespace isolation policy limits the set of brokers that can be used for assignment. " + "Before topics are assigned to brokers, you can set the namespace isolation policy with a primary or " + "a secondary regex to select desired brokers. If no broker matches the specified regex, you cannot " + "create a topic. If there are not enough primary brokers, topics are assigned to secondary brokers. " + "If there are not enough secondary brokers, topics are assigned to other brokers which do not have " - + "any isolation policies.", required = true, splitter = CommaParameterSplitter.class) + + "any isolation policies.", required = true, split = ",") private List primary; - @Parameter(names = "--secondary", description = "comma separated secondary-broker-regex list", - required = false, splitter = CommaParameterSplitter.class) + @Option(names = "--secondary", description = "comma separated secondary-broker-regex list", + required = false, split = ",") private List secondary = new ArrayList(); // optional - @Parameter(names = "--auto-failover-policy-type", + @Option(names = "--auto-failover-policy-type", description = "auto failover policy type name ['min_available']", required = true) private String autoFailoverPolicyTypeName; - @Parameter(names = "--auto-failover-policy-params", + @Option(names = "--auto-failover-policy-params", description = "comma separated name=value auto failover policy parameters", - required = true, converter = NameValueParameterSplitter.class) + required = true, split = ",") private Map autoFailoverPolicyParams; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params, 0, 2); - String policyName = getOneArgument(params, 1, 2); - // validate and create the POJO NamespaceIsolationData namespaceIsolationData = createNamespaceIsolationData(namespaces, primary, secondary, autoFailoverPolicyTypeName, autoFailoverPolicyParams); @@ -85,15 +82,13 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "List all namespace isolation policies of a cluster. " + @Command(description = "List all namespace isolation policies of a cluster. " + "This operation requires Pulsar super-user privileges") private class GetAllPolicies extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private List params; + @Parameters(description = "cluster-name", arity = "1") + private String clusterName; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params); - Map policyMap = getAdmin().clusters().getNamespaceIsolationPolicies(clusterName); @@ -101,15 +96,13 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "List all brokers with namespace-isolation policies attached to it. " + @Command(description = "List all brokers with namespace-isolation policies attached to it. " + "This operation requires Pulsar super-user privileges") private class GetAllBrokersWithPolicies extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private List params; + @Parameters(description = "cluster-name", arity = "1") + private String clusterName; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params); - List brokers = getAdmin().clusters() .getBrokersWithNamespaceIsolationPolicy(clusterName); List data = new ArrayList<>(); @@ -118,19 +111,16 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get broker with namespace-isolation policies attached to it. " + @Command(description = "Get broker with namespace-isolation policies attached to it. " + "This operation requires Pulsar super-user privileges") private class GetBrokerWithPolicies extends CliCommand { - @Parameter(description = "cluster-name", required = true) - private List params; - - @Parameter(names = "--broker", + @Parameters(description = "cluster-name", arity = "1") + private String clusterName; + @Option(names = "--broker", description = "Broker-name to get namespace-isolation policies attached to it", required = true) private String broker; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params); - BrokerNamespaceIsolationDataImpl brokerData = (BrokerNamespaceIsolationDataImpl) getAdmin().clusters() .getBrokerWithNamespaceIsolationPolicy(clusterName, broker); @@ -138,16 +128,15 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get namespace isolation policy of a cluster. " + @Command(description = "Get namespace isolation policy of a cluster. " + "This operation requires Pulsar super-user privileges") private class GetPolicy extends CliCommand { - @Parameter(description = "cluster-name policy-name", required = true) - private List params; + @Parameters(description = "cluster-name", index = "0", arity = "1") + private String clusterName; + @Parameters(description = "policy-name", index = "1", arity = "1") + private String policyName; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params, 0, 2); - String policyName = getOneArgument(params, 1, 2); - NamespaceIsolationDataImpl nsIsolationData = (NamespaceIsolationDataImpl) getAdmin().clusters() .getNamespaceIsolationPolicy(clusterName, policyName); @@ -155,16 +144,15 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Delete namespace isolation policy of a cluster. " + @Command(description = "Delete namespace isolation policy of a cluster. " + "This operation requires Pulsar super-user privileges") private class DeletePolicy extends CliCommand { - @Parameter(description = "cluster-name policy-name", required = true) - private List params; + @Parameters(description = "cluster-name", index = "0", arity = "1") + private String clusterName; + @Parameters(description = "policy-name", index = "1", arity = "1") + private String policyName; void run() throws PulsarAdminException { - String clusterName = getOneArgument(params, 0, 2); - String policyName = getOneArgument(params, 1, 2); - getAdmin().clusters().deleteNamespaceIsolationPolicy(clusterName, policyName); } } @@ -251,12 +239,12 @@ private NamespaceIsolationData createNamespaceIsolationData(List namespa public CmdNamespaceIsolationPolicy(Supplier admin) { super("ns-isolation-policy", admin); - jcommander.addCommand("set", new SetPolicy()); - jcommander.addCommand("get", new GetPolicy()); - jcommander.addCommand("list", new GetAllPolicies()); - jcommander.addCommand("delete", new DeletePolicy()); - jcommander.addCommand("brokers", new GetAllBrokersWithPolicies()); - jcommander.addCommand("broker", new GetBrokerWithPolicies()); + addCommand("set", new SetPolicy()); + addCommand("get", new GetPolicy()); + addCommand("list", new GetAllPolicies()); + addCommand("delete", new DeletePolicy()); + addCommand("brokers", new GetAllBrokersWithPolicies()); + addCommand("broker", new GetBrokerWithPolicies()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaces.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaces.java index c825397b8d84c..da8929da97cca 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaces.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNamespaces.java @@ -18,10 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.CommaParameterSplitter; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -36,15 +32,10 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.admin.cli.utils.IOUtils; -import org.apache.pulsar.cli.converters.ByteUnitIntegerConverter; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; -import org.apache.pulsar.cli.converters.TimeUnitToMillisConverter; -import org.apache.pulsar.cli.converters.TimeUnitToSecondsConverter; -import org.apache.pulsar.cli.validators.IntegerMaxValueLongValidator; -import org.apache.pulsar.cli.validators.MinNegativeOneValidator; -import org.apache.pulsar.cli.validators.NonNegativeValueValidator; -import org.apache.pulsar.cli.validators.PositiveIntegerValueValidator; -import org.apache.pulsar.cli.validators.PositiveLongValueValidator; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToIntegerConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToSecondsConverter; import org.apache.pulsar.client.admin.ListNamespaceTopicsOptions; import org.apache.pulsar.client.admin.Mode; import org.apache.pulsar.client.admin.PulsarAdmin; @@ -74,25 +65,27 @@ import org.apache.pulsar.common.policies.data.SubscribeRate; import org.apache.pulsar.common.policies.data.SubscriptionAuthMode; import org.apache.pulsar.common.policies.data.TopicType; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about namespaces") +@Command(description = "Operations about namespaces") public class CmdNamespaces extends CmdBase { - @Parameters(commandDescription = "Get the namespaces for a tenant") + @Command(description = "Get the namespaces for a tenant") private class GetNamespacesPerProperty extends CliCommand { - @Parameter(description = "tenant-name", required = true) - private java.util.List params; + @Parameters(description = "tenant-name", arity = "1") + private String tenant; @Override void run() throws PulsarAdminException { - String tenant = getOneArgument(params); print(getAdmin().namespaces().getNamespaces(tenant)); } } - @Parameters(commandDescription = "Get the namespaces for a tenant in a cluster", hidden = true) + @Command(description = "Get the namespaces for a tenant in a cluster", hidden = true) private class GetNamespacesPerCluster extends CliCommand { - @Parameter(description = "tenant/cluster", required = true) - private java.util.List params; + @Parameters(description = "tenant/cluster", arity = "1") + private String params; @Override void run() throws PulsarAdminException { @@ -101,22 +94,22 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the list of topics for a namespace") + @Command(description = "Get the list of topics for a namespace") private class GetTopics extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"-m", "--mode"}, + @Option(names = {"-m", "--mode"}, description = "Allowed topic domain mode (persistent, non_persistent, all).") private Mode mode; - @Parameter(names = { "-ist", + @Option(names = { "-ist", "--include-system-topic" }, description = "Include system topic") private boolean includeSystemTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); ListNamespaceTopicsOptions options = ListNamespaceTopicsOptions.builder() .mode(mode) .includeSystemTopic(includeSystemTopic) @@ -125,59 +118,59 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the list of bundles for a namespace") + @Command(description = "Get the list of bundles for a namespace") private class GetBundles extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getBundles(namespace)); } } - @Parameters(commandDescription = "Get the list of destinations for a namespace", hidden = true) + @Command(description = "Get the list of destinations for a namespace", hidden = true) private class GetDestinations extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getTopics(namespace)); } } - @Parameters(commandDescription = "Get the configuration policies of a namespace") + @Command(description = "Get the configuration policies of a namespace") private class GetPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getPolicies(namespace)); } } - @Parameters(commandDescription = "Creates a new namespace") + @Command(description = "Creates a new namespace") private class Create extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--clusters", "-c" }, - description = "List of clusters this namespace will be assigned", required = false) + @Option(names = { "--clusters", "-c" }, + description = "List of clusters this namespace will be assigned", required = false, split = ",") private java.util.List clusters; - @Parameter(names = { "--bundles", "-b" }, description = "number of bundles to activate", required = false) + @Option(names = { "--bundles", "-b" }, description = "number of bundles to activate", required = false) private int numBundles = 0; private static final long MAX_BUNDLES = ((long) 1) << 32; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (numBundles < 0 || numBundles > MAX_BUNDLES) { throw new ParameterException( "Invalid number of bundles. Number of bundles has to be in the range of (0, 2^32]."); @@ -208,162 +201,162 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Deletes a namespace.") + @Command(description = "Deletes a namespace.") private class Delete extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Delete namespace forcefully by force deleting all topics under it") private boolean force = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().deleteNamespace(namespace, force); } } - @Parameters(commandDescription = "Grant permissions on a namespace") + @Command(description = "Grant permissions on a namespace") private class GrantPermissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = "--role", description = "Client role to which grant permissions", required = true) + @Option(names = "--role", description = "Client role to which grant permissions", required = true) private String role; - @Parameter(names = "--actions", description = "Actions to be granted (produce,consume,sources,sinks," - + "functions,packages)", required = true) + @Option(names = "--actions", description = "Actions to be granted (produce,consume,sources,sinks," + + "functions,packages)", required = true, split = ",") private List actions; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().grantPermissionOnNamespace(namespace, role, getAuthActions(actions)); } } - @Parameters(commandDescription = "Revoke permissions on a namespace") + @Command(description = "Revoke permissions on a namespace") private class RevokePermissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = "--role", description = "Client role to which revoke permissions", required = true) + @Option(names = "--role", description = "Client role to which revoke permissions", required = true) private String role; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().revokePermissionsOnNamespace(namespace, role); } } - @Parameters(commandDescription = "Get permissions to access subscription admin-api") + @Command(description = "Get permissions to access subscription admin-api") private class SubscriptionPermissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getPermissionOnSubscription(namespace)); } } - @Parameters(commandDescription = "Grant permissions to access subscription admin-api") + @Command(description = "Grant permissions to access subscription admin-api") private class GrantSubscriptionPermissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"-s", "--subscription"}, + @Option(names = {"-s", "--subscription"}, description = "Subscription name for which permission will be granted to roles", required = true) private String subscription; - @Parameter(names = {"-rs", "--roles"}, + @Option(names = {"-rs", "--roles"}, description = "Client roles to which grant permissions (comma separated roles)", - required = true, splitter = CommaParameterSplitter.class) + required = true, split = ",") private List roles; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().grantPermissionOnSubscription(namespace, subscription, Sets.newHashSet(roles)); } } - @Parameters(commandDescription = "Revoke permissions to access subscription admin-api") + @Command(description = "Revoke permissions to access subscription admin-api") private class RevokeSubscriptionPermissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"-s", "--subscription"}, description = "Subscription name for which permission " + @Option(names = {"-s", "--subscription"}, description = "Subscription name for which permission " + "will be revoked to roles", required = true) private String subscription; - @Parameter(names = {"-r", "--role"}, + @Option(names = {"-r", "--role"}, description = "Client role to which revoke permissions", required = true) private String role; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().revokePermissionOnSubscription(namespace, subscription, role); } } - @Parameters(commandDescription = "Get the permissions on a namespace") + @Command(description = "Get the permissions on a namespace") private class Permissions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getPermissions(namespace)); } } - @Parameters(commandDescription = "Set replication clusters for a namespace") + @Command(description = "Set replication clusters for a namespace") private class SetReplicationClusters extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--clusters", + @Option(names = { "--clusters", "-c" }, description = "Replication Cluster Ids list (comma separated values)", required = true) private String clusterIds; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); List clusters = Lists.newArrayList(clusterIds.split(",")); getAdmin().namespaces().setNamespaceReplicationClusters(namespace, Sets.newHashSet(clusters)); } } - @Parameters(commandDescription = "Get replication clusters for a namespace") + @Command(description = "Get replication clusters for a namespace") private class GetReplicationClusters extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getNamespaceReplicationClusters(namespace)); } } - @Parameters(commandDescription = "Set subscription types enabled for a namespace") + @Command(description = "Set subscription types enabled for a namespace") private class SetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." - + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true) + @Option(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." + + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true, split = ",") private List subTypes; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); Set types = new HashSet<>(); subTypes.forEach(s -> { SubscriptionType subType; @@ -379,167 +372,166 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get subscription types enabled for a namespace") + @Command(description = "Get subscription types enabled for a namespace") private class GetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getSubscriptionTypesEnabled(namespace)); } } - @Parameters(commandDescription = "Remove subscription types enabled for a namespace") + @Command(description = "Remove subscription types enabled for a namespace") private class RemoveSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeSubscriptionTypesEnabled(namespace); } } - @Parameters(commandDescription = "Set Message TTL for a namespace") + @Command(description = "Set Message TTL for a namespace") private class SetMessageTTL extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--messageTTL", "-ttl" }, + @Option(names = {"--messageTTL", "-ttl"}, description = "Message TTL in seconds (or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w). " + "When the value is set to `0`, TTL is disabled.", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = {NonNegativeValueValidator.class}) + converter = TimeUnitToSecondsConverter.class) private Long messageTTLInSecond; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setNamespaceMessageTTL(namespace, messageTTLInSecond.intValue()); } } - @Parameters(commandDescription = "Remove Message TTL for a namespace") + @Command(description = "Remove Message TTL for a namespace") private class RemoveMessageTTL extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeNamespaceMessageTTL(namespace); } } - @Parameters(commandDescription = "Get max subscriptions per topic for a namespace") + @Command(description = "Get max subscriptions per topic for a namespace") private class GetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxSubscriptionsPerTopic(namespace)); } } - @Parameters(commandDescription = "Set max subscriptions per topic for a namespace") + @Command(description = "Set max subscriptions per topic for a namespace") private class SetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-subscriptions-per-topic", "-m" }, description = "Max subscriptions per topic", + @Option(names = { "--max-subscriptions-per-topic", "-m" }, description = "Max subscriptions per topic", required = true) private int maxSubscriptionsPerTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxSubscriptionsPerTopic(namespace, maxSubscriptionsPerTopic); } } - @Parameters(commandDescription = "Remove max subscriptions per topic for a namespace") + @Command(description = "Remove max subscriptions per topic for a namespace") private class RemoveMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxSubscriptionsPerTopic(namespace); } } - @Parameters(commandDescription = "Set subscription expiration time for a namespace") + @Command(description = "Set subscription expiration time for a namespace") private class SetSubscriptionExpirationTime extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-t", "--time" }, description = "Subscription expiration time in minutes", required = true) + @Option(names = { "-t", "--time" }, description = "Subscription expiration time in minutes", required = true) private int expirationTime; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setSubscriptionExpirationTime(namespace, expirationTime); } } - @Parameters(commandDescription = "Remove subscription expiration time for a namespace") + @Command(description = "Remove subscription expiration time for a namespace") private class RemoveSubscriptionExpirationTime extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeSubscriptionExpirationTime(namespace); } } - @Parameters(commandDescription = "Set Anti-affinity group name for a namespace") + @Command(description = "Set Anti-affinity group name for a namespace") private class SetAntiAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--group", "-g" }, description = "Anti-affinity group name", required = true) + @Option(names = { "--group", "-g" }, description = "Anti-affinity group name", required = true) private String antiAffinityGroup; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setNamespaceAntiAffinityGroup(namespace, antiAffinityGroup); } } - @Parameters(commandDescription = "Get Anti-affinity group name for a namespace") + @Command(description = "Get Anti-affinity group name for a namespace") private class GetAntiAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getNamespaceAntiAffinityGroup(namespace)); } } - @Parameters(commandDescription = "Get Anti-affinity namespaces grouped with the given anti-affinity group name") + @Command(description = "Get Anti-affinity namespaces grouped with the given anti-affinity group name") private class GetAntiAffinityNamespaces extends CliCommand { - @Parameter(names = { "--tenant", + @Option(names = { "--tenant", "-p" }, description = "tenant is only used for authorization. " + "Client has to be admin of any of the tenant to access this api", required = false) private String tenant; - @Parameter(names = { "--cluster", "-c" }, description = "Cluster name", required = true) + @Option(names = { "--cluster", "-c" }, description = "Cluster name", required = true) private String cluster; - @Parameter(names = { "--group", "-g" }, description = "Anti-affinity group name", required = true) + @Option(names = { "--group", "-g" }, description = "Anti-affinity group name", required = true) private String antiAffinityGroup; @Override @@ -548,56 +540,56 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove Anti-affinity group name for a namespace") + @Command(description = "Remove Anti-affinity group name for a namespace") private class DeleteAntiAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().deleteNamespaceAntiAffinityGroup(namespace); } } - @Parameters(commandDescription = "Get Deduplication for a namespace") + @Command(description = "Get Deduplication for a namespace") private class GetDeduplication extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getDeduplicationStatus(namespace)); } } - @Parameters(commandDescription = "Remove Deduplication for a namespace") + @Command(description = "Remove Deduplication for a namespace") private class RemoveDeduplication extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeDeduplicationStatus(namespace); } } - @Parameters(commandDescription = "Enable or disable deduplication for a namespace") + @Command(description = "Enable or disable deduplication for a namespace") private class SetDeduplication extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable deduplication") + @Option(names = { "--enable", "-e" }, description = "Enable deduplication") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable deduplication") + @Option(names = { "--disable", "-d" }, description = "Disable deduplication") private boolean disable = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); @@ -606,28 +598,28 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Enable or disable autoTopicCreation for a namespace, overriding broker settings") + @Command(description = "Enable or disable autoTopicCreation for a namespace, overriding broker settings") private class SetAutoTopicCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable allowAutoTopicCreation on namespace") + @Option(names = { "--enable", "-e" }, description = "Enable allowAutoTopicCreation on namespace") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable allowAutoTopicCreation on namespace") + @Option(names = { "--disable", "-d" }, description = "Disable allowAutoTopicCreation on namespace") private boolean disable = false; - @Parameter(names = { "--type", "-t" }, description = "Type of topic to be auto-created. " + @Option(names = { "--type", "-t" }, description = "Type of topic to be auto-created. " + "Possible values: (partitioned, non-partitioned). Default value: non-partitioned") private String type = "non-partitioned"; - @Parameter(names = { "--num-partitions", "-n" }, description = "Default number of partitions of topic to " + @Option(names = { "--num-partitions", "-n" }, description = "Default number of partitions of topic to " + "be auto-created, applicable to partitioned topics only", required = false) private Integer defaultNumPartitions = null; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); type = type.toLowerCase().trim(); if (enable == disable) { @@ -654,42 +646,42 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get autoTopicCreation info for a namespace") + @Command(description = "Get autoTopicCreation info for a namespace") private class GetAutoTopicCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getAutoTopicCreation(namespace)); } } - @Parameters(commandDescription = "Remove override of autoTopicCreation for a namespace") + @Command(description = "Remove override of autoTopicCreation for a namespace") private class RemoveAutoTopicCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeAutoTopicCreation(namespace); } } - @Parameters(commandDescription = "Enable autoSubscriptionCreation for a namespace, overriding broker settings") + @Command(description = "Enable autoSubscriptionCreation for a namespace, overriding broker settings") private class SetAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable allowAutoSubscriptionCreation on namespace") + @Option(names = {"--enable", "-e"}, description = "Enable allowAutoSubscriptionCreation on namespace") private boolean enable = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setAutoSubscriptionCreation(namespace, AutoSubscriptionCreationOverride.builder() .allowAutoSubscriptionCreation(enable) @@ -697,58 +689,57 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the autoSubscriptionCreation for a namespace") + @Command(description = "Get the autoSubscriptionCreation for a namespace") private class GetAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getAutoSubscriptionCreation(namespace)); } } - @Parameters(commandDescription = "Remove override of autoSubscriptionCreation for a namespace") + @Command(description = "Remove override of autoSubscriptionCreation for a namespace") private class RemoveAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeAutoSubscriptionCreation(namespace); } } - @Parameters(commandDescription = "Remove the retention policy for a namespace") + @Command(description = "Remove the retention policy for a namespace") private class RemoveRetention extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeRetention(namespace); } } - @Parameters(commandDescription = "Set the retention policy for a namespace") + @Command(description = "Set the retention policy for a namespace") private class SetRetention extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--time", + @Option(names = { "--time", "-t" }, description = "Retention time with optional time unit suffix. " + "For example, 100m, 3h, 2d, 5w. " + "If the time unit is not specified, the default unit is seconds. For example, " + "-t 120 sets retention to 2 minutes. " + "0 means no retention and -1 means infinite time retention.", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = MinNegativeOneValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long retentionTimeInSec; - @Parameter(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + @Option(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + "For example, 4096, 10M, 16G, 3T. The size unit suffix character can be k/K, m/M, g/G, or t/T. " + "If the size unit suffix is not specified, the default unit is bytes. " + "0 or less than 1MB means no retention and -1 means infinite size retention", required = true, @@ -757,7 +748,7 @@ private class SetRetention extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); final int retentionTimeInMin = retentionTimeInSec != -1 ? (int) TimeUnit.SECONDS.toMinutes(retentionTimeInSec) : retentionTimeInSec.intValue(); @@ -769,28 +760,28 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the retention policy for a namespace") + @Command(description = "Get the retention policy for a namespace") private class GetRetention extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getRetention(namespace)); } } - @Parameters(commandDescription = "Set the bookie-affinity group name") + @Command(description = "Set the bookie-affinity group name") private class SetBookieAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--primary-group", + @Option(names = { "--primary-group", "-pg" }, description = "Bookie-affinity primary-groups (comma separated) name " + "where namespace messages should be written", required = true) private String bookieAffinityGroupNamePrimary; - @Parameter(names = { "--secondary-group", + @Option(names = { "--secondary-group", "-sg" }, description = "Bookie-affinity secondary-group (comma separated) name where namespace " + "messages should be written. If you want to verify whether there are enough bookies in groups, " + "use `--secondary-group` flag. Messages in this namespace are stored in secondary groups. " @@ -800,7 +791,7 @@ private class SetBookieAffinityGroup extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setBookieAffinityGroup(namespace, BookieAffinityGroupData.builder() .bookkeeperAffinityGroupPrimary(bookieAffinityGroupNamePrimary) @@ -809,70 +800,70 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set the bookie-affinity group name") + @Command(description = "Set the bookie-affinity group name") private class DeleteBookieAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().deleteBookieAffinityGroup(namespace); } } - @Parameters(commandDescription = "Get the bookie-affinity group name") + @Command(description = "Get the bookie-affinity group name") private class GetBookieAffinityGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getBookieAffinityGroup(namespace)); } } - @Parameters(commandDescription = "Get message TTL for a namespace") + @Command(description = "Get message TTL for a namespace") private class GetMessageTTL extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getNamespaceMessageTTL(namespace)); } } - @Parameters(commandDescription = "Get subscription expiration time for a namespace") + @Command(description = "Get subscription expiration time for a namespace") private class GetSubscriptionExpirationTime extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getSubscriptionExpirationTime(namespace)); } } - @Parameters(commandDescription = "Unload a namespace from the current serving broker") + @Command(description = "Unload a namespace from the current serving broker") private class Unload extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") private String bundle; - @Parameter(names = { "--destinationBroker", "-d" }, + @Option(names = { "--destinationBroker", "-d" }, description = "Target brokerWebServiceAddress to which the bundle has to be allocated to. " + "--destinationBroker cannot be set when --bundle is not specified.") private String destinationBroker; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (bundle == null) { @@ -886,38 +877,38 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Split a namespace-bundle from the current serving broker") + @Command(description = "Split a namespace-bundle from the current serving broker") private class SplitBundle extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--bundle", + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary} " + "(mutually exclusive with --bundle-type)", required = false) private String bundle; - @Parameter(names = { "--bundle-type", + @Option(names = { "--bundle-type", "-bt" }, description = "bundle type (mutually exclusive with --bundle)", required = false) private BundleType bundleType; - @Parameter(names = { "--unload", + @Option(names = { "--unload", "-u" }, description = "Unload newly split bundles after splitting old bundle", required = false) private boolean unload; - @Parameter(names = { "--split-algorithm-name", "-san" }, description = "Algorithm name for split " + @Option(names = { "--split-algorithm-name", "-san" }, description = "Algorithm name for split " + "namespace bundle. Valid options are: [range_equally_divide, topic_count_equally_divide, " + "specified_positions_divide, flow_or_qps_equally_divide]. Use broker side config if absent" , required = false) private String splitAlgorithmName; - @Parameter(names = { "--split-boundaries", + @Option(names = { "--split-boundaries", "-sb" }, description = "Specified split boundary for bundle split, will split one bundle " + "to multi bundles only works with specified_positions_divide algorithm", required = false) private List splitBoundaries; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (StringUtils.isBlank(bundle) && bundleType == null) { throw new ParameterException("Must pass one of the params: --bundle / --bundle-type"); } @@ -935,18 +926,18 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the positions for one or more topic(s) in a namespace bundle") + @Command(description = "Get the positions for one or more topic(s) in a namespace bundle") private class GetTopicHashPositions extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter( + @Option( names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary} format namespace bundle", required = false) private String bundle; - @Parameter( + @Option( names = { "--topic-list", "-tl" }, description = "The list of topics(both non-partitioned topic and partitioned topic) to get positions " + "in this bundle, if none topic provided, will get the positions of all topics in this bundle", @@ -955,7 +946,7 @@ private class GetTopicHashPositions extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (StringUtils.isBlank(bundle)) { throw new ParameterException("Must pass one of the params: --bundle "); } @@ -963,34 +954,34 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set message-dispatch-rate for all topics of the namespace") + @Command(description = "Set message-dispatch-rate for all topics of the namespace") private class SetDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)", required = false) private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))", required = false) private boolean relativeToPublishRate = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setDispatchRate(namespace, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -1001,106 +992,106 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove configured message-dispatch-rate for all topics of the namespace") + @Command(description = "Remove configured message-dispatch-rate for all topics of the namespace") private class RemoveDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeDispatchRate(namespace); } } - @Parameters(commandDescription = "Get configured message-dispatch-rate for all topics of the namespace " + @Command(description = "Get configured message-dispatch-rate for all topics of the namespace " + "(Disabled if value < 0)") private class GetDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getDispatchRate(namespace)); } } - @Parameters(commandDescription = "Set subscribe-rate per consumer for all topics of the namespace") + @Command(description = "Set subscribe-rate per consumer for all topics of the namespace") private class SetSubscribeRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--subscribe-rate", + @Option(names = { "--subscribe-rate", "-sr" }, description = "subscribe-rate (default -1 will be overwrite if not passed)", required = false) private int subscribeRate = -1; - @Parameter(names = { "--subscribe-rate-period", + @Option(names = { "--subscribe-rate-period", "-st" }, description = "subscribe-rate-period in second type " + "(default 30 second will be overwrite if not passed)", required = false) private int subscribeRatePeriodSec = 30; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setSubscribeRate(namespace, new SubscribeRate(subscribeRate, subscribeRatePeriodSec)); } } - @Parameters(commandDescription = "Get configured subscribe-rate per consumer for all topics of the namespace") + @Command(description = "Get configured subscribe-rate per consumer for all topics of the namespace") private class GetSubscribeRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getSubscribeRate(namespace)); } } - @Parameters(commandDescription = "Remove configured subscribe-rate per consumer for all topics of the namespace") + @Command(description = "Remove configured subscribe-rate per consumer for all topics of the namespace") private class RemoveSubscribeRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeSubscribeRate(namespace); } } - @Parameters(commandDescription = "Set subscription message-dispatch-rate for all subscription of the namespace") + @Command(description = "Set subscription message-dispatch-rate for all subscription of the namespace") private class SetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)", required = false) private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))", required = false) private boolean relativeToPublishRate = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setSubscriptionDispatchRate(namespace, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -1111,100 +1102,100 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove subscription configured message-dispatch-rate " + @Command(description = "Remove subscription configured message-dispatch-rate " + "for all topics of the namespace") private class RemoveSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeSubscriptionDispatchRate(namespace); } } - @Parameters(commandDescription = "Get subscription configured message-dispatch-rate for all topics of " + @Command(description = "Get subscription configured message-dispatch-rate for all topics of " + "the namespace (Disabled if value < 0)") private class GetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getSubscriptionDispatchRate(namespace)); } } - @Parameters(commandDescription = "Set publish-rate for all topics of the namespace") + @Command(description = "Set publish-rate for all topics of the namespace") private class SetPublishRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--msg-publish-rate", + @Option(names = { "--msg-publish-rate", "-m" }, description = "message-publish-rate (default -1 will be overwrite if not passed)", required = false) private int msgPublishRate = -1; - @Parameter(names = { "--byte-publish-rate", + @Option(names = { "--byte-publish-rate", "-b" }, description = "byte-publish-rate (default -1 will be overwrite if not passed)", required = false) private long bytePublishRate = -1; - @Override + @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setPublishRate(namespace, new PublishRate(msgPublishRate, bytePublishRate)); } } - @Parameters(commandDescription = "Remove publish-rate for all topics of the namespace") + @Command(description = "Remove publish-rate for all topics of the namespace") private class RemovePublishRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removePublishRate(namespace); } } - @Parameters(commandDescription = "Get configured message-publish-rate for all topics of the namespace " - + "(Disabled if value < 0)") + @Command(name = "get-publish-rate", + description = "Get configured message-publish-rate for all topics of the namespace (Disabled if value < 0)") private class GetPublishRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Override + @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getPublishRate(namespace)); } } - @Parameters(commandDescription = "Set replicator message-dispatch-rate for all topics of the namespace") + @Command(description = "Set replicator message-dispatch-rate for all topics of the namespace") private class SetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)", required = false) private int dispatchRatePeriodSec = 1; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setReplicatorDispatchRate(namespace, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -1214,65 +1205,65 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get replicator configured message-dispatch-rate for all topics of the namespace " + @Command(description = "Get replicator configured message-dispatch-rate for all topics of the namespace " + "(Disabled if value < 0)") private class GetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getReplicatorDispatchRate(namespace)); } } - @Parameters(commandDescription = "Remove replicator configured message-dispatch-rate " + @Command(description = "Remove replicator configured message-dispatch-rate " + "for all topics of the namespace") private class RemoveReplicatorDispatchRate extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeReplicatorDispatchRate(namespace); } } - @Parameters(commandDescription = "Get the backlog quota policies for a namespace") + @Command(description = "Get the backlog quota policies for a namespace") private class GetBacklogQuotaMap extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getBacklogQuotaMap(namespace)); } } - @Parameters(commandDescription = "Set a backlog quota policy for a namespace") + @Command(description = "Set a backlog quota policy for a namespace") private class SetBacklogQuota extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", + @Option(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", converter = ByteUnitToLongConverter.class) - private Long limit = 0L; + private Long limit; - @Parameter(names = { "-lt", "--limitTime" }, + @Option(names = { "-lt", "--limitTime" }, description = "Time limit in second (or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w), " + "non-positive number for disabling time limit.", converter = TimeUnitToSecondsConverter.class) private Long limitTimeInSec; - @Parameter(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. " + @Option(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. " + "Valid options are: [producer_request_hold, producer_exception, consumer_backlog_eviction]", required = true) private String policyStr; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + @Option(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + "destination_storage (default) and message_age. " + "destination_storage limits backlog by size. " + "message_age limits backlog by time, that is, message timestamp (broker or publish timestamp). " @@ -1298,7 +1289,7 @@ void run() throws PulsarAdminException { backlogQuotaTypeStr, Arrays.toString(BacklogQuota.BacklogQuotaType.values()))); } - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); BacklogQuota.Builder builder = BacklogQuota.builder().retentionPolicy(policy); if (backlogQuotaType == BacklogQuota.BacklogQuotaType.destination_storage) { @@ -1318,18 +1309,18 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove a backlog quota policy from a namespace") + @Command(description = "Remove a backlog quota policy from a namespace") private class RemoveBacklogQuota extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to remove. Valid options are: " + @Option(names = {"-t", "--type"}, description = "Backlog quota type to remove. Valid options are: " + "destination_storage, message_age") private String backlogQuotaTypeStr = BacklogQuota.BacklogQuotaType.destination_storage.name(); @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); BacklogQuota.BacklogQuotaType backlogQuotaType; try { backlogQuotaType = BacklogQuota.BacklogQuotaType.valueOf(backlogQuotaTypeStr); @@ -1341,56 +1332,56 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the persistence policies for a namespace") + @Command(description = "Get the persistence policies for a namespace") private class GetPersistence extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getPersistence(namespace)); } } - @Parameters(commandDescription = "Remove the persistence policies for a namespace") + @Command(description = "Remove the persistence policies for a namespace") private class RemovePersistence extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removePersistence(namespace); } } - @Parameters(commandDescription = "Set the persistence policies for a namespace") + @Command(description = "Set the persistence policies for a namespace") private class SetPersistence extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-e", + @Option(names = { "-e", "--bookkeeper-ensemble" }, description = "Number of bookies to use for a topic") private int bookkeeperEnsemble = 2; - @Parameter(names = { "-w", + @Option(names = { "-w", "--bookkeeper-write-quorum" }, description = "How many writes to make of each entry") private int bookkeeperWriteQuorum = 2; - @Parameter(names = { "-a", + @Option(names = { "-a", "--bookkeeper-ack-quorum" }, description = "Number of acks (guaranteed copies) to wait for each entry") private int bookkeeperAckQuorum = 2; - @Parameter(names = { "-r", + @Option(names = { "-r", "--ml-mark-delete-max-rate" }, description = "Throttling rate of mark-delete operation (0 means no throttle)") private double managedLedgerMaxMarkDeleteRate = 0; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (bookkeeperEnsemble <= 0 || bookkeeperWriteQuorum <= 0 || bookkeeperAckQuorum <= 0) { throw new ParameterException("[--bookkeeper-ensemble], [--bookkeeper-write-quorum] " + "and [--bookkeeper-ack-quorum] must greater than 0."); @@ -1403,18 +1394,18 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Clear backlog for a namespace") + @Command(description = "Clear backlog for a namespace") private class ClearBacklog extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--sub", "-s" }, description = "subscription name") + @Option(names = { "--sub", "-s" }, description = "subscription name") private String subscription; - @Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") private String bundle; - @Parameter(names = { "--force", "-force" }, description = "Whether to force clear backlog without prompt") + @Option(names = { "--force", "-force" }, description = "Whether to force clear backlog without prompt") private boolean force; @Override @@ -1426,7 +1417,7 @@ void run() throws PulsarAdminException, IOException { return; } } - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (subscription != null && bundle != null) { getAdmin().namespaces().clearNamespaceBundleBacklogForSubscription(namespace, bundle, subscription); } else if (subscription != null) { @@ -1439,20 +1430,20 @@ void run() throws PulsarAdminException, IOException { } } - @Parameters(commandDescription = "Unsubscribe the given subscription on all topics on a namespace") + @Command(description = "Unsubscribe the given subscription on all topics on a namespace") private class Unsubscribe extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--sub", "-s" }, description = "subscription name", required = true) + @Option(names = { "--sub", "-s" }, description = "subscription name", required = true) private String subscription; - @Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}") private String bundle; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (bundle != null) { getAdmin().namespaces().unsubscribeNamespaceBundle(namespace, bundle, subscription); } else { @@ -1462,20 +1453,20 @@ void run() throws Exception { } - @Parameters(commandDescription = "Enable or disable message encryption required for a namespace") + @Command(description = "Enable or disable message encryption required for a namespace") private class SetEncryptionRequired extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable message encryption required") + @Option(names = { "--enable", "-e" }, description = "Enable message encryption required") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable message encryption required") + @Option(names = { "--disable", "-d" }, description = "Disable message encryption required") private boolean disable = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); @@ -1484,91 +1475,90 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get encryption required for a namespace") + @Command(description = "Get encryption required for a namespace") private class GetEncryptionRequired extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getEncryptionRequiredStatus(namespace)); } } - @Parameters(commandDescription = "Get the delayed delivery policy for a namespace") + @Command(description = "Get the delayed delivery policy for a namespace") private class GetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getDelayedDelivery(namespace)); } } - @Parameters(commandDescription = "Remove delayed delivery policies from a namespace") + @Command(description = "Remove delayed delivery policies from a namespace") private class RemoveDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeDelayedDeliveryMessages(namespace); } } - @Parameters(commandDescription = "Get the inactive topic policy for a namespace") + @Command(description = "Get the inactive topic policy for a namespace") private class GetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getInactiveTopicPolicies(namespace)); } } - @Parameters(commandDescription = "Remove inactive topic policies from a namespace") + @Command(description = "Remove inactive topic policies from a namespace") private class RemoveInactiveTopicPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeInactiveTopicPolicies(namespace); } } - @Parameters(commandDescription = "Set the inactive topic policies on a namespace") + @Command(description = "Set the inactive topic policies on a namespace") private class SetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") + @Option(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") private boolean enableDeleteWhileInactive = false; - @Parameter(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") + @Option(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") private boolean disableDeleteWhileInactive = false; - @Parameter(names = {"--max-inactive-duration", "-t"}, description = "Max duration of topic inactivity in " + @Option(names = {"--max-inactive-duration", "-t"}, description = "Max duration of topic inactivity in " + "seconds, topics that are inactive for longer than this value will be deleted " + "(eg: 1s, 10s, 1m, 5h, 3d)", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = IntegerMaxValueLongValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long maxInactiveDurationInSeconds; - @Parameter(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + @Option(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + "[delete_when_no_subscriptions, delete_when_subscriptions_caught_up]", required = true) private String inactiveTopicDeleteMode; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enableDeleteWhileInactive == disableDeleteWhileInactive) { throw new ParameterException("Need to specify either enable-delete-while-inactive or " + "disable-delete-while-inactive"); @@ -1585,31 +1575,31 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set the delayed delivery policy on a namespace") + @Command(description = "Set the delayed delivery policy on a namespace") private class SetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") + @Option(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") + @Option(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") private boolean disable = false; - @Parameter(names = { "--time", "-t" }, description = "The tick time for when retrying on " + @Option(names = { "--time", "-t" }, description = "The tick time for when retrying on " + "delayed delivery messages, affecting the accuracy of the delivery time compared to " + "the scheduled time. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryTimeInMills = 1000L; - @Parameter(names = { "--maxDelay", "-md" }, + @Option(names = { "--maxDelay", "-md" }, description = "The max allowed delay for delayed delivery. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryMaxDelayInMillis = 0L; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); } @@ -1622,304 +1612,304 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set subscription auth mode on a namespace") + @Command(description = "Set subscription auth mode on a namespace") private class SetSubscriptionAuthMode extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-m", "--subscription-auth-mode" }, description = "Subscription authorization mode for " + @Option(names = { "-m", "--subscription-auth-mode" }, description = "Subscription authorization mode for " + "Pulsar policies. Valid options are: [None, Prefix]", required = true) private String mode; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setSubscriptionAuthMode(namespace, SubscriptionAuthMode.valueOf(mode)); } } - @Parameters(commandDescription = "Get subscriptionAuthMod for a namespace") + @Command(description = "Get subscriptionAuthMod for a namespace") private class GetSubscriptionAuthMode extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getSubscriptionAuthMode(namespace)); } } - @Parameters(commandDescription = "Get deduplicationSnapshotInterval for a namespace") + @Command(description = "Get deduplicationSnapshotInterval for a namespace") private class GetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getDeduplicationSnapshotInterval(namespace)); } } - @Parameters(commandDescription = "Remove deduplicationSnapshotInterval for a namespace") + @Command(description = "Remove deduplicationSnapshotInterval for a namespace") private class RemoveDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeDeduplicationSnapshotInterval(namespace); } } - @Parameters(commandDescription = "Set deduplicationSnapshotInterval for a namespace") + @Command(description = "Set deduplicationSnapshotInterval for a namespace") private class SetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--interval", "-i"} + @Option(names = {"--interval", "-i"} , description = "deduplicationSnapshotInterval for a namespace", required = true) private int interval; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setDeduplicationSnapshotInterval(namespace, interval); } } - @Parameters(commandDescription = "Get maxProducersPerTopic for a namespace") + @Command(description = "Get maxProducersPerTopic for a namespace") private class GetMaxProducersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxProducersPerTopic(namespace)); } } - @Parameters(commandDescription = "Remove max producers per topic for a namespace") + @Command(description = "Remove max producers per topic for a namespace") private class RemoveMaxProducersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxProducersPerTopic(namespace); } } - @Parameters(commandDescription = "Set maxProducersPerTopic for a namespace") + @Command(description = "Set maxProducersPerTopic for a namespace") private class SetMaxProducersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-producers-per-topic", "-p" }, + @Option(names = { "--max-producers-per-topic", "-p" }, description = "maxProducersPerTopic for a namespace", required = true) private int maxProducersPerTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxProducersPerTopic(namespace, maxProducersPerTopic); } } - @Parameters(commandDescription = "Get maxConsumersPerTopic for a namespace") + @Command(description = "Get maxConsumersPerTopic for a namespace") private class GetMaxConsumersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxConsumersPerTopic(namespace)); } } - @Parameters(commandDescription = "Set maxConsumersPerTopic for a namespace") + @Command(description = "Set maxConsumersPerTopic for a namespace") private class SetMaxConsumersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-consumers-per-topic", "-c" }, + @Option(names = { "--max-consumers-per-topic", "-c" }, description = "maxConsumersPerTopic for a namespace", required = true) private int maxConsumersPerTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxConsumersPerTopic(namespace, maxConsumersPerTopic); } } - @Parameters(commandDescription = "Remove max consumers per topic for a namespace") + @Command(description = "Remove max consumers per topic for a namespace") private class RemoveMaxConsumersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxConsumersPerTopic(namespace); } } - @Parameters(commandDescription = "Get maxConsumersPerSubscription for a namespace") + @Command(description = "Get maxConsumersPerSubscription for a namespace") private class GetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxConsumersPerSubscription(namespace)); } } - @Parameters(commandDescription = "Remove maxConsumersPerSubscription for a namespace") + @Command(description = "Remove maxConsumersPerSubscription for a namespace") private class RemoveMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxConsumersPerSubscription(namespace); } } - @Parameters(commandDescription = "Set maxConsumersPerSubscription for a namespace") + @Command(description = "Set maxConsumersPerSubscription for a namespace") private class SetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-consumers-per-subscription", "-c" }, + @Option(names = { "--max-consumers-per-subscription", "-c" }, description = "maxConsumersPerSubscription for a namespace", required = true) private int maxConsumersPerSubscription; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxConsumersPerSubscription(namespace, maxConsumersPerSubscription); } } - @Parameters(commandDescription = "Get maxUnackedMessagesPerConsumer for a namespace") + @Command(description = "Get maxUnackedMessagesPerConsumer for a namespace") private class GetMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxUnackedMessagesPerConsumer(namespace)); } } - @Parameters(commandDescription = "Set maxUnackedMessagesPerConsumer for a namespace") + @Command(description = "Set maxUnackedMessagesPerConsumer for a namespace") private class SetMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-unacked-messages-per-topic", "-c" }, + @Option(names = { "--max-unacked-messages-per-topic", "-c" }, description = "maxUnackedMessagesPerConsumer for a namespace", required = true) private int maxUnackedMessagesPerConsumer; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxUnackedMessagesPerConsumer(namespace, maxUnackedMessagesPerConsumer); } } - @Parameters(commandDescription = "Remove maxUnackedMessagesPerConsumer for a namespace") + @Command(description = "Remove maxUnackedMessagesPerConsumer for a namespace") private class RemoveMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxUnackedMessagesPerConsumer(namespace); } } - @Parameters(commandDescription = "Get maxUnackedMessagesPerSubscription for a namespace") + @Command(description = "Get maxUnackedMessagesPerSubscription for a namespace") private class GetMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxUnackedMessagesPerSubscription(namespace)); } } - @Parameters(commandDescription = "Set maxUnackedMessagesPerSubscription for a namespace") + @Command(description = "Set maxUnackedMessagesPerSubscription for a namespace") private class SetMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--max-unacked-messages-per-subscription", "-c" }, + @Option(names = {"--max-unacked-messages-per-subscription", "-c"}, description = "maxUnackedMessagesPerSubscription for a namespace", required = true) private int maxUnackedMessagesPerSubscription; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxUnackedMessagesPerSubscription(namespace, maxUnackedMessagesPerSubscription); } } - @Parameters(commandDescription = "Remove maxUnackedMessagesPerSubscription for a namespace") + @Command(description = "Remove maxUnackedMessagesPerSubscription for a namespace") private class RemoveMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxUnackedMessagesPerSubscription(namespace); } } - @Parameters(commandDescription = "Get compactionThreshold for a namespace") + @Command(description = "Get compactionThreshold for a namespace") private class GetCompactionThreshold extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getCompactionThreshold(namespace)); } } - @Parameters(commandDescription = "Remove compactionThreshold for a namespace") + @Command(description = "Remove compactionThreshold for a namespace") private class RemoveCompactionThreshold extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeCompactionThreshold(namespace); } } - @Parameters(commandDescription = "Set compactionThreshold for a namespace") + @Command(description = "Set compactionThreshold for a namespace") private class SetCompactionThreshold extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--threshold", "-t" }, + @Option(names = { "--threshold", "-t" }, description = "Maximum number of bytes in a topic backlog before compaction is triggered " + "(eg: 10M, 16G, 3T). 0 disables automatic compaction", required = true, @@ -1928,30 +1918,30 @@ private class SetCompactionThreshold extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setCompactionThreshold(namespace, threshold); } } - @Parameters(commandDescription = "Get offloadThreshold for a namespace") + @Command(description = "Get offloadThreshold for a namespace") private class GetOffloadThreshold extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print("offloadThresholdInBytes: " + getAdmin().namespaces().getOffloadThreshold(namespace)); print("offloadThresholdInSeconds: " + getAdmin().namespaces().getOffloadThresholdInSeconds(namespace)); } } - @Parameters(commandDescription = "Set offloadThreshold for a namespace") + @Command(description = "Set offloadThreshold for a namespace") private class SetOffloadThreshold extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--size", "-s" }, + @Option(names = { "--size", "-s" }, description = "Maximum number of bytes stored in the pulsar cluster for a topic before data will" + " start being automatically offloaded to longterm storage (eg: 10M, 16G, 3T, 100)." + " -1 falls back to the cluster's namespace default." @@ -1961,7 +1951,7 @@ private class SetOffloadThreshold extends CliCommand { converter = ByteUnitToLongConverter.class) private Long threshold = -1L; - @Parameter(names = {"--time", "-t"}, + @Option(names = {"--time", "-t"}, description = "Maximum number of seconds stored on the pulsar cluster for a topic" + " before the broker will start offloading to longterm storage (eg: 10m, 5h, 3d, 2w).", converter = TimeUnitToSecondsConverter.class) @@ -1969,20 +1959,20 @@ private class SetOffloadThreshold extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setOffloadThreshold(namespace, threshold); getAdmin().namespaces().setOffloadThresholdInSeconds(namespace, thresholdInSeconds); } } - @Parameters(commandDescription = "Get offloadDeletionLag, in minutes, for a namespace") + @Command(description = "Get offloadDeletionLag, in minutes, for a namespace") private class GetOffloadDeletionLag extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); Long lag = getAdmin().namespaces().getOffloadDeleteLagMs(namespace); if (lag != null) { System.out.println(TimeUnit.MINUTES.convert(lag, TimeUnit.MILLISECONDS) + " minute(s)"); @@ -1992,12 +1982,12 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set offloadDeletionLag for a namespace") + @Command(description = "Set offloadDeletionLag for a namespace") private class SetOffloadDeletionLag extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--lag", "-l" }, + @Option(names = { "--lag", "-l" }, description = "Duration to wait after offloading a ledger segment, before deleting the copy of that" + " segment from cluster local storage. (eg: 10m, 5h, 3d, 2w).", required = true, @@ -2006,53 +1996,53 @@ private class SetOffloadDeletionLag extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setOffloadDeleteLag(namespace, lagInSec, TimeUnit.SECONDS); } } - @Parameters(commandDescription = "Clear offloadDeletionLag for a namespace") + @Command(description = "Clear offloadDeletionLag for a namespace") private class ClearOffloadDeletionLag extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().clearOffloadDeleteLag(namespace); } } - @Parameters(commandDescription = "Get the schema auto-update strategy for a namespace", hidden = true) + @Command(description = "Get the schema auto-update strategy for a namespace") private class GetSchemaAutoUpdateStrategy extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); System.out.println(getAdmin().namespaces().getSchemaAutoUpdateCompatibilityStrategy(namespace) .toString().toUpperCase()); } } - @Parameters(commandDescription = "Set the schema auto-update strategy for a namespace", hidden = true) + @Command(description = "Set the schema auto-update strategy for a namespace") private class SetSchemaAutoUpdateStrategy extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--compatibility", "-c" }, + @Option(names = { "--compatibility", "-c" }, description = "Compatibility level required for new schemas created via a Producer. " + "Possible values (Full, Backward, Forward).") private String strategyParam = null; - @Parameter(names = { "--disabled", "-d" }, description = "Disable automatic schema updates") + @Option(names = { "--disabled", "-d" }, description = "Disable automatic schema updates") private boolean disabled = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); SchemaAutoUpdateCompatibilityStrategy strategy = null; String strategyStr = strategyParam != null ? strategyParam.toUpperCase() : ""; @@ -2073,25 +2063,25 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the schema compatibility strategy for a namespace") + @Command(description = "Get the schema compatibility strategy for a namespace") private class GetSchemaCompatibilityStrategy extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); System.out.println(getAdmin().namespaces().getSchemaCompatibilityStrategy(namespace) .toString().toUpperCase()); } } - @Parameters(commandDescription = "Set the schema compatibility strategy for a namespace") + @Command(description = "Set the schema compatibility strategy for a namespace") private class SetSchemaCompatibilityStrategy extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--compatibility", "-c" }, + @Option(names = { "--compatibility", "-c" }, description = "Compatibility level required for new schemas created via a Producer. " + "Possible values (FULL, BACKWARD, FORWARD, " + "UNDEFINED, BACKWARD_TRANSITIVE, " @@ -2102,7 +2092,7 @@ private class SetSchemaCompatibilityStrategy extends CliCommand { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); String strategyStr = strategyParam != null ? strategyParam.toUpperCase() : ""; SchemaCompatibilityStrategy strategy; @@ -2116,33 +2106,33 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the namespace whether allow auto update schema") + @Command(description = "Get the namespace whether allow auto update schema") private class GetIsAllowAutoUpdateSchema extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); System.out.println(getAdmin().namespaces().getIsAllowAutoUpdateSchema(namespace)); } } - @Parameters(commandDescription = "Set the namespace whether allow auto update schema") + @Command(description = "Set the namespace whether allow auto update schema") private class SetIsAllowAutoUpdateSchema extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable schema validation enforced") + @Option(names = { "--enable", "-e" }, description = "Enable schema validation enforced") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable schema validation enforced") + @Option(names = { "--disable", "-d" }, description = "Disable schema validation enforced") private boolean disable = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); @@ -2151,36 +2141,36 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the schema validation enforced") + @Command(description = "Get the schema validation enforced") private class GetSchemaValidationEnforced extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the namespace") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the namespace") private boolean applied = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); System.out.println(getAdmin().namespaces().getSchemaValidationEnforced(namespace, applied)); } } - @Parameters(commandDescription = "Set the schema whether open schema validation enforced") + @Command(description = "Set the schema whether open schema validation enforced") private class SetSchemaValidationEnforced extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--enable", "-e" }, description = "Enable schema validation enforced") + @Option(names = { "--enable", "-e" }, description = "Enable schema validation enforced") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable schema validation enforced") + @Option(names = { "--disable", "-d" }, description = "Disable schema validation enforced") private boolean disable = false; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); @@ -2189,103 +2179,100 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set the offload policies for a namespace") + @Command(description = "Set the offload policies for a namespace") private class SetOffloadPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter( + @Option( names = {"--driver", "-d"}, description = "Driver to use to offload old data to long term storage, " + "(Possible values: S3, aws-s3, google-cloud-storage, filesystem, azureblob)", required = true) private String driver; - @Parameter( + @Option( names = {"--region", "-r"}, description = "The long term storage region, " - + "default is s3ManagedLedgerOffloadRegion or gcsManagedLedgerOffloadRegion in broker.conf", + + "default is s3ManagedLedgerOffloadRegion or gcsManagedLedgerOffloadRegion in broker.conf", required = false) private String region; - @Parameter( + @Option( names = {"--bucket", "-b"}, description = "Bucket to place offloaded ledger into", required = false) private String bucket; - @Parameter( + @Option( names = {"--endpoint", "-e"}, description = "Alternative endpoint to connect to, " + "s3 default is s3ManagedLedgerOffloadServiceEndpoint in broker.conf", required = false) private String endpoint; - @Parameter( + @Option( names = {"--aws-id", "-i"}, description = "AWS Credential Id to use when using driver S3 or aws-s3", required = false) private String awsId; - @Parameter( + @Option( names = {"--aws-secret", "-s"}, description = "AWS Credential Secret to use when using driver S3 or aws-s3", required = false) private String awsSecret; - @Parameter( + @Option( names = {"--s3-role", "-ro"}, description = "S3 Role used for STSAssumeRoleSessionCredentialsProvider", required = false) private String s3Role; - @Parameter( + @Option( names = {"--s3-role-session-name", "-rsn"}, description = "S3 role session name used for STSAssumeRoleSessionCredentialsProvider", required = false) private String s3RoleSessionName; - @Parameter( + @Option( names = {"--maxBlockSize", "-mbs"}, description = "Max block size (eg: 32M, 64M), default is 64MB" + "s3 and google-cloud-storage requires this parameter", required = false, - converter = ByteUnitIntegerConverter.class, - validateValueWith = {PositiveIntegerValueValidator.class}) + converter = ByteUnitToIntegerConverter.class) private Integer maxBlockSizeInBytes = OffloadPoliciesImpl.DEFAULT_MAX_BLOCK_SIZE_IN_BYTES; - @Parameter( + @Option( names = {"--readBufferSize", "-rbs"}, description = "Read buffer size (eg: 1M, 5M), default is 1MB", required = false, - converter = ByteUnitIntegerConverter.class, - validateValueWith = {PositiveIntegerValueValidator.class}) + converter = ByteUnitToIntegerConverter.class) private Integer readBufferSizeInBytes = OffloadPoliciesImpl.DEFAULT_READ_BUFFER_SIZE_IN_BYTES; - @Parameter( + @Option( names = {"--offloadAfterElapsed", "-oae"}, description = "Delay time in Millis for deleting the bookkeeper ledger after offload " + "(or seconds,minutes,hours,days,weeks eg: 10s, 100m, 3h, 2d, 5w).", required = false, - converter = TimeUnitToMillisConverter.class, - validateValueWith = PositiveLongValueValidator.class) + converter = TimeUnitToMillisConverter.class) private Long offloadAfterElapsedInMillis = OffloadPoliciesImpl.DEFAULT_OFFLOAD_DELETION_LAG_IN_MILLIS; - @Parameter( + @Option( names = {"--offloadAfterThreshold", "-oat"}, description = "Offload after threshold size (eg: 1M, 5M)", required = false, converter = ByteUnitToLongConverter.class) private Long offloadAfterThresholdInBytes = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_BYTES; - @Parameter( + @Option( names = {"--offloadAfterThresholdInSeconds", "-oats"}, description = "Offload after threshold seconds (or minutes,hours,days,weeks eg: 100m, 3h, 2d, 5w).", required = false, converter = TimeUnitToSecondsConverter.class) private Long offloadThresholdInSeconds = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_SECONDS; - @Parameter( + @Option( names = {"--offloadedReadPriority", "-orp"}, description = "Read priority for offloaded messages. By default, once messages are offloaded to " + "long-term storage, brokers read messages from long-term storage, but messages can " @@ -2311,7 +2298,7 @@ public boolean isS3Driver(String driver) { @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (!driverSupported(driver)) { throw new ParameterException("The driver " + driver + " is not supported, " @@ -2347,104 +2334,104 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the offload policies for a namespace") + @Command(description = "Remove the offload policies for a namespace") private class RemoveOffloadPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeOffloadPolicies(namespace); } } - @Parameters(commandDescription = "Get the offload policies for a namespace") + @Command(description = "Get the offload policies for a namespace") private class GetOffloadPolicies extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getOffloadPolicies(namespace)); } } - @Parameters(commandDescription = "Set max topics per namespace") + @Command(description = "Set max topics per namespace") private class SetMaxTopicsPerNamespace extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--max-topics-per-namespace", "-t"}, + @Option(names = {"--max-topics-per-namespace", "-t"}, description = "max topics per namespace", required = true) private int maxTopicsPerNamespace; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setMaxTopicsPerNamespace(namespace, maxTopicsPerNamespace); } } - @Parameters(commandDescription = "Get max topics per namespace") + @Command(description = "Get max topics per namespace") private class GetMaxTopicsPerNamespace extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getMaxTopicsPerNamespace(namespace)); } } - @Parameters(commandDescription = "Remove max topics per namespace") + @Command(description = "Remove max topics per namespace") private class RemoveMaxTopicsPerNamespace extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeMaxTopicsPerNamespace(namespace); } } - @Parameters(commandDescription = "Set property for a namespace") + @Command(description = "Set property for a namespace") private class SetPropertyForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--key", "-k"}, description = "Key of the property", required = true) + @Option(names = {"--key", "-k"}, description = "Key of the property", required = true) private String key; - @Parameter(names = {"--value", "-v"}, description = "Value of the property", required = true) + @Option(names = {"--value", "-v"}, description = "Value of the property", required = true) private String value; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setProperty(namespace, key, value); } } - @Parameters(commandDescription = "Set properties of a namespace") + @Command(description = "Set properties of a namespace") private class SetPropertiesForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--properties", "-p"}, description = "key value pair properties(a=a,b=b,c=c)", + @Option(names = {"--properties", "-p"}, description = "key value pair properties(a=a,b=b,c=c)", required = true) private java.util.List properties; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); if (properties.size() == 0) { throw new ParameterException(String.format("Required at least one property for the namespace, " + "but found %d.", properties.size())); @@ -2454,386 +2441,387 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get property for a namespace") + @Command(description = "Get property for a namespace") private class GetPropertyForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--key", "-k"}, description = "Key of the property", required = true) + @Option(names = {"--key", "-k"}, description = "Key of the property", required = true) private String key; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getProperty(namespace, key)); } } - @Parameters(commandDescription = "Get properties of a namespace") + @Command(description = "Get properties of a namespace") private class GetPropertiesForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws Exception { - final String namespace = validateNamespace(params); + final String namespace = validateNamespace(namespaceName); final Map properties = getAdmin().namespaces().getProperties(namespace); prettyPrint(properties); } } - @Parameters(commandDescription = "Remove property for a namespace") + @Command(description = "Remove property for a namespace") private class RemovePropertyForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"--key", "-k"}, description = "Key of the property", required = true) + @Option(names = {"--key", "-k"}, description = "Key of the property", required = true) private String key; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().removeProperty(namespace, key)); } } - @Parameters(commandDescription = "Clear all properties for a namespace") + @Command(description = "Clear all properties for a namespace") private class ClearPropertiesForNamespace extends CliCommand { - @Parameter(description = "tenant/namespace\n", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws Exception { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().clearProperties(namespace); } } - @Parameters(commandDescription = "Get ResourceGroup for a namespace") + @Command(description = "Get ResourceGroup for a namespace") private class GetResourceGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getNamespaceResourceGroup(namespace)); } } - @Parameters(commandDescription = "Set ResourceGroup for a namespace") + @Command(description = "Set ResourceGroup for a namespace") private class SetResourceGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--resource-group-name", "-rgn" }, description = "ResourceGroup name", required = true) + @Option(names = {"--resource-group-name", "-rgn"}, description = "ResourceGroup name", required = true) private String rgName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setNamespaceResourceGroup(namespace, rgName); } } - @Parameters(commandDescription = "Remove ResourceGroup from a namespace") + @Command(description = "Remove ResourceGroup from a namespace") private class RemoveResourceGroup extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeNamespaceResourceGroup(namespace); } } - @Parameters(commandDescription = "Update migration state for a namespace") + + @Command(description = "Update migration state for a namespace") private class UpdateMigrationState extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = "--migrated", description = "Is namespace migrated") + @Option(names = "--migrated", description = "Is namespace migrated") private boolean migrated; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().updateMigrationState(namespace, migrated); } } - @Parameters(commandDescription = "Get entry filters for a namespace") + @Command(description = "Get entry filters for a namespace") private class GetEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getNamespaceEntryFilters(namespace)); } } - @Parameters(commandDescription = "Set entry filters for a namespace") + @Command(description = "Set entry filters for a namespace") private class SetEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "--entry-filters-name", "-efn" }, + @Option(names = { "--entry-filters-name", "-efn" }, description = "The class name for the entry filter.", required = true) - private String entryFiltersName = ""; + private String entryFiltersName = ""; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setNamespaceEntryFilters(namespace, new EntryFilters(entryFiltersName)); } } - @Parameters(commandDescription = "Remove entry filters for a namespace") + @Command(description = "Remove entry filters for a namespace") private class RemoveEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeNamespaceEntryFilters(namespace); } } - @Parameters(commandDescription = "Enable dispatcherPauseOnAckStatePersistent for a namespace") + @Command(description = "Enable dispatcherPauseOnAckStatePersistent for a namespace") private class SetDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().setDispatcherPauseOnAckStatePersistent(namespace); } } - @Parameters(commandDescription = "Get the dispatcherPauseOnAckStatePersistent for a namespace") + @Command(description = "Get the dispatcherPauseOnAckStatePersistent for a namespace") private class GetDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getAdmin().namespaces().getDispatcherPauseOnAckStatePersistent(namespace)); } } - @Parameters(commandDescription = "Remove dispatcherPauseOnAckStatePersistent for a namespace") + @Command(description = "Remove dispatcherPauseOnAckStatePersistent for a namespace") private class RemoveDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); getAdmin().namespaces().removeDispatcherPauseOnAckStatePersistent(namespace); } } public CmdNamespaces(Supplier admin) { super("namespaces", admin); - jcommander.addCommand("list", new GetNamespacesPerProperty()); - jcommander.addCommand("list-cluster", new GetNamespacesPerCluster()); + addCommand("list", new GetNamespacesPerProperty()); + addCommand("list-cluster", new GetNamespacesPerCluster()); - jcommander.addCommand("topics", new GetTopics()); - jcommander.addCommand("bundles", new GetBundles()); - jcommander.addCommand("destinations", new GetDestinations()); - jcommander.addCommand("policies", new GetPolicies()); - jcommander.addCommand("create", new Create()); - jcommander.addCommand("delete", new Delete()); + addCommand("topics", new GetTopics()); + addCommand("bundles", new GetBundles()); + addCommand("destinations", new GetDestinations()); + addCommand("policies", new GetPolicies()); + addCommand("create", new Create()); + addCommand("delete", new Delete()); - jcommander.addCommand("permissions", new Permissions()); - jcommander.addCommand("grant-permission", new GrantPermissions()); - jcommander.addCommand("revoke-permission", new RevokePermissions()); + addCommand("permissions", new Permissions()); + addCommand("grant-permission", new GrantPermissions()); + addCommand("revoke-permission", new RevokePermissions()); - jcommander.addCommand("subscription-permission", new SubscriptionPermissions()); - jcommander.addCommand("grant-subscription-permission", new GrantSubscriptionPermissions()); - jcommander.addCommand("revoke-subscription-permission", new RevokeSubscriptionPermissions()); + addCommand("subscription-permission", new SubscriptionPermissions()); + addCommand("grant-subscription-permission", new GrantSubscriptionPermissions()); + addCommand("revoke-subscription-permission", new RevokeSubscriptionPermissions()); - jcommander.addCommand("set-clusters", new SetReplicationClusters()); - jcommander.addCommand("get-clusters", new GetReplicationClusters()); + addCommand("set-clusters", new SetReplicationClusters()); + addCommand("get-clusters", new GetReplicationClusters()); - jcommander.addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); - jcommander.addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); - jcommander.addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); + addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); + addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); + addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); - jcommander.addCommand("get-backlog-quotas", new GetBacklogQuotaMap()); - jcommander.addCommand("set-backlog-quota", new SetBacklogQuota()); - jcommander.addCommand("remove-backlog-quota", new RemoveBacklogQuota()); + addCommand("get-backlog-quotas", new GetBacklogQuotaMap()); + addCommand("set-backlog-quota", new SetBacklogQuota()); + addCommand("remove-backlog-quota", new RemoveBacklogQuota()); - jcommander.addCommand("get-persistence", new GetPersistence()); - jcommander.addCommand("set-persistence", new SetPersistence()); - jcommander.addCommand("remove-persistence", new RemovePersistence()); + addCommand("get-persistence", new GetPersistence()); + addCommand("set-persistence", new SetPersistence()); + addCommand("remove-persistence", new RemovePersistence()); - jcommander.addCommand("get-message-ttl", new GetMessageTTL()); - jcommander.addCommand("set-message-ttl", new SetMessageTTL()); - jcommander.addCommand("remove-message-ttl", new RemoveMessageTTL()); + addCommand("get-message-ttl", new GetMessageTTL()); + addCommand("set-message-ttl", new SetMessageTTL()); + addCommand("remove-message-ttl", new RemoveMessageTTL()); - jcommander.addCommand("get-max-subscriptions-per-topic", new GetMaxSubscriptionsPerTopic()); - jcommander.addCommand("set-max-subscriptions-per-topic", new SetMaxSubscriptionsPerTopic()); - jcommander.addCommand("remove-max-subscriptions-per-topic", new RemoveMaxSubscriptionsPerTopic()); + addCommand("get-max-subscriptions-per-topic", new GetMaxSubscriptionsPerTopic()); + addCommand("set-max-subscriptions-per-topic", new SetMaxSubscriptionsPerTopic()); + addCommand("remove-max-subscriptions-per-topic", new RemoveMaxSubscriptionsPerTopic()); - jcommander.addCommand("get-subscription-expiration-time", new GetSubscriptionExpirationTime()); - jcommander.addCommand("set-subscription-expiration-time", new SetSubscriptionExpirationTime()); - jcommander.addCommand("remove-subscription-expiration-time", new RemoveSubscriptionExpirationTime()); + addCommand("get-subscription-expiration-time", new GetSubscriptionExpirationTime()); + addCommand("set-subscription-expiration-time", new SetSubscriptionExpirationTime()); + addCommand("remove-subscription-expiration-time", new RemoveSubscriptionExpirationTime()); - jcommander.addCommand("get-anti-affinity-group", new GetAntiAffinityGroup()); - jcommander.addCommand("set-anti-affinity-group", new SetAntiAffinityGroup()); - jcommander.addCommand("get-anti-affinity-namespaces", new GetAntiAffinityNamespaces()); - jcommander.addCommand("delete-anti-affinity-group", new DeleteAntiAffinityGroup()); + addCommand("get-anti-affinity-group", new GetAntiAffinityGroup()); + addCommand("set-anti-affinity-group", new SetAntiAffinityGroup()); + addCommand("get-anti-affinity-namespaces", new GetAntiAffinityNamespaces()); + addCommand("delete-anti-affinity-group", new DeleteAntiAffinityGroup()); - jcommander.addCommand("set-deduplication", new SetDeduplication()); - jcommander.addCommand("get-deduplication", new GetDeduplication()); - jcommander.addCommand("remove-deduplication", new RemoveDeduplication()); + addCommand("set-deduplication", new SetDeduplication()); + addCommand("get-deduplication", new GetDeduplication()); + addCommand("remove-deduplication", new RemoveDeduplication()); - jcommander.addCommand("set-auto-topic-creation", new SetAutoTopicCreation()); - jcommander.addCommand("get-auto-topic-creation", new GetAutoTopicCreation()); - jcommander.addCommand("remove-auto-topic-creation", new RemoveAutoTopicCreation()); + addCommand("set-auto-topic-creation", new SetAutoTopicCreation()); + addCommand("get-auto-topic-creation", new GetAutoTopicCreation()); + addCommand("remove-auto-topic-creation", new RemoveAutoTopicCreation()); - jcommander.addCommand("set-auto-subscription-creation", new SetAutoSubscriptionCreation()); - jcommander.addCommand("get-auto-subscription-creation", new GetAutoSubscriptionCreation()); - jcommander.addCommand("remove-auto-subscription-creation", new RemoveAutoSubscriptionCreation()); + addCommand("set-auto-subscription-creation", new SetAutoSubscriptionCreation()); + addCommand("get-auto-subscription-creation", new GetAutoSubscriptionCreation()); + addCommand("remove-auto-subscription-creation", new RemoveAutoSubscriptionCreation()); - jcommander.addCommand("get-retention", new GetRetention()); - jcommander.addCommand("set-retention", new SetRetention()); - jcommander.addCommand("remove-retention", new RemoveRetention()); + addCommand("get-retention", new GetRetention()); + addCommand("set-retention", new SetRetention()); + addCommand("remove-retention", new RemoveRetention()); - jcommander.addCommand("set-bookie-affinity-group", new SetBookieAffinityGroup()); - jcommander.addCommand("get-bookie-affinity-group", new GetBookieAffinityGroup()); - jcommander.addCommand("delete-bookie-affinity-group", new DeleteBookieAffinityGroup()); + addCommand("set-bookie-affinity-group", new SetBookieAffinityGroup()); + addCommand("get-bookie-affinity-group", new GetBookieAffinityGroup()); + addCommand("delete-bookie-affinity-group", new DeleteBookieAffinityGroup()); - jcommander.addCommand("unload", new Unload()); + addCommand("unload", new Unload()); - jcommander.addCommand("split-bundle", new SplitBundle()); - jcommander.addCommand("get-topic-positions", new GetTopicHashPositions()); + addCommand("split-bundle", new SplitBundle()); + addCommand("get-topic-positions", new GetTopicHashPositions()); - jcommander.addCommand("set-dispatch-rate", new SetDispatchRate()); - jcommander.addCommand("remove-dispatch-rate", new RemoveDispatchRate()); - jcommander.addCommand("get-dispatch-rate", new GetDispatchRate()); + addCommand("set-dispatch-rate", new SetDispatchRate()); + addCommand("remove-dispatch-rate", new RemoveDispatchRate()); + addCommand("get-dispatch-rate", new GetDispatchRate()); - jcommander.addCommand("set-subscribe-rate", new SetSubscribeRate()); - jcommander.addCommand("get-subscribe-rate", new GetSubscribeRate()); - jcommander.addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); + addCommand("set-subscribe-rate", new SetSubscribeRate()); + addCommand("get-subscribe-rate", new GetSubscribeRate()); + addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); - jcommander.addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); - jcommander.addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); - jcommander.addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); + addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); + addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); + addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); - jcommander.addCommand("set-publish-rate", new SetPublishRate()); - jcommander.addCommand("get-publish-rate", new GetPublishRate()); - jcommander.addCommand("remove-publish-rate", new RemovePublishRate()); + addCommand("set-publish-rate", new SetPublishRate()); + addCommand("get-publish-rate", new GetPublishRate()); + addCommand("remove-publish-rate", new RemovePublishRate()); - jcommander.addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); - jcommander.addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); - jcommander.addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); + addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); + addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); + addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); - jcommander.addCommand("clear-backlog", new ClearBacklog()); + addCommand("clear-backlog", new ClearBacklog()); - jcommander.addCommand("unsubscribe", new Unsubscribe()); + addCommand("unsubscribe", new Unsubscribe()); - jcommander.addCommand("set-encryption-required", new SetEncryptionRequired()); - jcommander.addCommand("get-encryption-required", new GetEncryptionRequired()); - jcommander.addCommand("set-subscription-auth-mode", new SetSubscriptionAuthMode()); - jcommander.addCommand("get-subscription-auth-mode", new GetSubscriptionAuthMode()); + addCommand("set-encryption-required", new SetEncryptionRequired()); + addCommand("get-encryption-required", new GetEncryptionRequired()); + addCommand("set-subscription-auth-mode", new SetSubscriptionAuthMode()); + addCommand("get-subscription-auth-mode", new GetSubscriptionAuthMode()); - jcommander.addCommand("set-delayed-delivery", new SetDelayedDelivery()); - jcommander.addCommand("get-delayed-delivery", new GetDelayedDelivery()); - jcommander.addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); + addCommand("set-delayed-delivery", new SetDelayedDelivery()); + addCommand("get-delayed-delivery", new GetDelayedDelivery()); + addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); - jcommander.addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); - jcommander.addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); - jcommander.addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); + addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); + addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); + addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); - jcommander.addCommand("get-max-producers-per-topic", new GetMaxProducersPerTopic()); - jcommander.addCommand("set-max-producers-per-topic", new SetMaxProducersPerTopic()); - jcommander.addCommand("remove-max-producers-per-topic", new RemoveMaxProducersPerTopic()); + addCommand("get-max-producers-per-topic", new GetMaxProducersPerTopic()); + addCommand("set-max-producers-per-topic", new SetMaxProducersPerTopic()); + addCommand("remove-max-producers-per-topic", new RemoveMaxProducersPerTopic()); - jcommander.addCommand("get-max-consumers-per-topic", new GetMaxConsumersPerTopic()); - jcommander.addCommand("set-max-consumers-per-topic", new SetMaxConsumersPerTopic()); - jcommander.addCommand("remove-max-consumers-per-topic", new RemoveMaxConsumersPerTopic()); + addCommand("get-max-consumers-per-topic", new GetMaxConsumersPerTopic()); + addCommand("set-max-consumers-per-topic", new SetMaxConsumersPerTopic()); + addCommand("remove-max-consumers-per-topic", new RemoveMaxConsumersPerTopic()); - jcommander.addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); - jcommander.addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); - jcommander.addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); + addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); + addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); + addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); - jcommander.addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("remove-max-unacked-messages-per-subscription", + addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesPerSubscription()); + addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesPerSubscription()); + addCommand("remove-max-unacked-messages-per-subscription", new RemoveMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesPerConsumer()); - jcommander.addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesPerConsumer()); - jcommander.addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesPerConsumer()); + addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesPerConsumer()); + addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesPerConsumer()); + addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesPerConsumer()); - jcommander.addCommand("get-compaction-threshold", new GetCompactionThreshold()); - jcommander.addCommand("set-compaction-threshold", new SetCompactionThreshold()); - jcommander.addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); + addCommand("get-compaction-threshold", new GetCompactionThreshold()); + addCommand("set-compaction-threshold", new SetCompactionThreshold()); + addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); - jcommander.addCommand("get-offload-threshold", new GetOffloadThreshold()); - jcommander.addCommand("set-offload-threshold", new SetOffloadThreshold()); + addCommand("get-offload-threshold", new GetOffloadThreshold()); + addCommand("set-offload-threshold", new SetOffloadThreshold()); - jcommander.addCommand("get-offload-deletion-lag", new GetOffloadDeletionLag()); - jcommander.addCommand("set-offload-deletion-lag", new SetOffloadDeletionLag()); - jcommander.addCommand("clear-offload-deletion-lag", new ClearOffloadDeletionLag()); + addCommand("get-offload-deletion-lag", new GetOffloadDeletionLag()); + addCommand("set-offload-deletion-lag", new SetOffloadDeletionLag()); + addCommand("clear-offload-deletion-lag", new ClearOffloadDeletionLag()); - jcommander.addCommand("get-schema-autoupdate-strategy", new GetSchemaAutoUpdateStrategy()); - jcommander.addCommand("set-schema-autoupdate-strategy", new SetSchemaAutoUpdateStrategy()); + addCommand("get-schema-autoupdate-strategy", new GetSchemaAutoUpdateStrategy()); + addCommand("set-schema-autoupdate-strategy", new SetSchemaAutoUpdateStrategy()); - jcommander.addCommand("get-schema-compatibility-strategy", new GetSchemaCompatibilityStrategy()); - jcommander.addCommand("set-schema-compatibility-strategy", new SetSchemaCompatibilityStrategy()); + addCommand("get-schema-compatibility-strategy", new GetSchemaCompatibilityStrategy()); + addCommand("set-schema-compatibility-strategy", new SetSchemaCompatibilityStrategy()); - jcommander.addCommand("get-is-allow-auto-update-schema", new GetIsAllowAutoUpdateSchema()); - jcommander.addCommand("set-is-allow-auto-update-schema", new SetIsAllowAutoUpdateSchema()); + addCommand("get-is-allow-auto-update-schema", new GetIsAllowAutoUpdateSchema()); + addCommand("set-is-allow-auto-update-schema", new SetIsAllowAutoUpdateSchema()); - jcommander.addCommand("get-schema-validation-enforce", new GetSchemaValidationEnforced()); - jcommander.addCommand("set-schema-validation-enforce", new SetSchemaValidationEnforced()); + addCommand("get-schema-validation-enforce", new GetSchemaValidationEnforced()); + addCommand("set-schema-validation-enforce", new SetSchemaValidationEnforced()); - jcommander.addCommand("set-offload-policies", new SetOffloadPolicies()); - jcommander.addCommand("remove-offload-policies", new RemoveOffloadPolicies()); - jcommander.addCommand("get-offload-policies", new GetOffloadPolicies()); + addCommand("set-offload-policies", new SetOffloadPolicies()); + addCommand("remove-offload-policies", new RemoveOffloadPolicies()); + addCommand("get-offload-policies", new GetOffloadPolicies()); - jcommander.addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); - jcommander.addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); - jcommander.addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); + addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); + addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); + addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); - jcommander.addCommand("set-max-topics-per-namespace", new SetMaxTopicsPerNamespace()); - jcommander.addCommand("get-max-topics-per-namespace", new GetMaxTopicsPerNamespace()); - jcommander.addCommand("remove-max-topics-per-namespace", new RemoveMaxTopicsPerNamespace()); + addCommand("set-max-topics-per-namespace", new SetMaxTopicsPerNamespace()); + addCommand("get-max-topics-per-namespace", new GetMaxTopicsPerNamespace()); + addCommand("remove-max-topics-per-namespace", new RemoveMaxTopicsPerNamespace()); - jcommander.addCommand("set-property", new SetPropertyForNamespace()); - jcommander.addCommand("get-property", new GetPropertyForNamespace()); - jcommander.addCommand("remove-property", new RemovePropertyForNamespace()); - jcommander.addCommand("set-properties", new SetPropertiesForNamespace()); - jcommander.addCommand("get-properties", new GetPropertiesForNamespace()); - jcommander.addCommand("clear-properties", new ClearPropertiesForNamespace()); + addCommand("set-property", new SetPropertyForNamespace()); + addCommand("get-property", new GetPropertyForNamespace()); + addCommand("remove-property", new RemovePropertyForNamespace()); + addCommand("set-properties", new SetPropertiesForNamespace()); + addCommand("get-properties", new GetPropertiesForNamespace()); + addCommand("clear-properties", new ClearPropertiesForNamespace()); - jcommander.addCommand("get-resource-group", new GetResourceGroup()); - jcommander.addCommand("set-resource-group", new SetResourceGroup()); - jcommander.addCommand("remove-resource-group", new RemoveResourceGroup()); + addCommand("get-resource-group", new GetResourceGroup()); + addCommand("set-resource-group", new SetResourceGroup()); + addCommand("remove-resource-group", new RemoveResourceGroup()); - jcommander.addCommand("get-entry-filters", new GetEntryFiltersPerTopic()); - jcommander.addCommand("set-entry-filters", new SetEntryFiltersPerTopic()); - jcommander.addCommand("remove-entry-filters", new RemoveEntryFiltersPerTopic()); + addCommand("get-entry-filters", new GetEntryFiltersPerTopic()); + addCommand("set-entry-filters", new SetEntryFiltersPerTopic()); + addCommand("remove-entry-filters", new RemoveEntryFiltersPerTopic()); - jcommander.addCommand("update-migration-state", new UpdateMigrationState()); + addCommand("update-migration-state", new UpdateMigrationState()); - jcommander.addCommand("set-dispatcher-pause-on-ack-state-persistent", + addCommand("set-dispatcher-pause-on-ack-state-persistent", new SetDispatcherPauseOnAckStatePersistent()); - jcommander.addCommand("get-dispatcher-pause-on-ack-state-persistent", + addCommand("get-dispatcher-pause-on-ack-state-persistent", new GetDispatcherPauseOnAckStatePersistent()); - jcommander.addCommand("remove-dispatcher-pause-on-ack-state-persistent", + addCommand("remove-dispatcher-pause-on-ack-state-persistent", new RemoveDispatcherPauseOnAckStatePersistent()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java index c344a76853c1d..cb75ea345787c 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java @@ -18,28 +18,29 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.util.function.Supplier; import org.apache.pulsar.client.admin.NonPersistentTopics; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @SuppressWarnings("deprecation") -@Parameters(commandDescription = "Operations on non-persistent topics", hidden = true) +@Command(description = "Operations on non-persistent topics", hidden = true) public class CmdNonPersistentTopics extends CmdBase { private NonPersistentTopics nonPersistentTopics; public CmdNonPersistentTopics(Supplier admin) { super("non-persistent", admin); - jcommander.addCommand("create-partitioned-topic", new CreatePartitionedCmd()); - jcommander.addCommand("lookup", new Lookup()); - jcommander.addCommand("stats", new GetStats()); - jcommander.addCommand("stats-internal", new GetInternalStats()); - jcommander.addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); - jcommander.addCommand("list", new GetList()); - jcommander.addCommand("list-in-bundle", new GetListInBundle()); + addCommand("create-partitioned-topic", new CreatePartitionedCmd()); + addCommand("lookup", new Lookup()); + addCommand("stats", new GetStats()); + addCommand("stats-internal", new GetInternalStats()); + addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); + addCommand("list", new GetList()); + addCommand("list-in-bundle", new GetListInBundle()); } private NonPersistentTopics getNonPersistentTopics() { @@ -49,99 +50,97 @@ private NonPersistentTopics getNonPersistentTopics() { return nonPersistentTopics; } - @Parameters(commandDescription = "Lookup a topic from the current serving broker") + @Command(description = "Lookup a topic from the current serving broker") private class Lookup extends CliCommand { - @Parameter(description = "non-persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "non-persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getAdmin().lookups().lookupTopic(topic)); } } - @Parameters(commandDescription = "Get the stats for the topic and its connected producers and consumers. " + @Command(description = "Get the stats for the topic and its connected producers and consumers. " + "All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.") private class GetStats extends CliCommand { - @Parameter(description = "non-persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "non-persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validateNonPersistentTopic(params); + String persistentTopic = validateNonPersistentTopic(topicName); print(getNonPersistentTopics().getStats(persistentTopic)); } } - @Parameters(commandDescription = "Get the internal stats for the topic") + @Command(description = "Get the internal stats for the topic") private class GetInternalStats extends CliCommand { - @Parameter(description = "non-persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "non-persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validateNonPersistentTopic(params); + String persistentTopic = validateNonPersistentTopic(topicName); print(getNonPersistentTopics().getInternalStats(persistentTopic)); } } - @Parameters(commandDescription = "Create a partitioned topic. " + @Command(description = "Create a partitioned topic. " + "The partitioned topic has to be created before creating a producer on it.") private class CreatePartitionedCmd extends CliCommand { - @Parameter(description = "non-persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "non-persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-p", + @Option(names = { "-p", "--partitions" }, description = "Number of partitions for the topic", required = true) private int numPartitions; @Override void run() throws Exception { - String persistentTopic = validateNonPersistentTopic(params); + String persistentTopic = validateNonPersistentTopic(topicName); getNonPersistentTopics().createPartitionedTopic(persistentTopic, numPartitions); } } - @Parameters(commandDescription = "Get the partitioned topic metadata. " + @Command(description = "Get the partitioned topic metadata. " + "If the topic is not created or is a non-partitioned topic, it returns empty topic with 0 partitions") private class GetPartitionedTopicMetadataCmd extends CliCommand { - @Parameter(description = "non-persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "non-persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String persistentTopic = validateNonPersistentTopic(params); - print(getNonPersistentTopics().getPartitionedTopicMetadata(persistentTopic)); + String nonPersistentTopic = validateNonPersistentTopic(topicName); + print(getNonPersistentTopics().getPartitionedTopicMetadata(nonPersistentTopic)); } } - @Parameters(commandDescription = "Get list of non-persistent topics present under a namespace") + @Command(description = "Get list of non-persistent topics present under a namespace") private class GetList extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespace; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); print(getNonPersistentTopics().getList(namespace)); } } - @Parameters(commandDescription = "Get list of non-persistent topics present under a namespace bundle") + @Command(description = "Get list of non-persistent topics present under a namespace bundle") private class GetListInBundle extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespace; - @Parameter(names = { "-b", + @Option(names = { "-b", "--bundle" }, description = "bundle range", required = true) private String bundleRange; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); print(getNonPersistentTopics().getListInBundle(namespace, bundleRange)); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPackages.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPackages.java index 24214a89c57c3..68547d233460a 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPackages.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPackages.java @@ -18,20 +18,20 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.DynamicParameter; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.apache.pulsar.client.admin.Packages; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.packages.management.core.common.PackageMetadata; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * Commands for administering packages. */ -@Parameters(commandDescription = "Operations about packages") +@Command(description = "Operations about packages") class CmdPackages extends CmdBase { private Packages packages; @@ -40,13 +40,13 @@ public CmdPackages(Supplier admin) { super("packages", admin); - jcommander.addCommand("get-metadata", new GetMetadataCmd()); - jcommander.addCommand("update-metadata", new UpdateMetadataCmd()); - jcommander.addCommand("upload", new UploadCmd()); - jcommander.addCommand("download", new DownloadCmd()); - jcommander.addCommand("list", new ListPackagesCmd()); - jcommander.addCommand("list-versions", new ListPackageVersionsCmd()); - jcommander.addCommand("delete", new DeletePackageCmd()); + addCommand("get-metadata", new GetMetadataCmd()); + addCommand("update-metadata", new UpdateMetadataCmd()); + addCommand("upload", new UploadCmd()); + addCommand("download", new DownloadCmd()); + addCommand("list", new ListPackagesCmd()); + addCommand("list-versions", new ListPackageVersionsCmd()); + addCommand("delete", new DeletePackageCmd()); } private Packages getPackages() { @@ -56,9 +56,9 @@ private Packages getPackages() { return packages; } - @Parameters(commandDescription = "Get a package metadata information.") + @Command(description = "Get a package metadata information.") private class GetMetadataCmd extends CliCommand { - @Parameter(description = "type://tenant/namespace/packageName@version", required = true) + @Parameters(description = "type://tenant/namespace/packageName@version", arity = "1") private String packageName; @Override @@ -67,18 +67,18 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Update a package metadata information.") + @Command(description = "Update a package metadata information.") private class UpdateMetadataCmd extends CliCommand { - @Parameter(description = "type://tenant/namespace/packageName@version", required = true) + @Parameters(description = "type://tenant/namespace/packageName@version", arity = "1") private String packageName; - @Parameter(names = {"-d", "--description"}, description = "descriptions of a package", required = true) + @Option(names = {"-d", "--description"}, description = "descriptions of a package", required = true) private String description; - @Parameter(names = {"-c", "--contact"}, description = "contact info of a package") + @Option(names = {"-c", "--contact"}, description = "contact info of a package") private String contact; - @DynamicParameter(names = {"--properties", "-P"}, description = "external information of a package") + @Option(names = {"--properties", "-P"}, description = "external information of a package") private Map properties = new HashMap<>(); @Override @@ -89,21 +89,21 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Upload a package") + @Command(description = "Upload a package") private class UploadCmd extends CliCommand { - @Parameter(description = "type://tenant/namespace/packageName@version", required = true) + @Parameters(description = "type://tenant/namespace/packageName@version", arity = "1") private String packageName; - @Parameter(names = "--description", description = "descriptions of a package", required = true) + @Option(names = "--description", description = "descriptions of a package", required = true) private String description; - @Parameter(names = "--contact", description = "contact information of a package") + @Option(names = "--contact", description = "contact information of a package") private String contact; - @DynamicParameter(names = {"--properties", "-P"}, description = "external information of a package") + @Option(names = {"--properties", "-P"}, description = "external information of a package") private Map properties = new HashMap<>(); - @Parameter(names = "--path", description = "file path of the package", required = true) + @Option(names = "--path", description = "file path of the package", required = true) private String path; @Override @@ -117,12 +117,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Download a package") + @Command(description = "Download a package") private class DownloadCmd extends CliCommand { - @Parameter(description = "type://tenant/namespace/packageName@version", required = true) + @Parameters(description = "type://tenant/namespace/packageName@version", arity = "1") private String packageName; - @Parameter(names = "--path", description = "download destiny path of the package", required = true) + @Option(names = "--path", description = "download destiny path of the package", required = true) private String path; @Override @@ -132,10 +132,10 @@ void run() throws Exception { } } - @Parameters(commandDescription = "List all versions of the given package") + @Command(description = "List all versions of the given package") private class ListPackageVersionsCmd extends CliCommand { - @Parameter(description = "the package name you want to query, don't need to specify the package version. " - + "type://tenant/namespace/packageName", required = true) + @Parameters(description = "the package name you want to query, don't need to specify the package version. " + + "type://tenant/namespace/packageName", arity = "1") private String packageName; @Override @@ -144,12 +144,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "List all packages with given type in the specified namespace") + @Command(description = "List all packages with given type in the specified namespace") private class ListPackagesCmd extends CliCommand { - @Parameter(names = "--type", description = "type of the package", required = true) + @Option(names = "--type", description = "type of the package", required = true) private String type; - @Parameter(description = "namespace of the package", required = true) + @Parameters(description = "namespace of the package", arity = "1") private String namespace; @Override @@ -158,9 +158,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Delete a package") - private class DeletePackageCmd extends CliCommand{ - @Parameter(description = "type://tenant/namespace/packageName@version", required = true) + @Command(description = "Delete a package") + private class DeletePackageCmd extends CliCommand { + @Parameters(description = "type://tenant/namespace/packageName@version", arity = "1") private String packageName; @Override diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java index cdfcaefc7f6e4..3dc0ba7b6f24a 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java @@ -19,8 +19,6 @@ package org.apache.pulsar.admin.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import io.netty.buffer.ByteBuf; @@ -31,19 +29,21 @@ import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; -import org.apache.pulsar.cli.converters.TimeUnitToMillisConverter; -import org.apache.pulsar.cli.converters.TimeUnitToSecondsConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToSecondsConverter; import org.apache.pulsar.client.admin.LongRunningProcessStatus; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.Topics; import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.MessageId; -import org.apache.pulsar.client.cli.NoSplitter; import org.apache.pulsar.client.impl.BatchMessageIdImpl; import org.apache.pulsar.client.impl.MessageIdImpl; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations on persistent topics. The persistent-topics " +@Command(description = "Operations on persistent topics. The persistent-topics " + "has been deprecated in favor of topics", hidden = true) public class CmdPersistentTopics extends CmdBase { private Topics persistentTopics; @@ -51,39 +51,39 @@ public class CmdPersistentTopics extends CmdBase { public CmdPersistentTopics(Supplier admin) { super("persistent", admin); - jcommander.addCommand("list", new ListCmd()); - jcommander.addCommand("list-partitioned-topics", new PartitionedTopicListCmd()); - jcommander.addCommand("permissions", new Permissions()); - jcommander.addCommand("grant-permission", new GrantPermissions()); - jcommander.addCommand("revoke-permission", new RevokePermissions()); - jcommander.addCommand("lookup", new Lookup()); - jcommander.addCommand("bundle-range", new GetBundleRange()); - jcommander.addCommand("delete", new DeleteCmd()); - jcommander.addCommand("unload", new UnloadCmd()); - jcommander.addCommand("truncate", new TruncateCmd()); - jcommander.addCommand("subscriptions", new ListSubscriptions()); - jcommander.addCommand("unsubscribe", new DeleteSubscription()); - jcommander.addCommand("create-subscription", new CreateSubscription()); - jcommander.addCommand("stats", new GetStats()); - jcommander.addCommand("stats-internal", new GetInternalStats()); - jcommander.addCommand("info-internal", new GetInternalInfo()); - jcommander.addCommand("partitioned-stats", new GetPartitionedStats()); - jcommander.addCommand("partitioned-stats-internal", new GetPartitionedStatsInternal()); - jcommander.addCommand("skip", new Skip()); - jcommander.addCommand("skip-all", new SkipAll()); - jcommander.addCommand("expire-messages", new ExpireMessages()); - jcommander.addCommand("expire-messages-all-subscriptions", new ExpireMessagesForAllSubscriptions()); - jcommander.addCommand("create-partitioned-topic", new CreatePartitionedCmd()); - jcommander.addCommand("update-partitioned-topic", new UpdatePartitionedCmd()); - jcommander.addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); - jcommander.addCommand("delete-partitioned-topic", new DeletePartitionedCmd()); - jcommander.addCommand("peek-messages", new PeekMessages()); - jcommander.addCommand("get-message-by-id", new GetMessageById()); - jcommander.addCommand("last-message-id", new GetLastMessageId()); - jcommander.addCommand("reset-cursor", new ResetCursor()); - jcommander.addCommand("terminate", new Terminate()); - jcommander.addCommand("compact", new Compact()); - jcommander.addCommand("compaction-status", new CompactionStatusCmd()); + addCommand("list", new ListCmd()); + addCommand("list-partitioned-topics", new PartitionedTopicListCmd()); + addCommand("permissions", new Permissions()); + addCommand("grant-permission", new GrantPermissions()); + addCommand("revoke-permission", new RevokePermissions()); + addCommand("lookup", new Lookup()); + addCommand("bundle-range", new GetBundleRange()); + addCommand("delete", new DeleteCmd()); + addCommand("unload", new UnloadCmd()); + addCommand("truncate", new TruncateCmd()); + addCommand("subscriptions", new ListSubscriptions()); + addCommand("unsubscribe", new DeleteSubscription()); + addCommand("create-subscription", new CreateSubscription()); + addCommand("stats", new GetStats()); + addCommand("stats-internal", new GetInternalStats()); + addCommand("info-internal", new GetInternalInfo()); + addCommand("partitioned-stats", new GetPartitionedStats()); + addCommand("partitioned-stats-internal", new GetPartitionedStatsInternal()); + addCommand("skip", new Skip()); + addCommand("skip-all", new SkipAll()); + addCommand("expire-messages", new ExpireMessages()); + addCommand("expire-messages-all-subscriptions", new ExpireMessagesForAllSubscriptions()); + addCommand("create-partitioned-topic", new CreatePartitionedCmd()); + addCommand("update-partitioned-topic", new UpdatePartitionedCmd()); + addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); + addCommand("delete-partitioned-topic", new DeletePartitionedCmd()); + addCommand("peek-messages", new PeekMessages()); + addCommand("get-message-by-id", new GetMessageById()); + addCommand("last-message-id", new GetLastMessageId()); + addCommand("reset-cursor", new ResetCursor()); + addCommand("terminate", new Terminate()); + addCommand("compact", new Compact()); + addCommand("compaction-status", new CompactionStatusCmd()); } private Topics getPersistentTopics() { @@ -93,420 +93,420 @@ private Topics getPersistentTopics() { return persistentTopics; } - @Parameters(commandDescription = "Get the list of topics under a namespace.") + @Command(description = "Get the list of topics under a namespace.") private class ListCmd extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getPersistentTopics().getList(namespace)); } } - @Parameters(commandDescription = "Get the list of partitioned topics under a namespace.") + @Command(description = "Get the list of partitioned topics under a namespace.") private class PartitionedTopicListCmd extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); print(getPersistentTopics().getPartitionedTopicList(namespace)); } } - @Parameters(commandDescription = "Grant a new permission to a client role on a single topic.") + @Command(description = "Grant a new permission to a client role on a single topic.") private class GrantPermissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--role", description = "Client role to which grant permissions", required = true) + @Option(names = "--role", description = "Client role to which grant permissions", required = true) private String role; - @Parameter(names = "--actions", description = "Actions to be granted (produce,consume,sources,sinks," + @Option(names = "--actions", description = "Actions to be granted (produce,consume,sources,sinks," + "functions,packages)", required = true) private List actions; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getPersistentTopics().grantPermission(topic, role, getAuthActions(actions)); } } - @Parameters(commandDescription = "Revoke permissions on a topic. " + @Command(description = "Revoke permissions on a topic. " + "Revoke permissions to a client role on a single topic. If the permission " + "was not set at the topic level, but rather at the namespace level, this " + "operation will return an error (HTTP status code 412).") private class RevokePermissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--role", description = "Client role to which revoke permissions", required = true) + @Option(names = "--role", description = "Client role to which revoke permissions", required = true) private String role; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getPersistentTopics().revokePermissions(topic, role); } } - @Parameters(commandDescription = "Get the permissions on a topic. " + @Command(description = "Get the permissions on a topic. " + "Retrieve the effective permissions for a topic. These permissions are defined " + "by the permissions set at the namespace level combined (union) with any eventual " + "specific permission set on the topic.") private class Permissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getPersistentTopics().getPermissions(topic)); } } - @Parameters(commandDescription = "Lookup a topic from the current serving broker") + @Command(description = "Lookup a topic from the current serving broker") private class Lookup extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getAdmin().lookups().lookupTopic(topic)); } } - @Parameters(commandDescription = "Get Namespace bundle range of a topic") + @Command(description = "Get Namespace bundle range of a topic") private class GetBundleRange extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getAdmin().lookups().getBundleRange(topic)); } } - @Parameters(commandDescription = "Create a partitioned topic. " + @Command(description = "Create a partitioned topic. " + "The partitioned topic has to be created before creating a producer on it.") private class CreatePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-p", + @Option(names = { "-p", "--partitions" }, description = "Number of partitions for the topic", required = true) private int numPartitions; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().createPartitionedTopic(persistentTopic, numPartitions); } } - @Parameters(commandDescription = "Update existing partitioned topic. " + @Command(description = "Update existing partitioned topic. " + "New updating number of partitions must be greater than existing number of partitions.") private class UpdatePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-p", + @Option(names = { "-p", "--partitions" }, description = "Number of partitions for the topic", required = true) private int numPartitions; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().updatePartitionedTopic(persistentTopic, numPartitions); } } - @Parameters(commandDescription = "Get the partitioned topic metadata. " + @Command(description = "Get the partitioned topic metadata. " + "If the topic is not created or is a non-partitioned topic, it returns empty topic with 0 partitions") private class GetPartitionedTopicMetadataCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getPartitionedTopicMetadata(persistentTopic)); } } - @Parameters(commandDescription = "Delete a partitioned topic. " + @Command(description = "Delete a partitioned topic. " + "It will also delete all the partitions of the topic if it exists.") private class DeletePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--force", + @Option(names = "--force", description = "Close all producer/consumer/replicator and delete topic forcefully") private boolean force = false; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().deletePartitionedTopic(persistentTopic, force); } } - @Parameters(commandDescription = "Delete a topic. " + @Command(description = "Delete a topic. " + "The topic cannot be deleted if there's any active subscription or producers connected to it.") private class DeleteCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--force", + @Option(names = "--force", description = "Close all producer/consumer/replicator and delete topic forcefully") private boolean force = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().delete(persistentTopic, force); } } - @Parameters(commandDescription = "Unload a topic.") + @Command(description = "Unload a topic.") private class UnloadCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().unload(persistentTopic); } } - @Parameters(commandDescription = "Truncate a topic. \n\t\tThe truncate operation will move all cursors to the end " + @Command(description = "Truncate a topic. \n\t\tThe truncate operation will move all cursors to the end " + "of the topic and delete all inactive ledgers. ") private class TruncateCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic\n", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getPersistentTopics().truncate(topic); } } - @Parameters(commandDescription = "Get the list of subscriptions on the topic") + @Command(description = "Get the list of subscriptions on the topic") private class ListSubscriptions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getSubscriptions(persistentTopic)); } } - @Parameters(commandDescription = "Delete a durable subscriber from a topic. " + @Command(description = "Delete a durable subscriber from a topic. " + "The subscription cannot be deleted if there are any active consumers attached to it") private class DeleteSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Disconnect and close all consumers and delete subscription forcefully") private boolean force = false; - @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be deleted", required = true) + @Option(names = { "-s", "--subscription" }, description = "Subscription to be deleted", required = true) private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().deleteSubscription(persistentTopic, subName, force); } } - @Parameters(commandDescription = "Get the stats for the topic and its connected producers and consumers. " + @Command(description = "Get the stats for the topic and its connected producers and consumers. " + "All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.") private class GetStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-gpb", "--get-precise-backlog" }, description = "Set true to get precise backlog") + @Option(names = { "-gpb", "--get-precise-backlog" }, description = "Set true to get precise backlog") private boolean getPreciseBacklog = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getStats(persistentTopic, getPreciseBacklog)); } } - @Parameters(commandDescription = "Get the internal stats for the topic") + @Command(description = "Get the internal stats for the topic") private class GetInternalStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-m", + @Option(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") private boolean metadata = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getInternalStats(persistentTopic, metadata)); } } - @Parameters(commandDescription = "Get the internal metadata info for the topic") + @Command(description = "Get the internal metadata info for the topic") private class GetInternalInfo extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); String result = getPersistentTopics().getInternalInfo(persistentTopic); Gson gson = new GsonBuilder().setPrettyPrinting().create(); System.out.println(gson.toJson(result)); } } - @Parameters(commandDescription = "Get the stats for the partitioned topic and " + @Command(description = "Get the stats for the partitioned topic and " + "its connected producers and consumers. All the rates are computed over a 1 minute window and " + "are relative the last completed 1 minute period.") private class GetPartitionedStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--per-partition", description = "Get per partition stats") + @Option(names = "--per-partition", description = "Get per partition stats") private boolean perPartition = false; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getPartitionedStats(persistentTopic, perPartition)); } } - @Parameters(commandDescription = "Get the stats-internal for the partitioned topic and " + @Command(description = "Get the stats-internal for the partitioned topic and " + "its connected producers and consumers. All the rates are computed over a 1 minute window and " + "are relative the last completed 1 minute period.") private class GetPartitionedStatsInternal extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getPersistentTopics().getPartitionedInternalStats(persistentTopic)); } } - @Parameters(commandDescription = "Skip all the messages for the subscription") + @Command(description = "Skip all the messages for the subscription") private class SkipAll extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be cleared", required = true) + @Option(names = { "-s", "--subscription" }, description = "Subscription to be cleared", required = true) private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().skipAllMessages(persistentTopic, subName); } } - @Parameters(commandDescription = "Skip some messages for the subscription") + @Command(description = "Skip some messages for the subscription") private class Skip extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to be skip messages on", required = true) private String subName; - @Parameter(names = { "-n", "--count" }, description = "Number of messages to skip", required = true) + @Option(names = { "-n", "--count" }, description = "Number of messages to skip", required = true) private long numMessages; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().skipMessages(persistentTopic, subName, numMessages); } } - @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) " + @Command(description = "Expire messages that older than given expiry time (in seconds) " + "for the subscription") private class ExpireMessages extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to be skip messages on", required = true) private String subName; - @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + @Option(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w)", required = true, converter = TimeUnitToSecondsConverter.class) private Long expireTimeInSeconds; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().expireMessages(persistentTopic, subName, expireTimeInSeconds); } } - @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) " + @Command(description = "Expire messages that older than given expiry time (in seconds) " + "for all subscriptions") private class ExpireMessagesForAllSubscriptions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + @Option(names = {"-t", "--expireTime"}, description = "Expire messages older than time in seconds " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w)", required = true, converter = TimeUnitToSecondsConverter.class) private Long expireTimeInSeconds; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().expireMessagesForAllSubscriptions(persistentTopic, expireTimeInSeconds); } } - @Parameters(commandDescription = "Create a new subscription on a topic") + @Command(description = "Create a new subscription on a topic") private class CreateSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription name", required = true) private String subscriptionName; - @Parameter(names = { "--messageId", + @Option(names = { "--messageId", "-m" }, description = "messageId where to create the subscription. " + "It can be either 'latest', 'earliest' or (ledgerId:entryId)", required = false) private String messageIdStr = "latest"; - @Parameter(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", - required = false, splitter = NoSplitter.class) - private java.util.List properties; + @Option(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", + required = false) + private Map properties; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); MessageId messageId; if (messageIdStr.equals("latest")) { messageId = MessageId.latest; @@ -515,33 +515,32 @@ void run() throws PulsarAdminException { } else { messageId = validateMessageIdString(messageIdStr); } - Map map = parseListKeyValueMap(properties); - getPersistentTopics().createSubscription(persistentTopic, subscriptionName, messageId, false, map); + getPersistentTopics().createSubscription(persistentTopic, subscriptionName, messageId, false, properties); } } - @Parameters(commandDescription = "Reset position for subscription to position closest to timestamp or messageId") + @Command(description = "Reset position for subscription to position closest to timestamp or messageId") private class ResetCursor extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to reset position on", required = true) private String subName; - @Parameter(names = { "--time", + @Option(names = { "--time", "-t" }, description = "time in minutes to reset back to " + "(or minutes, hours,days,weeks eg: 100m, 3h, 2d, 5w)", required = false, converter = TimeUnitToMillisConverter.class) private Long resetTimeInMillis = null; - @Parameter(names = { "--messageId", + @Option(names = { "--messageId", "-m" }, description = "messageId to reset back to (ledgerId:entryId)", required = false) private String resetMessageIdStr; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (isNotBlank(resetMessageIdStr)) { MessageId messageId = validateMessageIdString(resetMessageIdStr); getPersistentTopics().resetCursor(persistentTopic, subName, messageId); @@ -556,14 +555,14 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Terminate a topic and don't allow any more messages to be published") + @Command(description = "Terminate a topic and don't allow any more messages to be published") private class Terminate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); try { MessageId lastMessageId = getPersistentTopics().terminateTopicAsync(persistentTopic).get(); @@ -574,21 +573,21 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Peek some messages for the subscription") + @Command(description = "Peek some messages for the subscription") private class PeekMessages extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to get messages from", required = true) private String subName; - @Parameter(names = { "-n", "--count" }, description = "Number of messages (default 1)", required = false) + @Option(names = { "-n", "--count" }, description = "Number of messages (default 1)", required = false) private int numMessages = 1; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); List> messages = getPersistentTopics().peekMessages(persistentTopic, subName, numMessages); int position = 0; for (Message msg : messages) { @@ -613,24 +612,24 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get message by its ledgerId and entryId") + @Command(description = "Get message by its ledgerId and entryId") private class GetMessageById extends CliCommand { - @Parameter(description = "persistent://property/cluster/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://property/cluster/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-l", "--ledgerId" }, + @Option(names = { "-l", "--ledgerId" }, description = "ledger id pointing to the desired ledger", required = true) private long ledgerId; - @Parameter(names = { "-e", "--entryId" }, + @Option(names = { "-e", "--entryId" }, description = "entry id pointing to the desired entry", required = true) private long entryId; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); Message message = getPersistentTopics().getMessageById(persistentTopic, ledgerId, entryId); @@ -640,45 +639,45 @@ void run() throws PulsarAdminException { } - @Parameters(commandDescription = "Get last message Id of the topic") + @Command(description = "Get last message Id of the topic") private class GetLastMessageId extends CliCommand { - @Parameter(description = "persistent://property/cluster/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://property/cluster/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); MessageId messageId = getPersistentTopics().getLastMessageId(persistentTopic); print(messageId); } } - @Parameters(commandDescription = "Compact a topic") + @Command(description = "Compact a topic") private class Compact extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getPersistentTopics().triggerCompaction(persistentTopic); System.out.println("Topic compaction requested for " + persistentTopic); } } - @Parameters(commandDescription = "Status of compaction on a topic") + @Command(description = "Status of compaction on a topic") private class CompactionStatusCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-w", "--wait-complete" }, - description = "Wait for compaction to complete", required = false) + @Option(names = {"-w", "--wait-complete"}, + description = "Wait for compaction to complete", required = false) private boolean wait = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); try { LongRunningProcessStatus status = getPersistentTopics().compactionStatus(persistentTopic); diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdProxyStats.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdProxyStats.java index a5ec14a3ed725..a8f8205bf8127 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdProxyStats.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdProxyStats.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; @@ -28,13 +26,15 @@ import java.io.IOException; import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -@Parameters(commandDescription = "Operations to collect Proxy statistics") +@Command(description = "Operations to collect Proxy statistics") public class CmdProxyStats extends CmdBase { - @Parameters(commandDescription = "dump connections metrics for Monitoring") + @Command(description = "dump connections metrics for Monitoring") private class CmdConnectionMetrics extends CliCommand { - @Parameter(names = { "-i", "--indent" }, description = "Indent JSON output", required = false) + @Option(names = {"-i", "--indent"}, description = "Indent JSON output", required = false) private boolean indent = false; @Override @@ -45,9 +45,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "dump topics metrics for Monitoring") + @Command(description = "dump topics metrics for Monitoring") private class CmdTopicsMetrics extends CliCommand { - @Parameter(names = { "-i", "--indent" }, description = "Indent JSON output", required = false) + @Option(names = {"-i", "--indent"}, description = "Indent JSON output", required = false) private boolean indent = false; @Override @@ -66,7 +66,7 @@ public void printStats(JsonElement json, boolean indent) throws IOException { public CmdProxyStats(Supplier admin) { super("proxy-stats", admin); - jcommander.addCommand("connections", new CmdConnectionMetrics()); - jcommander.addCommand("topics", new CmdTopicsMetrics()); + addCommand("connections", new CmdConnectionMetrics()); + addCommand("topics", new CmdTopicsMetrics()); } } \ No newline at end of file diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceGroups.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceGroups.java index 7ed853be44d9f..6ee8d7a4764a0 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceGroups.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceGroups.java @@ -18,16 +18,17 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.policies.data.ResourceGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about ResourceGroups") +@Command(description = "Operations about ResourceGroups") public class CmdResourceGroups extends CmdBase { - @Parameters(commandDescription = "List the existing resourcegroups") + @Command(description = "List the existing resourcegroups") private class List extends CliCommand { @Override void run() throws PulsarAdminException { @@ -35,112 +36,107 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Gets the configuration of a resourcegroup") + @Command(description = "Gets the configuration of a resourcegroup") private class Get extends CliCommand { - @Parameter(description = "resourcegroup-name", required = true) - private java.util.List params; + @Parameters(description = "resourcegroup-name", arity = "1") + private String resourceGroupName; @Override void run() throws PulsarAdminException { - String name = getOneArgument(params); - print(getAdmin().resourcegroups().getResourceGroup(name)); + print(getAdmin().resourcegroups().getResourceGroup(resourceGroupName)); } } - @Parameters(commandDescription = "Creates a new resourcegroup") + + @Command(description = "Creates a new resourcegroup") private class Create extends CliCommand { - @Parameter(description = "resourcegroup-name", required = true) - private java.util.List params; + @Parameters(description = "resourcegroup-name", arity = "1") + private String resourceGroupName; - @Parameter(names = { "--msg-publish-rate", + @Option(names = { "--msg-publish-rate", "-mp" }, description = "message-publish-rate " + "(default -1 will be overwrite if not passed)", required = false) private Integer publishRateInMsgs; - @Parameter(names = { "--byte-publish-rate", + @Option(names = { "--byte-publish-rate", "-bp" }, description = "byte-publish-rate " + "(default -1 will be overwrite if not passed)", required = false) private Long publishRateInBytes; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private Integer dispatchRateInMsgs; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private Long dispatchRateInBytes; @Override void run() throws PulsarAdminException { - String name = getOneArgument(params); - ResourceGroup resourcegroup = new ResourceGroup(); resourcegroup.setDispatchRateInMsgs(dispatchRateInMsgs); resourcegroup.setDispatchRateInBytes(dispatchRateInBytes); resourcegroup.setPublishRateInMsgs(publishRateInMsgs); resourcegroup.setPublishRateInBytes(publishRateInBytes); - getAdmin().resourcegroups().createResourceGroup(name, resourcegroup); + getAdmin().resourcegroups().createResourceGroup(resourceGroupName, resourcegroup); } } - @Parameters(commandDescription = "Updates a resourcegroup") + @Command(description = "Updates a resourcegroup") private class Update extends CliCommand { - @Parameter(description = "resourcegroup-name", required = true) - private java.util.List params; + @Parameters(description = "resourcegroup-name", arity = "1") + private String resourceGroupName; - @Parameter(names = { "--msg-publish-rate", + @Option(names = { "--msg-publish-rate", "-mp" }, description = "message-publish-rate ", required = false) private Integer publishRateInMsgs; - @Parameter(names = { "--byte-publish-rate", + @Option(names = { "--byte-publish-rate", "-bp" }, description = "byte-publish-rate ", required = false) private Long publishRateInBytes; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate ", required = false) private Integer dispatchRateInMsgs; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate ", required = false) private Long dispatchRateInBytes; @Override void run() throws PulsarAdminException { - String name = getOneArgument(params); - ResourceGroup resourcegroup = new ResourceGroup(); resourcegroup.setDispatchRateInMsgs(dispatchRateInMsgs); resourcegroup.setDispatchRateInBytes(dispatchRateInBytes); resourcegroup.setPublishRateInMsgs(publishRateInMsgs); resourcegroup.setPublishRateInBytes(publishRateInBytes); - getAdmin().resourcegroups().updateResourceGroup(name, resourcegroup); + getAdmin().resourcegroups().updateResourceGroup(resourceGroupName, resourcegroup); } } - @Parameters(commandDescription = "Deletes an existing ResourceGroup") + @Command(description = "Deletes an existing ResourceGroup") private class Delete extends CliCommand { - @Parameter(description = "resourcegroup-name", required = true) - private java.util.List params; + @Parameters(description = "resourcegroup-name", arity = "1") + private String resourceGroupName; @Override void run() throws PulsarAdminException { - String name = getOneArgument(params); - getAdmin().resourcegroups().deleteResourceGroup(name); + getAdmin().resourcegroups().deleteResourceGroup(resourceGroupName); } } public CmdResourceGroups(Supplier admin) { super("resourcegroups", admin); - jcommander.addCommand("list", new CmdResourceGroups.List()); - jcommander.addCommand("get", new CmdResourceGroups.Get()); - jcommander.addCommand("create", new CmdResourceGroups.Create()); - jcommander.addCommand("update", new CmdResourceGroups.Update()); - jcommander.addCommand("delete", new CmdResourceGroups.Delete()); + addCommand("list", new CmdResourceGroups.List()); + addCommand("get", new CmdResourceGroups.Get()); + addCommand("create", new CmdResourceGroups.Create()); + addCommand("update", new CmdResourceGroups.Update()); + addCommand("delete", new CmdResourceGroups.Delete()); } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceQuotas.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceQuotas.java index 25940f52a1be3..ad8c432b124cf 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceQuotas.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdResourceQuotas.java @@ -18,35 +18,34 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import java.util.function.Supplier; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.policies.data.ResourceQuota; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -@Parameters(commandDescription = "Operations about resource quotas") +@Command(description = "Operations about resource quotas") public class CmdResourceQuotas extends CmdBase { - @Parameters(commandDescription = "Get the resource quota for specified namespace bundle, " + @Command(description = "Get the resource quota for specified namespace bundle, " + "or default quota if no namespace/bundle specified.") private class GetResourceQuota extends CliCommand { - @Parameter(names = { "--namespace", + @Option(names = { "--namespace", "-n" }, description = "tenant/namespace, must be specified together with '--bundle'") - private java.util.List names; + private String namespaceName; - @Parameter(names = { "--bundle", + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}, must be specified together with '--namespace'") private String bundle; @Override void run() throws PulsarAdminException, ParameterException { - if (bundle == null && names == null) { + if (bundle == null && namespaceName == null) { print(getAdmin().resourceQuotas().getDefaultResourceQuota()); - } else if (bundle != null && names != null) { - String namespace = validateNamespace(names); + } else if (bundle != null && namespaceName != null) { + String namespace = validateNamespace(namespaceName); print(getAdmin().resourceQuotas().getNamespaceBundleResourceQuota(namespace, bundle)); } else { throw new ParameterException("namespace and bundle must be provided together."); @@ -54,38 +53,38 @@ void run() throws PulsarAdminException, ParameterException { } } - @Parameters(commandDescription = "Set the resource quota for specified namespace bundle, " + @Command(description = "Set the resource quota for specified namespace bundle, " + "or default quota if no namespace/bundle specified.") private class SetResourceQuota extends CliCommand { - @Parameter(names = { "--namespace", + @Option(names = { "--namespace", "-n" }, description = "tenant/namespace, must be specified together with '--bundle'") - private java.util.List names; + private String namespaceName; - @Parameter(names = { "--bundle", + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}, must be specified together with '--namespace'") private String bundle; - @Parameter(names = { "--msgRateIn", + @Option(names = { "--msgRateIn", "-mi" }, description = "expected incoming messages per second", required = true) private long msgRateIn = 0; - @Parameter(names = { "--msgRateOut", + @Option(names = { "--msgRateOut", "-mo" }, description = "expected outgoing messages per second", required = true) private long msgRateOut = 0; - @Parameter(names = { "--bandwidthIn", - "-bi" }, description = "expected inbound bandwidth (bytes/second)", required = true) + @Option(names = {"--bandwidthIn", + "-bi"}, description = "expected inbound bandwidth (bytes/second)", required = true) private long bandwidthIn = 0; - @Parameter(names = { "--bandwidthOut", + @Option(names = { "--bandwidthOut", "-bo" }, description = "expected outbound bandwidth (bytes/second)", required = true) private long bandwidthOut = 0; - @Parameter(names = { "--memory", "-mem" }, description = "expected memory usage (Mbytes)", required = true) + @Option(names = { "--memory", "-mem" }, description = "expected memory usage (Mbytes)", required = true) private long memory = 0; - @Parameter(names = { "--dynamic", + @Option(names = { "--dynamic", "-d" }, description = "dynamic (allow to be dynamically re-calculated) or not") private boolean dynamic = false; @@ -99,10 +98,10 @@ void run() throws PulsarAdminException { quota.setMemory(memory); quota.setDynamic(dynamic); - if (bundle == null && names == null) { + if (bundle == null && namespaceName == null) { getAdmin().resourceQuotas().setDefaultResourceQuota(quota); - } else if (bundle != null && names != null) { - String namespace = validateNamespace(names); + } else if (bundle != null && namespaceName != null) { + String namespace = validateNamespace(namespaceName); getAdmin().resourceQuotas().setNamespaceBundleResourceQuota(namespace, bundle, quota); } else { throw new ParameterException("namespace and bundle must be provided together."); @@ -110,26 +109,26 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Reset the specified namespace bundle's resource quota to default value.") + @Command(description = "Reset the specified namespace bundle's resource quota to default value.") private class ResetNamespaceBundleResourceQuota extends CliCommand { - @Parameter(names = { "--namespace", "-n" }, description = "tenant/namespace", required = true) - private java.util.List names; + @Option(names = { "--namespace", "-n" }, description = "tenant/namespace", required = true) + private String namespaceName; - @Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}", required = true) + @Option(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}", required = true) private String bundle; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(names); + String namespace = validateNamespace(namespaceName); getAdmin().resourceQuotas().resetNamespaceBundleResourceQuota(namespace, bundle); } } public CmdResourceQuotas(Supplier admin) { super("resource-quotas", admin); - jcommander.addCommand("get", new GetResourceQuota()); - jcommander.addCommand("set", new SetResourceQuota()); - jcommander.addCommand("reset-namespace-bundle-quota", new ResetNamespaceBundleResourceQuota()); + addCommand("get", new GetResourceQuota()); + addCommand("set", new SetResourceQuota()); + addCommand("reset-namespace-bundle-quota", new ResetNamespaceBundleResourceQuota()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSchemas.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSchemas.java index 638e9b1840a88..ab8fdc1f01359 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSchemas.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSchemas.java @@ -18,9 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.FileNotFoundException; @@ -33,34 +30,37 @@ import org.apache.pulsar.client.api.schema.SchemaDefinition; import org.apache.pulsar.common.protocol.schema.PostSchemaPayload; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about schemas") +@Command(description = "Operations about schemas") public class CmdSchemas extends CmdBase { private static final ObjectMapper MAPPER = ObjectMapperFactory.create(); public CmdSchemas(Supplier admin) { super("schemas", admin); - jcommander.addCommand("get", new GetSchema()); - jcommander.addCommand("delete", new DeleteSchema()); - jcommander.addCommand("upload", new UploadSchema()); - jcommander.addCommand("extract", new ExtractSchema()); - jcommander.addCommand("compatibility", new TestCompatibility()); + addCommand("get", new GetSchema()); + addCommand("delete", new DeleteSchema()); + addCommand("upload", new UploadSchema()); + addCommand("extract", new ExtractSchema()); + addCommand("compatibility", new TestCompatibility()); } - @Parameters(commandDescription = "Get the schema for a topic") + @Command(description = "Get the schema for a topic") private class GetSchema extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-v", "--version"}, description = "version", required = false) + @Option(names = {"-v", "--version"}, description = "version", required = false) private Long version; - @Parameter(names = {"-a", "--all-version"}, description = "all version", required = false) + @Option(names = {"-a", "--all-version"}, description = "all version", required = false) private boolean all = false; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); if (version != null && all) { throw new ParameterException("Only one or neither of --version and --all-version can be specified."); } @@ -77,12 +77,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Delete all versions schema of a topic") + @Command(description = "Delete all versions schema of a topic") private class DeleteSchema extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "whether to delete schema completely. If true, delete " + "all resources (including metastore and ledger), otherwise only do a mark deletion" + " and not remove any resources indeed") @@ -90,22 +90,22 @@ private class DeleteSchema extends CliCommand { @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getAdmin().schemas().deleteSchema(topic, force); } } - @Parameters(commandDescription = "Update the schema for a topic") + @Command(description = "Update the schema for a topic") private class UploadSchema extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", "--filename" }, description = "filename", required = true) + @Option(names = { "-f", "--filename" }, description = "filename", required = true) private String schemaFileName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); Path schemaPath = Path.of(schemaFileName); File schemaFile = schemaPath.toFile(); if (!schemaFile.exists()) { @@ -123,31 +123,31 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Provide the schema via a topic") + @Command(description = "Provide the schema via a topic") private class ExtractSchema extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-j", "--jar" }, description = "jar filepath", required = true) + @Option(names = { "-j", "--jar" }, description = "jar filepath", required = true) private String jarFilePath; - @Parameter(names = { "-t", "--type" }, description = "type avro or json", required = true) + @Option(names = { "-t", "--type" }, description = "type avro or json", required = true) private String type; - @Parameter(names = { "-c", "--classname" }, description = "class name of pojo", required = true) + @Option(names = { "-c", "--classname" }, description = "class name of pojo", required = true) private String className; - @Parameter(names = {"-a", "--always-allow-null"}, arity = 1, + @Option(names = {"-a", "--always-allow-null"}, arity = "1", description = "set schema whether always allow null or not") private boolean alwaysAllowNull = true; - @Parameter(names = { "-n", "--dry-run"}, + @Option(names = { "-n", "--dry-run"}, description = "dost not apply to schema registry, just prints the post schema payload") private boolean dryRun = false; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); File file = new File(jarFilePath); ClassLoader cl = new URLClassLoader(new URL[]{ file.toURI().toURL() }); @@ -179,17 +179,17 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Test schema compatibility") + @Command(description = "Test schema compatibility") private class TestCompatibility extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", "--filename" }, description = "filename", required = true) + @Option(names = { "-f", "--filename" }, description = "filename", required = true) private String schemaFileName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); PostSchemaPayload input = MAPPER.readValue(new File(schemaFileName), PostSchemaPayload.class); getAdmin().schemas().testCompatibility(topic, input); } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java index 66b2816e77705..47af7e6794ca2 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java @@ -22,10 +22,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.pulsar.common.naming.TopicName.DEFAULT_NAMESPACE; import static org.apache.pulsar.common.naming.TopicName.PUBLIC_TENANT; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.StringConverter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -60,9 +56,11 @@ import org.apache.pulsar.common.io.ConnectorDefinition; import org.apache.pulsar.common.io.SinkConfig; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Getter -@Parameters(commandDescription = "Interface for managing Pulsar IO sinks (egress data from Pulsar)") +@Command(description = "Interface for managing Pulsar IO sinks (egress data from Pulsar)", aliases = "sink") @Slf4j public class CmdSinks extends CmdBase { @@ -90,19 +88,19 @@ public CmdSinks(Supplier admin) { restartSink = new RestartSink(); localSinkRunner = new LocalSinkRunner(); - jcommander.addCommand("create", createSink); - jcommander.addCommand("update", updateSink); - jcommander.addCommand("delete", deleteSink); - jcommander.addCommand("list", listSinks); - jcommander.addCommand("get", getSink); + addCommand("create", createSink); + addCommand("update", updateSink); + addCommand("delete", deleteSink); + addCommand("list", listSinks); + addCommand("get", getSink); // TODO deprecate getstatus - jcommander.addCommand("status", getSinkStatus, "getstatus"); - jcommander.addCommand("stop", stopSink); - jcommander.addCommand("start", startSink); - jcommander.addCommand("restart", restartSink); - jcommander.addCommand("localrun", localSinkRunner); - jcommander.addCommand("available-sinks", new ListBuiltInSinks()); - jcommander.addCommand("reload", new ReloadBuiltInSinks()); + addCommand("status", getSinkStatus, "getstatus"); + addCommand("stop", stopSink); + addCommand("start", startSink); + addCommand("restart", restartSink); + addCommand("localrun", localSinkRunner); + addCommand("available-sinks", new ListBuiltInSinks()); + addCommand("reload", new ReloadBuiltInSinks()); } /** @@ -112,13 +110,7 @@ public CmdSinks(Supplier admin) { abstract class BaseCommand extends CliCommand { @Override void run() throws Exception { - try { - processArguments(); - } catch (Exception e) { - String chosenCommand = jcommander.getParsedCommand(); - getUsageFormatter().usage(chosenCommand); - throw e; - } + processArguments(); runCmd(); } @@ -128,57 +120,57 @@ void processArguments() throws Exception { abstract void runCmd() throws Exception; } - @Parameters(commandDescription = "Run a Pulsar IO sink connector locally " + @Command(description = "Run a Pulsar IO sink connector locally " + "(rather than deploying it to the Pulsar cluster)") protected class LocalSinkRunner extends CreateSink { - @Parameter(names = "--state-storage-service-url", + @Option(names = "--state-storage-service-url", description = "The URL for the state storage service (the default is Apache BookKeeper)") protected String stateStorageServiceUrl; - @Parameter(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) + @Option(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) protected String deprecatedBrokerServiceUrl; - @Parameter(names = "--broker-service-url", description = "The URL for the Pulsar broker") + @Option(names = "--broker-service-url", description = "The URL for the Pulsar broker") protected String brokerServiceUrl; - @Parameter(names = "--clientAuthPlugin", description = "Client authentication plugin using " + @Option(names = "--clientAuthPlugin", description = "Client authentication plugin using " + "which function-process can connect to broker", hidden = true) protected String deprecatedClientAuthPlugin; - @Parameter(names = "--client-auth-plugin", + @Option(names = "--client-auth-plugin", description = "Client authentication plugin using which function-process can connect to broker") protected String clientAuthPlugin; - @Parameter(names = "--clientAuthParams", description = "Client authentication param", hidden = true) + @Option(names = "--clientAuthParams", description = "Client authentication param", hidden = true) protected String deprecatedClientAuthParams; - @Parameter(names = "--client-auth-params", description = "Client authentication param") + @Option(names = "--client-auth-params", description = "Client authentication param") protected String clientAuthParams; - @Parameter(names = "--use_tls", description = "Use tls connection", hidden = true) + @Option(names = "--use_tls", description = "Use tls connection", hidden = true) protected Boolean deprecatedUseTls; - @Parameter(names = "--use-tls", description = "Use tls connection") + @Option(names = "--use-tls", description = "Use tls connection") protected boolean useTls; - @Parameter(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) + @Option(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) protected Boolean deprecatedTlsAllowInsecureConnection; - @Parameter(names = "--tls-allow-insecure", description = "Allow insecure tls connection") + @Option(names = "--tls-allow-insecure", description = "Allow insecure tls connection") protected boolean tlsAllowInsecureConnection; - @Parameter(names = "--hostname_verification_enabled", + @Option(names = "--hostname_verification_enabled", description = "Enable hostname verification", hidden = true) protected Boolean deprecatedTlsHostNameVerificationEnabled; - @Parameter(names = "--hostname-verification-enabled", description = "Enable hostname verification") + @Option(names = "--hostname-verification-enabled", description = "Enable hostname verification") protected boolean tlsHostNameVerificationEnabled; - @Parameter(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) + @Option(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) protected String deprecatedTlsTrustCertFilePath; - @Parameter(names = "--tls-trust-cert-path", description = "tls trust cert file path") + @Option(names = "--tls-trust-cert-path", description = "tls trust cert file path") protected String tlsTrustCertFilePath; - @Parameter(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider") + @Option(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider") protected String secretsProviderClassName; - @Parameter(names = "--secrets-provider-config", + @Option(names = "--secrets-provider-config", description = "Config that needs to be passed to secrets provider") protected String secretsProviderConfig; - @Parameter(names = "--metrics-port-start", description = "The starting port range for metrics server") + @Option(names = "--metrics-port-start", description = "The starting port range for metrics server") protected String metricsPortStart; private void mergeArgs() { @@ -237,7 +229,7 @@ protected String validateSinkType(String sinkType) { } } - @Parameters(commandDescription = "Submit a Pulsar IO sink connector to run in a Pulsar cluster") + @Command(description = "Submit a Pulsar IO sink connector to run in a Pulsar cluster") protected class CreateSink extends SinkDetailsCommand { @Override void runCmd() throws Exception { @@ -250,10 +242,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Update a Pulsar IO sink connector") + @Command(description = "Update a Pulsar IO sink connector") protected class UpdateSink extends SinkDetailsCommand { - @Parameter(names = "--update-auth-data", description = "Whether or not to update the auth data") + @Option(names = "--update-auth-data", description = "Whether or not to update the auth data") protected boolean updateAuthData; @Override @@ -279,138 +271,138 @@ protected void validateSinkConfigs(SinkConfig sinkConfig) { } abstract class SinkDetailsCommand extends BaseCommand { - @Parameter(names = "--tenant", description = "The sink's tenant") + @Option(names = "--tenant", description = "The sink's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The sink's namespace") + @Option(names = "--namespace", description = "The sink's namespace") protected String namespace; - @Parameter(names = "--name", description = "The sink's name") + @Option(names = "--name", description = "The sink's name") protected String name; - @Parameter(names = { "-t", "--sink-type" }, description = "The sinks's connector provider") + @Option(names = { "-t", "--sink-type" }, description = "The sinks's connector provider") protected String sinkType; - @Parameter(names = "--cleanup-subscription", description = "Whether delete the subscription " + @Option(names = "--cleanup-subscription", description = "Whether delete the subscription " + "when sink is deleted") protected Boolean cleanupSubscription; - @Parameter(names = { "-i", + @Option(names = { "-i", "--inputs" }, description = "The sink's input topic or topics " + "(multiple topics can be specified as a comma-separated list)") protected String inputs; - @Parameter(names = "--topicsPattern", description = "TopicsPattern to consume from list of topics " + @Option(names = "--topicsPattern", description = "TopicsPattern to consume from list of topics " + "under a namespace that match the pattern. [--input] and [--topicsPattern] are mutually exclusive. " + "Add SerDe class name for a pattern in --customSerdeInputs (supported for java fun only)", hidden = true) protected String deprecatedTopicsPattern; - @Parameter(names = "--topics-pattern", description = "The topic pattern to consume from a list of topics " + @Option(names = "--topics-pattern", description = "The topic pattern to consume from a list of topics " + "under a namespace that matches the pattern. [--input] and [--topics-pattern] are mutually " + "exclusive. Add SerDe class name for a pattern in --custom-serde-inputs") protected String topicsPattern; - @Parameter(names = "--subsName", description = "Pulsar source subscription name " + @Option(names = "--subsName", description = "Pulsar source subscription name " + "if user wants a specific subscription-name for input-topic consumer", hidden = true) protected String deprecatedSubsName; - @Parameter(names = "--subs-name", description = "Pulsar source subscription name " + @Option(names = "--subs-name", description = "Pulsar source subscription name " + "if user wants a specific subscription-name for input-topic consumer") protected String subsName; - @Parameter(names = "--subs-position", description = "Pulsar source subscription position " + @Option(names = "--subs-position", description = "Pulsar source subscription position " + "if user wants to consume messages from the specified location") protected SubscriptionInitialPosition subsPosition; - @Parameter(names = "--customSerdeInputs", + @Option(names = "--customSerdeInputs", description = "The map of input topics to SerDe class names (as a JSON string)", hidden = true) protected String deprecatedCustomSerdeInputString; - @Parameter(names = "--custom-serde-inputs", + @Option(names = "--custom-serde-inputs", description = "The map of input topics to SerDe class names (as a JSON string)") protected String customSerdeInputString; - @Parameter(names = "--custom-schema-inputs", + @Option(names = "--custom-schema-inputs", description = "The map of input topics to Schema types or class names (as a JSON string)") protected String customSchemaInputString; - @Parameter(names = "--input-specs", + @Option(names = "--input-specs", description = "The map of inputs to custom configuration (as a JSON string)") protected String inputSpecs; - @Parameter(names = "--max-redeliver-count", description = "Maximum number of times that a message " + @Option(names = "--max-redeliver-count", description = "Maximum number of times that a message " + "will be redelivered before being sent to the dead letter queue") protected Integer maxMessageRetries; - @Parameter(names = "--dead-letter-topic", + @Option(names = "--dead-letter-topic", description = "Name of the dead topic where the failing messages will be sent.") protected String deadLetterTopic; - @Parameter(names = "--processingGuarantees", + @Option(names = "--processingGuarantees", description = "The processing guarantees (aka delivery semantics) applied to the sink", hidden = true) protected FunctionConfig.ProcessingGuarantees deprecatedProcessingGuarantees; - @Parameter(names = "--processing-guarantees", + @Option(names = "--processing-guarantees", description = "The processing guarantees (as known as delivery semantics) applied to the sink." + " The '--processing-guarantees' implementation in Pulsar also relies on sink implementation." + " The available values are `ATLEAST_ONCE`, `ATMOST_ONCE`, `EFFECTIVELY_ONCE`." + " If it is not specified, `ATLEAST_ONCE` delivery guarantee is used.") protected FunctionConfig.ProcessingGuarantees processingGuarantees; - @Parameter(names = "--retainOrdering", description = "Sink consumes and sinks messages in order", hidden = true) + @Option(names = "--retainOrdering", description = "Sink consumes and sinks messages in order", hidden = true) protected Boolean deprecatedRetainOrdering; - @Parameter(names = "--retain-ordering", description = "Sink consumes and sinks messages in order") + @Option(names = "--retain-ordering", description = "Sink consumes and sinks messages in order") protected Boolean retainOrdering; - @Parameter(names = "--parallelism", + @Option(names = "--parallelism", description = "The sink's parallelism factor (i.e. the number of sink instances to run)") protected Integer parallelism; - @Parameter(names = "--retain-key-ordering", + @Option(names = "--retain-key-ordering", description = "Sink consumes and processes messages in key order") protected Boolean retainKeyOrdering; - @Parameter(names = {"-a", "--archive"}, description = "Path to the archive file for the sink. It also supports " + @Option(names = {"-a", "--archive"}, description = "Path to the archive file for the sink. It also supports " + "url-path [http/https/file (file protocol assumes that file already exists on worker host)] from " - + "which worker can download the package.", listConverter = StringConverter.class) + + "which worker can download the package.") protected String archive; - @Parameter(names = "--className", + @Option(names = "--className", description = "The sink's class name if archive is file-url-path (file://)", hidden = true) protected String deprecatedClassName; - @Parameter(names = "--classname", description = "The sink's class name if archive is file-url-path (file://)") + @Option(names = "--classname", description = "The sink's class name if archive is file-url-path (file://)") protected String className; - @Parameter(names = "--sinkConfigFile", description = "The path to a YAML config file specifying the " + @Option(names = "--sinkConfigFile", description = "The path to a YAML config file specifying the " + "sink's configuration", hidden = true) protected String deprecatedSinkConfigFile; - @Parameter(names = "--sink-config-file", description = "The path to a YAML config file specifying the " + @Option(names = "--sink-config-file", description = "The path to a YAML config file specifying the " + "sink's configuration") protected String sinkConfigFile; - @Parameter(names = "--cpu", description = "The CPU (in cores) that needs to be allocated " + @Option(names = "--cpu", description = "The CPU (in cores) that needs to be allocated " + "per sink instance (applicable only to Docker runtime)") protected Double cpu; - @Parameter(names = "--ram", description = "The RAM (in bytes) that need to be allocated " + @Option(names = "--ram", description = "The RAM (in bytes) that need to be allocated " + "per sink instance (applicable only to the process and Docker runtimes)") protected Long ram; - @Parameter(names = "--disk", description = "The disk (in bytes) that need to be allocated " + @Option(names = "--disk", description = "The disk (in bytes) that need to be allocated " + "per sink instance (applicable only to Docker runtime)") protected Long disk; - @Parameter(names = "--sinkConfig", description = "User defined configs key/values", hidden = true) + @Option(names = "--sinkConfig", description = "User defined configs key/values", hidden = true) protected String deprecatedSinkConfigString; - @Parameter(names = "--sink-config", description = "User defined configs key/values") + @Option(names = "--sink-config", description = "User defined configs key/values") protected String sinkConfigString; - @Parameter(names = "--auto-ack", - description = "Whether or not the framework will automatically acknowledge messages", arity = 1) + @Option(names = "--auto-ack", + description = "Whether or not the framework will automatically acknowledge messages", arity = "1") protected Boolean autoAck; - @Parameter(names = "--timeout-ms", description = "The message timeout in milliseconds") + @Option(names = "--timeout-ms", description = "The message timeout in milliseconds") protected Long timeoutMs; - @Parameter(names = "--negative-ack-redelivery-delay-ms", + @Option(names = "--negative-ack-redelivery-delay-ms", description = "The negative ack message redelivery delay in milliseconds") protected Long negativeAckRedeliveryDelayMs; - @Parameter(names = "--custom-runtime-options", description = "A string that encodes options to " + @Option(names = "--custom-runtime-options", description = "A string that encodes options to " + "customize the runtime, see docs for configured runtime for details") protected String customRuntimeOptions; - @Parameter(names = "--secrets", description = "The map of secretName to an object that encapsulates " + @Option(names = "--secrets", description = "The map of secretName to an object that encapsulates " + "how the secret is fetched by the underlying secrets provider") protected String secretsString; - @Parameter(names = "--transform-function", description = "Transform function applied before the Sink") + @Option(names = "--transform-function", description = "Transform function applied before the Sink") protected String transformFunction; - @Parameter(names = "--transform-function-classname", description = "The transform function class name") + @Option(names = "--transform-function-classname", description = "The transform function class name") protected String transformFunctionClassName; - @Parameter(names = "--transform-function-config", description = "Configuration of the transform function " + @Option(names = "--transform-function-config", description = "Configuration of the transform function " + "applied before the Sink") protected String transformFunctionConfig; - @Parameter(names = "--log-topic", description = "The topic to which the logs of a Pulsar Sink are produced") + @Option(names = "--log-topic", description = "The topic to which the logs of a Pulsar Sink are produced") protected String logTopic; protected SinkConfig sinkConfig; @@ -570,7 +562,7 @@ void processArguments() throws Exception { sinkConfig.setConfigs(parseConfigs(sinkConfigString)); } } catch (Exception ex) { - throw new ParameterException("Cannot parse sink-config", ex); + throw new IllegalArgumentException("Cannot parse sink-config", ex); } if (autoAck != null) { @@ -663,13 +655,13 @@ protected String validateSinkType(String sinkType) throws IOException { */ @Getter abstract class SinkCommand extends BaseCommand { - @Parameter(names = "--tenant", description = "The sink's tenant") + @Option(names = "--tenant", description = "The sink's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The sink's namespace") + @Option(names = "--namespace", description = "The sink's namespace") protected String namespace; - @Parameter(names = "--name", description = "The sink's name") + @Option(names = "--name", description = "The sink's name") protected String sinkName; @Override @@ -688,7 +680,7 @@ void processArguments() throws Exception { } } - @Parameters(commandDescription = "Stops a Pulsar IO sink connector") + @Command(description = "Stops a Pulsar IO sink connector") protected class DeleteSink extends SinkCommand { @Override @@ -698,7 +690,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Gets the information about a Pulsar IO sink connector") + @Command(description = "Gets the information about a Pulsar IO sink connector") protected class GetSink extends SinkCommand { @Override @@ -712,12 +704,12 @@ void runCmd() throws Exception { /** * List Sources command. */ - @Parameters(commandDescription = "List all running Pulsar IO sink connectors") + @Command(description = "List all running Pulsar IO sink connectors") protected class ListSinks extends BaseCommand { - @Parameter(names = "--tenant", description = "The sink's tenant") + @Option(names = "--tenant", description = "The sink's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The sink's namespace") + @Option(names = "--namespace", description = "The sink's namespace") protected String namespace; @Override @@ -738,10 +730,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Check the current status of a Pulsar Sink") + @Command(description = "Check the current status of a Pulsar Sink") class GetSinkStatus extends SinkCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The sink instanceId (Get-status of all instances if instance-id is not provided") protected String instanceId; @@ -755,10 +747,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Restart sink instance") + @Command(description = "Restart sink instance") class RestartSink extends SinkCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The sink instanceId (restart all instances if instance-id is not provided") protected String instanceId; @@ -777,10 +769,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Stops sink instance") + @Command(description = "Stops sink instance") class StopSink extends SinkCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The sink instanceId (stop all instances if instance-id is not provided") protected String instanceId; @@ -799,10 +791,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Starts sink instance") + @Command(description = "Starts sink instance") class StartSink extends SinkCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The sink instanceId (start all instances if instance-id is not provided") protected String instanceId; @@ -821,7 +813,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get the list of Pulsar IO connector sinks supported by Pulsar cluster") + @Command(description = "Get the list of Pulsar IO connector sinks supported by Pulsar cluster") public class ListBuiltInSinks extends BaseCommand { @Override void runCmd() throws Exception { @@ -834,7 +826,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Reload the available built-in connectors") + @Command(description = "Reload the available built-in connectors") public class ReloadBuiltInSinks extends BaseCommand { @Override diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSources.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSources.java index c94fd49d71748..03df3903a6c16 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSources.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSources.java @@ -22,10 +22,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.pulsar.common.naming.TopicName.DEFAULT_NAMESPACE; import static org.apache.pulsar.common.naming.TopicName.PUBLIC_TENANT; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.StringConverter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -60,9 +56,11 @@ import org.apache.pulsar.common.io.ConnectorDefinition; import org.apache.pulsar.common.io.SourceConfig; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Getter -@Parameters(commandDescription = "Interface for managing Pulsar IO Sources (ingress data into Pulsar)") +@Command(description = "Interface for managing Pulsar IO Sources (ingress data into Pulsar)", aliases = "source") @Slf4j public class CmdSources extends CmdBase { @@ -90,19 +88,19 @@ public CmdSources(Supplier admin) { startSource = new StartSource(); localSourceRunner = new LocalSourceRunner(); - jcommander.addCommand("create", createSource); - jcommander.addCommand("update", updateSource); - jcommander.addCommand("delete", deleteSource); - jcommander.addCommand("get", getSource); + addCommand("create", createSource); + addCommand("update", updateSource); + addCommand("delete", deleteSource); + addCommand("get", getSource); // TODO depecreate getstatus - jcommander.addCommand("status", getSourceStatus, "getstatus"); - jcommander.addCommand("list", listSources); - jcommander.addCommand("stop", stopSource); - jcommander.addCommand("start", startSource); - jcommander.addCommand("restart", restartSource); - jcommander.addCommand("localrun", localSourceRunner); - jcommander.addCommand("available-sources", new ListBuiltInSources()); - jcommander.addCommand("reload", new ReloadBuiltInSources()); + addCommand("status", getSourceStatus, "getstatus"); + addCommand("list", listSources); + addCommand("stop", stopSource); + addCommand("start", startSource); + addCommand("restart", restartSource); + addCommand("localrun", localSourceRunner); + addCommand("available-sources", new ListBuiltInSources()); + addCommand("reload", new ReloadBuiltInSources()); } /** @@ -112,13 +110,7 @@ public CmdSources(Supplier admin) { abstract class BaseCommand extends CliCommand { @Override void run() throws Exception { - try { - processArguments(); - } catch (Exception e) { - String chosenCommand = jcommander.getParsedCommand(); - getUsageFormatter().usage(chosenCommand); - throw e; - } + processArguments(); runCmd(); } @@ -128,58 +120,58 @@ void processArguments() throws Exception { abstract void runCmd() throws Exception; } - @Parameters(commandDescription = "Run a Pulsar IO source connector locally " + @Command(description = "Run a Pulsar IO source connector locally " + "(rather than deploying it to the Pulsar cluster)") protected class LocalSourceRunner extends CreateSource { - @Parameter(names = "--state-storage-service-url", + @Option(names = "--state-storage-service-url", description = "The URL for the state storage service (the default is Apache BookKeeper)") protected String stateStorageServiceUrl; - @Parameter(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) + @Option(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) protected String deprecatedBrokerServiceUrl; - @Parameter(names = "--broker-service-url", description = "The URL for the Pulsar broker") + @Option(names = "--broker-service-url", description = "The URL for the Pulsar broker") protected String brokerServiceUrl; - @Parameter(names = "--clientAuthPlugin", + @Option(names = "--clientAuthPlugin", description = "Client authentication plugin using which function-process can connect to broker", hidden = true) protected String deprecatedClientAuthPlugin; - @Parameter(names = "--client-auth-plugin", + @Option(names = "--client-auth-plugin", description = "Client authentication plugin using which function-process can connect to broker") protected String clientAuthPlugin; - @Parameter(names = "--clientAuthParams", description = "Client authentication param", hidden = true) + @Option(names = "--clientAuthParams", description = "Client authentication param", hidden = true) protected String deprecatedClientAuthParams; - @Parameter(names = "--client-auth-params", description = "Client authentication param") + @Option(names = "--client-auth-params", description = "Client authentication param") protected String clientAuthParams; - @Parameter(names = "--use_tls", description = "Use tls connection", hidden = true) + @Option(names = "--use_tls", description = "Use tls connection", hidden = true) protected Boolean deprecatedUseTls; - @Parameter(names = "--use-tls", description = "Use tls connection") + @Option(names = "--use-tls", description = "Use tls connection") protected boolean useTls; - @Parameter(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) + @Option(names = "--tls_allow_insecure", description = "Allow insecure tls connection", hidden = true) protected Boolean deprecatedTlsAllowInsecureConnection; - @Parameter(names = "--tls-allow-insecure", description = "Allow insecure tls connection") + @Option(names = "--tls-allow-insecure", description = "Allow insecure tls connection") protected boolean tlsAllowInsecureConnection; - @Parameter(names = "--hostname_verification_enabled", + @Option(names = "--hostname_verification_enabled", description = "Enable hostname verification", hidden = true) protected Boolean deprecatedTlsHostNameVerificationEnabled; - @Parameter(names = "--hostname-verification-enabled", description = "Enable hostname verification") + @Option(names = "--hostname-verification-enabled", description = "Enable hostname verification") protected boolean tlsHostNameVerificationEnabled; - @Parameter(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) + @Option(names = "--tls_trust_cert_path", description = "tls trust cert file path", hidden = true) protected String deprecatedTlsTrustCertFilePath; - @Parameter(names = "--tls-trust-cert-path", description = "tls trust cert file path") + @Option(names = "--tls-trust-cert-path", description = "tls trust cert file path") protected String tlsTrustCertFilePath; - @Parameter(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider") + @Option(names = "--secrets-provider-classname", description = "Whats the classname for secrets provider") protected String secretsProviderClassName; - @Parameter(names = "--secrets-provider-config", + @Option(names = "--secrets-provider-config", description = "Config that needs to be passed to secrets provider") protected String secretsProviderConfig; - @Parameter(names = "--metrics-port-start", description = "The starting port range for metrics server") + @Option(names = "--metrics-port-start", description = "The starting port range for metrics server") protected String metricsPortStart; private void mergeArgs() { @@ -239,7 +231,7 @@ protected String validateSourceType(String sourceType) { } } - @Parameters(commandDescription = "Submit a Pulsar IO source connector to run in a Pulsar cluster") + @Command(description = "Submit a Pulsar IO source connector to run in a Pulsar cluster") protected class CreateSource extends SourceDetailsCommand { @Override void runCmd() throws Exception { @@ -252,10 +244,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Update a Pulsar IO source connector") + @Command(description = "Update a Pulsar IO source connector") protected class UpdateSource extends SourceDetailsCommand { - @Parameter(names = "--update-auth-data", description = "Whether or not to update the auth data") + @Option(names = "--update-auth-data", description = "Whether or not to update the auth data") protected boolean updateAuthData; @Override @@ -281,20 +273,20 @@ protected void validateSourceConfigs(SourceConfig sourceConfig) { } abstract class SourceDetailsCommand extends BaseCommand { - @Parameter(names = "--tenant", description = "The source's tenant") + @Option(names = "--tenant", description = "The source's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The source's namespace") + @Option(names = "--namespace", description = "The source's namespace") protected String namespace; - @Parameter(names = "--name", description = "The source's name") + @Option(names = "--name", description = "The source's name") protected String name; - @Parameter(names = { "-t", "--source-type" }, description = "The source's connector provider") + @Option(names = {"-t", "--source-type"}, description = "The source's connector provider") protected String sourceType; - @Parameter(names = "--processingGuarantees", + @Option(names = "--processingGuarantees", description = "The processing guarantees (aka delivery semantics) applied to the Source", hidden = true) protected FunctionConfig.ProcessingGuarantees deprecatedProcessingGuarantees; - @Parameter(names = "--processing-guarantees", + @Option(names = "--processing-guarantees", description = "The processing guarantees (as known as delivery semantics) applied to the source." + " A source connector receives messages from external system and writes messages to a Pulsar" + " topic. The '--processing-guarantees' is used to ensure the processing guarantees for writing" @@ -302,70 +294,70 @@ abstract class SourceDetailsCommand extends BaseCommand { + " `EFFECTIVELY_ONCE`. If it is not specified, `ATLEAST_ONCE` delivery guarantee is used.") protected FunctionConfig.ProcessingGuarantees processingGuarantees; - @Parameter(names = { "-o", "--destinationTopicName" }, + @Option(names = { "-o", "--destinationTopicName" }, description = "The Pulsar topic to which data is sent", hidden = true) protected String deprecatedDestinationTopicName; - @Parameter(names = "--destination-topic-name", description = "The Pulsar topic to which data is sent") + @Option(names = "--destination-topic-name", description = "The Pulsar topic to which data is sent") protected String destinationTopicName; - @Parameter(names = "--producer-config", description = "The custom producer configuration (as a JSON string)") + @Option(names = "--producer-config", description = "The custom producer configuration (as a JSON string)") protected String producerConfig; - @Parameter(names = "--batch-builder", description = "BatchBuilder provides two types of " + @Option(names = "--batch-builder", description = "BatchBuilder provides two types of " + "batch construction methods, DEFAULT and KEY_BASED. The default value is: DEFAULT") protected String batchBuilder; - @Parameter(names = "--deserializationClassName", + @Option(names = "--deserializationClassName", description = "The SerDe classname for the source", hidden = true) protected String deprecatedDeserializationClassName; - @Parameter(names = "--deserialization-classname", description = "The SerDe classname for the source") + @Option(names = "--deserialization-classname", description = "The SerDe classname for the source") protected String deserializationClassName; - @Parameter(names = { "-st", + @Option(names = { "-st", "--schema-type" }, description = "The schema type (either a builtin schema like 'avro', 'json', etc.." - + " or custom Schema class name to be used to encode messages emitted from the source") + + " or custom Schema class name to be used to encode messages emitted from the source") protected String schemaType; - @Parameter(names = "--parallelism", + @Option(names = "--parallelism", description = "The source's parallelism factor (i.e. the number of source instances to run)") protected Integer parallelism; - @Parameter(names = { "-a", "--archive" }, + @Option(names = { "-a", "--archive" }, description = "The path to the NAR archive for the Source. It also supports url-path " + "[http/https/file (file protocol assumes that file already exists on worker host)] " - + "from which worker can download the package.", listConverter = StringConverter.class) + + "from which worker can download the package.") protected String archive; - @Parameter(names = "--className", + @Option(names = "--className", description = "The source's class name if archive is file-url-path (file://)", hidden = true) protected String deprecatedClassName; - @Parameter(names = "--classname", description = "The source's class name if archive is file-url-path (file://)") + @Option(names = "--classname", description = "The source's class name if archive is file-url-path (file://)") protected String className; - @Parameter(names = "--sourceConfigFile", description = "The path to a YAML config file specifying the " + @Option(names = "--sourceConfigFile", description = "The path to a YAML config file specifying the " + "source's configuration", hidden = true) protected String deprecatedSourceConfigFile; - @Parameter(names = "--source-config-file", description = "The path to a YAML config file specifying the " + @Option(names = "--source-config-file", description = "The path to a YAML config file specifying the " + "source's configuration") protected String sourceConfigFile; - @Parameter(names = "--cpu", description = "The CPU (in cores) that needs to be allocated " + @Option(names = "--cpu", description = "The CPU (in cores) that needs to be allocated " + "per source instance (applicable only to Docker runtime)") protected Double cpu; - @Parameter(names = "--ram", description = "The RAM (in bytes) that need to be allocated " + @Option(names = "--ram", description = "The RAM (in bytes) that need to be allocated " + "per source instance (applicable only to the process and Docker runtimes)") protected Long ram; - @Parameter(names = "--disk", description = "The disk (in bytes) that need to be allocated " + @Option(names = "--disk", description = "The disk (in bytes) that need to be allocated " + "per source instance (applicable only to Docker runtime)") protected Long disk; - @Parameter(names = "--sourceConfig", description = "Source config key/values", hidden = true) + @Option(names = "--sourceConfig", description = "Source config key/values", hidden = true) protected String deprecatedSourceConfigString; - @Parameter(names = "--source-config", description = "Source config key/values") + @Option(names = "--source-config", description = "Source config key/values") protected String sourceConfigString; - @Parameter(names = "--batch-source-config", description = "Batch source config key/values") + @Option(names = "--batch-source-config", description = "Batch source config key/values") protected String batchSourceConfigString; - @Parameter(names = "--custom-runtime-options", description = "A string that encodes options to " + @Option(names = "--custom-runtime-options", description = "A string that encodes options to " + "customize the runtime, see docs for configured runtime for details") protected String customRuntimeOptions; - @Parameter(names = "--secrets", description = "The map of secretName to an object that encapsulates " + @Option(names = "--secrets", description = "The map of secretName to an object that encapsulates " + "how the secret is fetched by the underlying secrets provider") protected String secretsString; - @Parameter(names = "--log-topic", description = "The topic to which the logs of a Pulsar Sink are produced") + @Option(names = "--log-topic", description = "The topic to which the logs of a Pulsar Sink are produced") protected String logTopic; protected SourceConfig sourceConfig; @@ -572,13 +564,13 @@ protected String validateSourceType(String sourceType) throws IOException { */ @Getter abstract class SourceCommand extends BaseCommand { - @Parameter(names = "--tenant", description = "The source's tenant") + @Option(names = "--tenant", description = "The source's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The source's namespace") + @Option(names = "--namespace", description = "The source's namespace") protected String namespace; - @Parameter(names = "--name", description = "The source's name") + @Option(names = "--name", description = "The source's name") protected String sourceName; @Override @@ -597,7 +589,7 @@ void processArguments() throws Exception { } } - @Parameters(commandDescription = "Stops a Pulsar IO source connector") + @Command(description = "Stops a Pulsar IO source connector") protected class DeleteSource extends SourceCommand { @Override @@ -607,7 +599,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Gets the information about a Pulsar IO source connector") + @Command(description = "Gets the information about a Pulsar IO source connector") protected class GetSource extends SourceCommand { @Override @@ -621,12 +613,12 @@ void runCmd() throws Exception { /** * List Sources command. */ - @Parameters(commandDescription = "List all running Pulsar IO source connectors") + @Command(description = "List all running Pulsar IO source connectors") protected class ListSources extends BaseCommand { - @Parameter(names = "--tenant", description = "The source's tenant") + @Option(names = "--tenant", description = "The source's tenant") protected String tenant; - @Parameter(names = "--namespace", description = "The source's namespace") + @Option(names = "--namespace", description = "The source's namespace") protected String namespace; @Override @@ -647,10 +639,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Check the current status of a Pulsar Source") + @Command(description = "Check the current status of a Pulsar Source") class GetSourceStatus extends SourceCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The source instanceId (Get-status of all instances if instance-id is not provided") protected String instanceId; @@ -665,10 +657,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Restart source instance") + @Command(description = "Restart source instance") class RestartSource extends SourceCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The source instanceId (restart all instances if instance-id is not provided") protected String instanceId; @@ -687,10 +679,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Stop source instance") + @Command(description = "Stop source instance") class StopSource extends SourceCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The source instanceId (stop all instances if instance-id is not provided") protected String instanceId; @@ -709,10 +701,10 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Start source instance") + @Command(description = "Start source instance") class StartSource extends SourceCommand { - @Parameter(names = "--instance-id", + @Option(names = "--instance-id", description = "The source instanceId (start all instances if instance-id is not provided") protected String instanceId; @@ -731,7 +723,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Get the list of Pulsar IO connector sources supported by Pulsar cluster") + @Command(description = "Get the list of Pulsar IO connector sources supported by Pulsar cluster") public class ListBuiltInSources extends BaseCommand { @Override void runCmd() throws Exception { @@ -744,7 +736,7 @@ void runCmd() throws Exception { } } - @Parameters(commandDescription = "Reload the available built-in connectors") + @Command(description = "Reload the available built-in connectors") public class ReloadBuiltInSources extends BaseCommand { @Override diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTenants.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTenants.java index 0d5502e506ceb..324686c3f261b 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTenants.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTenants.java @@ -18,9 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.converters.CommaParameterSplitter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -28,10 +25,13 @@ import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.policies.data.TenantInfoImpl; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations about tenants") +@Command(description = "Operations about tenants") public class CmdTenants extends CmdBase { - @Parameters(commandDescription = "List the existing tenants") + @Command(description = "List the existing tenants") private class List extends CliCommand { @Override void run() throws PulsarAdminException { @@ -39,38 +39,35 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Gets the configuration of a tenant") + @Command(description = "Gets the configuration of a tenant") private class Get extends CliCommand { - @Parameter(description = "tenant-name", required = true) - private java.util.List params; + @Parameters(description = "tenant-name", arity = "1") + private String tenant; @Override void run() throws PulsarAdminException { - String tenant = getOneArgument(params); print(getAdmin().tenants().getTenantInfo(tenant)); } } - @Parameters(commandDescription = "Creates a new tenant") + @Command(description = "Creates a new tenant") private class Create extends CliCommand { - @Parameter(description = "tenant-name", required = true) - private java.util.List params; + @Parameters(description = "tenant-name", arity = "1") + private String tenant; - @Parameter(names = { "--admin-roles", + @Option(names = { "--admin-roles", "-r" }, description = "Comma separated list of auth principal allowed to administrate the tenant", - required = false, splitter = CommaParameterSplitter.class) + required = false, split = ",") private java.util.List adminRoles; - @Parameter(names = { "--allowed-clusters", + @Option(names = { "--allowed-clusters", "-c" }, description = "Comma separated allowed clusters. " + "If empty, the tenant will have access to all clusters", - required = false, splitter = CommaParameterSplitter.class) + required = false, split = ",") private java.util.List allowedClusters; @Override void run() throws PulsarAdminException { - String tenant = getOneArgument(params); - if (adminRoles == null) { adminRoles = Collections.emptyList(); } @@ -85,27 +82,25 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Updates the configuration for a tenant") + @Command(description = "Updates the configuration for a tenant") private class Update extends CliCommand { - @Parameter(description = "tenant-name", required = true) - private java.util.List params; + @Parameters(description = "tenant-name", arity = "1") + private String tenant; - @Parameter(names = { "--admin-roles", + @Option(names = { "--admin-roles", "-r" }, description = "Comma separated list of auth principal allowed to administrate the tenant. " + "If empty the current set of roles won't be modified", - required = false, splitter = CommaParameterSplitter.class) + required = false, split = ",") private java.util.List adminRoles; - @Parameter(names = { "--allowed-clusters", + @Option(names = { "--allowed-clusters", "-c" }, description = "Comma separated allowed clusters. " + "If omitted, the current set of clusters will be preserved", - required = false, splitter = CommaParameterSplitter.class) + required = false, split = ",") private java.util.List allowedClusters; @Override void run() throws PulsarAdminException { - String tenant = getOneArgument(params); - if (adminRoles == null) { adminRoles = new ArrayList<>(getAdmin().tenants().getTenantInfo(tenant).getAdminRoles()); } @@ -119,41 +114,34 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Deletes an existing tenant") + @Command(description = "Deletes an existing tenant") private class Delete extends CliCommand { - @Parameter(description = "tenant-name", required = true) - private java.util.List params; + @Parameters(description = "tenant-name", arity = "1") + private String tenant; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Delete a tenant forcefully by deleting all namespaces under it.") private boolean force = false; @Override void run() throws PulsarAdminException { - String tenant = getOneArgument(params); getAdmin().tenants().deleteTenant(tenant, force); } } public CmdTenants(Supplier admin) { super("tenants", admin); - jcommander.addCommand("list", new List()); - jcommander.addCommand("get", new Get()); - jcommander.addCommand("create", new Create()); - jcommander.addCommand("update", new Update()); - jcommander.addCommand("delete", new Delete()); + addCommand("list", new List()); + addCommand("get", new Get()); + addCommand("create", new Create()); + addCommand("update", new Update()); + addCommand("delete", new Delete()); } - @Parameters(hidden = true) + @Command(hidden = true) static class CmdProperties extends CmdTenants { public CmdProperties(Supplier admin) { super(admin); } - - @Override - public boolean run(String[] args) { - System.err.println("WARN: The properties subcommand is deprecated. Please use tenants instead"); - return super.run(args); - } } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopicPolicies.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopicPolicies.java index c27cbd06849e7..3cc72db2e95f1 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopicPolicies.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopicPolicies.java @@ -20,9 +20,6 @@ import static org.apache.pulsar.admin.cli.utils.CmdUtils.maxValueCheck; import static org.apache.pulsar.admin.cli.utils.CmdUtils.positiveCheck; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.base.Strings; import java.util.Arrays; import java.util.HashSet; @@ -32,13 +29,10 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.cli.converters.ByteUnitIntegerConverter; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; -import org.apache.pulsar.cli.converters.TimeUnitToMillisConverter; -import org.apache.pulsar.cli.converters.TimeUnitToSecondsConverter; -import org.apache.pulsar.cli.validators.IntegerMaxValueLongValidator; -import org.apache.pulsar.cli.validators.MinNegativeOneValidator; -import org.apache.pulsar.cli.validators.NonNegativeValueValidator; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToIntegerConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToSecondsConverter; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.TopicPolicies; @@ -57,360 +51,362 @@ import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SchemaCompatibilityStrategy; import org.apache.pulsar.common.policies.data.SubscribeRate; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; -@Parameters(commandDescription = "Operations on persistent topics") +@Command(description = "Operations on persistent topics") public class CmdTopicPolicies extends CmdBase { public CmdTopicPolicies(Supplier admin) { super("topicPolicies", admin); - jcommander.addCommand("get-message-ttl", new GetMessageTTL()); - jcommander.addCommand("set-message-ttl", new SetMessageTTL()); - jcommander.addCommand("remove-message-ttl", new RemoveMessageTTL()); - - jcommander.addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesPerConsumer()); - jcommander.addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesPerConsumer()); - jcommander.addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesPerConsumer()); - - jcommander.addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); - jcommander.addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); - jcommander.addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); - jcommander.addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); - jcommander.addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); - jcommander.addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); - jcommander.addCommand("get-retention", new GetRetention()); - jcommander.addCommand("set-retention", new SetRetention()); - jcommander.addCommand("remove-retention", new RemoveRetention()); - jcommander.addCommand("get-backlog-quota", new GetBacklogQuotaMap()); - jcommander.addCommand("set-backlog-quota", new SetBacklogQuota()); - jcommander.addCommand("remove-backlog-quota", new RemoveBacklogQuota()); - - jcommander.addCommand("get-max-producers", new GetMaxProducers()); - jcommander.addCommand("set-max-producers", new SetMaxProducers()); - jcommander.addCommand("remove-max-producers", new RemoveMaxProducers()); - - jcommander.addCommand("get-max-message-size", new GetMaxMessageSize()); - jcommander.addCommand("set-max-message-size", new SetMaxMessageSize()); - jcommander.addCommand("remove-max-message-size", new RemoveMaxMessageSize()); - - jcommander.addCommand("set-deduplication", new SetDeduplicationStatus()); - jcommander.addCommand("get-deduplication", new GetDeduplicationStatus()); - jcommander.addCommand("remove-deduplication", new RemoveDeduplicationStatus()); - - jcommander.addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); - jcommander.addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); - jcommander.addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); - - jcommander.addCommand("get-persistence", new GetPersistence()); - jcommander.addCommand("set-persistence", new SetPersistence()); - jcommander.addCommand("remove-persistence", new RemovePersistence()); - - jcommander.addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); - jcommander.addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); - jcommander.addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); - - jcommander.addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); - jcommander.addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); - jcommander.addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); - - jcommander.addCommand("get-publish-rate", new GetPublishRate()); - jcommander.addCommand("set-publish-rate", new SetPublishRate()); - jcommander.addCommand("remove-publish-rate", new RemovePublishRate()); - - jcommander.addCommand("get-compaction-threshold", new GetCompactionThreshold()); - jcommander.addCommand("set-compaction-threshold", new SetCompactionThreshold()); - jcommander.addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); - - jcommander.addCommand("get-subscribe-rate", new GetSubscribeRate()); - jcommander.addCommand("set-subscribe-rate", new SetSubscribeRate()); - jcommander.addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); - - jcommander.addCommand("get-max-consumers", new GetMaxConsumers()); - jcommander.addCommand("set-max-consumers", new SetMaxConsumers()); - jcommander.addCommand("remove-max-consumers", new RemoveMaxConsumers()); - - jcommander.addCommand("get-delayed-delivery", new GetDelayedDelivery()); - jcommander.addCommand("set-delayed-delivery", new SetDelayedDelivery()); - jcommander.addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); - - jcommander.addCommand("get-dispatch-rate", new GetDispatchRate()); - jcommander.addCommand("set-dispatch-rate", new SetDispatchRate()); - jcommander.addCommand("remove-dispatch-rate", new RemoveDispatchRate()); - - jcommander.addCommand("get-offload-policies", new GetOffloadPolicies()); - jcommander.addCommand("set-offload-policies", new SetOffloadPolicies()); - jcommander.addCommand("remove-offload-policies", new RemoveOffloadPolicies()); - - jcommander.addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("remove-max-unacked-messages-per-subscription", + addCommand("get-message-ttl", new GetMessageTTL()); + addCommand("set-message-ttl", new SetMessageTTL()); + addCommand("remove-message-ttl", new RemoveMessageTTL()); + + addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesPerConsumer()); + addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesPerConsumer()); + addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesPerConsumer()); + + addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); + addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); + addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); + addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); + addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); + addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); + addCommand("get-retention", new GetRetention()); + addCommand("set-retention", new SetRetention()); + addCommand("remove-retention", new RemoveRetention()); + addCommand("get-backlog-quota", new GetBacklogQuotaMap()); + addCommand("set-backlog-quota", new SetBacklogQuota()); + addCommand("remove-backlog-quota", new RemoveBacklogQuota()); + + addCommand("get-max-producers", new GetMaxProducers()); + addCommand("set-max-producers", new SetMaxProducers()); + addCommand("remove-max-producers", new RemoveMaxProducers()); + + addCommand("get-max-message-size", new GetMaxMessageSize()); + addCommand("set-max-message-size", new SetMaxMessageSize()); + addCommand("remove-max-message-size", new RemoveMaxMessageSize()); + + addCommand("set-deduplication", new SetDeduplicationStatus()); + addCommand("get-deduplication", new GetDeduplicationStatus()); + addCommand("remove-deduplication", new RemoveDeduplicationStatus()); + + addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); + addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); + addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); + + addCommand("get-persistence", new GetPersistence()); + addCommand("set-persistence", new SetPersistence()); + addCommand("remove-persistence", new RemovePersistence()); + + addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); + addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); + addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); + + addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); + addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); + addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); + + addCommand("get-publish-rate", new GetPublishRate()); + addCommand("set-publish-rate", new SetPublishRate()); + addCommand("remove-publish-rate", new RemovePublishRate()); + + addCommand("get-compaction-threshold", new GetCompactionThreshold()); + addCommand("set-compaction-threshold", new SetCompactionThreshold()); + addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); + + addCommand("get-subscribe-rate", new GetSubscribeRate()); + addCommand("set-subscribe-rate", new SetSubscribeRate()); + addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); + + addCommand("get-max-consumers", new GetMaxConsumers()); + addCommand("set-max-consumers", new SetMaxConsumers()); + addCommand("remove-max-consumers", new RemoveMaxConsumers()); + + addCommand("get-delayed-delivery", new GetDelayedDelivery()); + addCommand("set-delayed-delivery", new SetDelayedDelivery()); + addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); + + addCommand("get-dispatch-rate", new GetDispatchRate()); + addCommand("set-dispatch-rate", new SetDispatchRate()); + addCommand("remove-dispatch-rate", new RemoveDispatchRate()); + + addCommand("get-offload-policies", new GetOffloadPolicies()); + addCommand("set-offload-policies", new SetOffloadPolicies()); + addCommand("remove-offload-policies", new RemoveOffloadPolicies()); + + addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesPerSubscription()); + addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesPerSubscription()); + addCommand("remove-max-unacked-messages-per-subscription", new RemoveMaxUnackedMessagesPerSubscription()); - jcommander.addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); - jcommander.addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); - jcommander.addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); + addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); + addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); + addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); - jcommander.addCommand("get-max-subscriptions-per-topic", new GetMaxSubscriptionsPerTopic()); - jcommander.addCommand("set-max-subscriptions-per-topic", new SetMaxSubscriptionsPerTopic()); - jcommander.addCommand("remove-max-subscriptions-per-topic", new RemoveMaxSubscriptionsPerTopic()); + addCommand("get-max-subscriptions-per-topic", new GetMaxSubscriptionsPerTopic()); + addCommand("set-max-subscriptions-per-topic", new SetMaxSubscriptionsPerTopic()); + addCommand("remove-max-subscriptions-per-topic", new RemoveMaxSubscriptionsPerTopic()); - jcommander.addCommand("remove-schema-compatibility-strategy", new RemoveSchemaCompatibilityStrategy()); - jcommander.addCommand("set-schema-compatibility-strategy", new SetSchemaCompatibilityStrategy()); - jcommander.addCommand("get-schema-compatibility-strategy", new GetSchemaCompatibilityStrategy()); + addCommand("remove-schema-compatibility-strategy", new RemoveSchemaCompatibilityStrategy()); + addCommand("set-schema-compatibility-strategy", new SetSchemaCompatibilityStrategy()); + addCommand("get-schema-compatibility-strategy", new GetSchemaCompatibilityStrategy()); - jcommander.addCommand("get-entry-filters-per-topic", new GetEntryFiltersPerTopic()); - jcommander.addCommand("set-entry-filters-per-topic", new SetEntryFiltersPerTopic()); - jcommander.addCommand("remove-entry-filters-per-topic", new RemoveEntryFiltersPerTopic()); + addCommand("get-entry-filters-per-topic", new GetEntryFiltersPerTopic()); + addCommand("set-entry-filters-per-topic", new SetEntryFiltersPerTopic()); + addCommand("remove-entry-filters-per-topic", new RemoveEntryFiltersPerTopic()); - jcommander.addCommand("set-auto-subscription-creation", new SetAutoSubscriptionCreation()); - jcommander.addCommand("get-auto-subscription-creation", new GetAutoSubscriptionCreation()); - jcommander.addCommand("remove-auto-subscription-creation", new RemoveAutoSubscriptionCreation()); + addCommand("set-auto-subscription-creation", new SetAutoSubscriptionCreation()); + addCommand("get-auto-subscription-creation", new GetAutoSubscriptionCreation()); + addCommand("remove-auto-subscription-creation", new RemoveAutoSubscriptionCreation()); - jcommander.addCommand("set-dispatcher-pause-on-ack-state-persistent", + addCommand("set-dispatcher-pause-on-ack-state-persistent", new SetDispatcherPauseOnAckStatePersistent()); - jcommander.addCommand("get-dispatcher-pause-on-ack-state-persistent", + addCommand("get-dispatcher-pause-on-ack-state-persistent", new GetDispatcherPauseOnAckStatePersistent()); - jcommander.addCommand("remove-dispatcher-pause-on-ack-state-persistent", + addCommand("remove-dispatcher-pause-on-ack-state-persistent", new RemoveDispatcherPauseOnAckStatePersistent()); } - @Parameters(commandDescription = "Get entry filters for a topic") + @Command(description = "Get entry filters for a topic") private class GetEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getEntryFiltersPerTopic(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set entry filters for a topic") + @Command(description = "Set entry filters for a topic") private class SetEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--entry-filters-name", "-efn" }, + @Option(names = { "--entry-filters-name", "-efn" }, description = "The class name for the entry filter.", required = true) private String entryFiltersName = ""; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setEntryFiltersPerTopic(persistentTopic, new EntryFilters(entryFiltersName)); } } - @Parameters(commandDescription = "Remove entry filters for a topic") + @Command(description = "Remove entry filters for a topic") private class RemoveEntryFiltersPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeEntryFiltersPerTopic(persistentTopic); } } - @Parameters(commandDescription = "Get max consumers per subscription for a topic") + @Command(description = "Get max consumers per subscription for a topic") private class GetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxConsumersPerSubscription(persistentTopic)); } } - @Parameters(commandDescription = "Set max consumers per subscription for a topic") + @Command(description = "Set max consumers per subscription for a topic") private class SetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--max-consumers-per-subscription", "-c" }, + @Option(names = { "--max-consumers-per-subscription", "-c" }, description = "maxConsumersPerSubscription for a namespace", required = true) private int maxConsumersPerSubscription; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxConsumersPerSubscription(persistentTopic, maxConsumersPerSubscription); } } - @Parameters(commandDescription = "Remove max consumers per subscription for a topic") + @Command(description = "Remove max consumers per subscription for a topic") private class RemoveMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxConsumersPerSubscription(persistentTopic); } } - @Parameters(commandDescription = "Get max unacked messages policy per consumer for a topic") + @Command(description = "Get max unacked messages policy per consumer for a topic") private class GetMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxUnackedMessagesOnConsumer(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove max unacked messages policy per consumer for a topic") + @Command(description = "Remove max unacked messages policy per consumer for a topic") private class RemoveMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxUnackedMessagesOnConsumer(persistentTopic); } } - @Parameters(commandDescription = "Set max unacked messages policy per consumer for a topic") + @Command(description = "Set max unacked messages policy per consumer for a topic") private class SetMaxUnackedMessagesPerConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-m", "--maxNum"}, description = "max unacked messages num on consumer", required = true) + @Option(names = {"-m", "--maxNum"}, description = "max unacked messages num on consumer", required = true) private int maxNum; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxUnackedMessagesOnConsumer(persistentTopic, maxNum); } } - @Parameters(commandDescription = "Get the message TTL for a topic") + @Command(description = "Get the message TTL for a topic") private class GetMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMessageTTL(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set message TTL for a topic") + @Command(description = "Set message TTL for a topic") private class SetMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-t", "--ttl" }, + @Option(names = { "-t", "--ttl" }, description = "Message TTL for topic in seconds (or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w), " + "allowed range from 1 to Integer.MAX_VALUE", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = {NonNegativeValueValidator.class}) + converter = TimeUnitToSecondsConverter.class) private Long messageTTLInSecond; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMessageTTL(persistentTopic, messageTTLInSecond.intValue()); } } - @Parameters(commandDescription = "Remove message TTL for a topic") + @Command(description = "Remove message TTL for a topic") private class RemoveMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMessageTTL(persistentTopic); } } - @Parameters(commandDescription = "Set subscription types enabled for a topic") + @Command(description = "Set subscription types enabled for a topic") private class SetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." - + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true) + @Option(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." + + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true, split = ",") private List subTypes; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); Set types = new HashSet<>(); subTypes.forEach(s -> { SubscriptionType subType; @@ -426,143 +422,142 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get subscription types enabled for a topic") + @Command(description = "Get subscription types enabled for a topic") private class GetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getSubscriptionTypesEnabled(persistentTopic)); } } - @Parameters(commandDescription = "Remove subscription types enabled for a topic") + @Command(description = "Remove subscription types enabled for a topic") private class RemoveSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeSubscriptionTypesEnabled(persistentTopic); } } - @Parameters(commandDescription = "Get max number of consumers for a topic") + @Command(description = "Get max number of consumers for a topic") private class GetMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxConsumers(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set max number of consumers for a topic") + @Command(description = "Set max number of consumers for a topic") private class SetMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--max-consumers", "-c" }, description = "Max consumers for a topic", required = true) + @Option(names = { "--max-consumers", "-c" }, description = "Max consumers for a topic", required = true) private int maxConsumers; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxConsumers(persistentTopic, maxConsumers); } } - @Parameters(commandDescription = "Remove max number of consumers for a topic") + @Command(description = "Remove max number of consumers for a topic") private class RemoveMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxConsumers(persistentTopic); } } - @Parameters(commandDescription = "Get the retention policy for a topic") + @Command(description = "Get the retention policy for a topic") private class GetRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, the broker returns global topic policies" + "If set to false or not set, the broker returns local topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getRetention(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set the retention policy for a topic") + @Command(description = "Set the retention policy for a topic") private class SetRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--time", + @Option(names = { "--time", "-t" }, description = "Retention time with optional time unit suffix. " + "For example, 100m, 3h, 2d, 5w. " + "If the time unit is not specified, the default unit is seconds. For example, " + "-t 120 sets retention to 2 minutes. " + "0 means no retention and -1 means infinite time retention.", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = MinNegativeOneValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long retentionTimeInSec; - @Parameter(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + @Option(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + "For example, 4096, 10M, 16G, 3T. The size unit suffix character can be k/K, m/M, g/G, or t/T. " + "If the size unit suffix is not specified, the default unit is bytes. " + "0 or less than 1MB means no retention and -1 means infinite size retention", required = true, - converter = ByteUnitIntegerConverter.class) + converter = ByteUnitToIntegerConverter.class) private Integer sizeLimit; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy is replicated to other clusters asynchronously, " + "If set to false or not set, the topic retention policy is replicated to local clusters.") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); - final int retentionTimeInMin = retentionTimeInSec != -1 + String persistentTopic = validatePersistentTopic(topicName); + final int retentionTimeInMin = retentionTimeInSec != -1 ? (int) TimeUnit.SECONDS.toMinutes(retentionTimeInSec) : retentionTimeInSec.intValue(); final int retentionSizeInMB = sizeLimit != -1 @@ -573,169 +568,169 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the retention policy for a topic") + @Command(description = "Remove the retention policy for a topic") private class RemoveRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation is replicated to other clusters asynchronously" + "If set to false or not set, the topic retention policy is replicated to local clusters.") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeRetention(persistentTopic); } } - @Parameters(commandDescription = "Get max unacked messages policy per subscription for a topic") + @Command(description = "Get max unacked messages policy per subscription for a topic") private class GetMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxUnackedMessagesOnSubscription(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove max unacked messages policy per subscription for a topic") + @Command(description = "Remove max unacked messages policy per subscription for a topic") private class RemoveMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxUnackedMessagesOnSubscription(persistentTopic); } } - @Parameters(commandDescription = "Set max unacked messages policy on subscription for a topic") + @Command(description = "Set max unacked messages policy on subscription for a topic") private class SetMaxUnackedMessagesPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-m", "--maxNum"}, + @Option(names = {"-m", "--maxNum"}, description = "max unacked messages num on subscription", required = true) private int maxNum; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxUnackedMessagesOnSubscription(persistentTopic, maxNum); } } - @Parameters(commandDescription = "Get max number of producers for a topic") + @Command(description = "Get max number of producers for a topic") private class GetMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxProducers(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set max number of producers for a topic") + @Command(description = "Set max number of producers for a topic") private class SetMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-producers", "-p"}, description = "Max producers for a topic", required = true) + @Option(names = {"--max-producers", "-p"}, description = "Max producers for a topic", required = true) private int maxProducers; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxProducers(persistentTopic, maxProducers); } } - @Parameters(commandDescription = "Get the delayed delivery policy for a topic") + @Command(description = "Get the delayed delivery policy for a topic") private class GetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); - print(getTopicPolicies(isGlobal).getDelayedDeliveryPolicy(topicName, applied)); + String topic = validateTopicName(topicName); + print(getTopicPolicies(isGlobal).getDelayedDeliveryPolicy(topic, applied)); } } - @Parameters(commandDescription = "Set the delayed delivery policy on a topic") + @Command(description = "Set the delayed delivery policy on a topic") private class SetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") + @Option(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") + @Option(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") private boolean disable = false; - @Parameter(names = { "--time", "-t" }, description = "The tick time for when retrying on " + @Option(names = { "--time", "-t" }, description = "The tick time for when retrying on " + "delayed delivery messages, affecting the accuracy of the delivery time compared to " + "the scheduled time. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryTimeInMills = 1000L; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal; - @Parameter(names = { "--maxDelay", "-md" }, + @Option(names = { "--maxDelay", "-md" }, description = "The max allowed delay for delayed delivery. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryMaxDelayInMillis = 0L; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); + String topic = validateTopicName(topicName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); } - getTopicPolicies(isGlobal).setDelayedDeliveryPolicy(topicName, DelayedDeliveryPolicies.builder() + getTopicPolicies(isGlobal).setDelayedDeliveryPolicy(topic, DelayedDeliveryPolicies.builder() .tickTime(delayedDeliveryTimeInMills) .active(enable) .maxDeliveryDelayInMillis(delayedDeliveryMaxDelayInMillis) @@ -743,105 +738,105 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the delayed delivery policy on a topic") + @Command(description = "Remove the delayed delivery policy on a topic") private class RemoveDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); - getTopicPolicies(isGlobal).removeDelayedDeliveryPolicy(topicName); + String topic = validateTopicName(topicName); + getTopicPolicies(isGlobal).removeDelayedDeliveryPolicy(topic); } } - @Parameters(commandDescription = "Remove max number of producers for a topic") + @Command(description = "Remove max number of producers for a topic") private class RemoveMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxProducers(persistentTopic); } } - @Parameters(commandDescription = "Get max message size for a topic") + @Command(description = "Get max message size for a topic") private class GetMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returns global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxMessageSize(persistentTopic)); } } - @Parameters(commandDescription = "Set max message size for a topic") + @Command(description = "Set max message size for a topic") private class SetMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-message-size", "-m"}, description = "Max message size for a topic", required = true) + @Option(names = {"--max-message-size", "-m"}, description = "Max message size for a topic", required = true) private int maxMessageSize; - @Parameter(names = {"--global", "-g"}, description = "Whether to set this policy globally.") + @Option(names = {"--global", "-g"}, description = "Whether to set this policy globally.") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxMessageSize(persistentTopic, maxMessageSize); } } - @Parameters(commandDescription = "Remove max message size for a topic") + @Command(description = "Remove max message size for a topic") private class RemoveMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxMessageSize(persistentTopic); } } - @Parameters(commandDescription = "Enable or disable status for a topic") + @Command(description = "Enable or disable status for a topic") private class SetDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable", "-e" }, description = "Enable deduplication") + @Option(names = { "--enable", "-e" }, description = "Enable deduplication") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable deduplication") + @Option(names = { "--disable", "-d" }, description = "Disable deduplication") private boolean disable = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); @@ -850,64 +845,64 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the deduplication status for a topic") + @Command(description = "Get the deduplication status for a topic") private class GetDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. ") + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. ") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getDeduplicationStatus(persistentTopic)); } } - @Parameters(commandDescription = "Remove the deduplication status for a topic") + @Command(description = "Remove the deduplication status for a topic") private class RemoveDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeDeduplicationStatus(persistentTopic); } } - @Parameters(commandDescription = "Get deduplication snapshot interval for a topic") + @Command(description = "Get deduplication snapshot interval for a topic") private class GetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returns global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getDeduplicationSnapshotInterval(persistentTopic)); } } - @Parameters(commandDescription = "Set deduplication snapshot interval for a topic") + @Command(description = "Set deduplication snapshot interval for a topic") private class SetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-i", "--interval"}, description = + @Option(names = {"-i", "--interval"}, description = "Deduplication snapshot interval for topic in second, allowed range from 0 to Integer.MAX_VALUE", required = true) private int interval; - @Parameter(names = {"--global", "-g"}, description = "Whether to set this policy globally.") + @Option(names = {"--global", "-g"}, description = "Whether to set this policy globally.") private boolean isGlobal = false; @Override @@ -916,75 +911,74 @@ void run() throws PulsarAdminException { throw new ParameterException(String.format("Invalid interval '%d'. ", interval)); } - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setDeduplicationSnapshotInterval(persistentTopic, interval); } } - @Parameters(commandDescription = "Remove deduplication snapshot interval for a topic") + @Command(description = "Remove deduplication snapshot interval for a topic") private class RemoveDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeDeduplicationSnapshotInterval(persistentTopic); } } - @Parameters(commandDescription = "Get the backlog quota policies for a topic") + @Command(description = "Get the backlog quota policies for a topic") private class GetBacklogQuotaMap extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") + @Option(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getBacklogQuotaMap(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set a backlog quota policy for a topic") + @Command(description = "Set a backlog quota policy for a topic") private class SetBacklogQuota extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", + @Option(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", converter = ByteUnitToLongConverter.class) private Long limit; - @Parameter(names = { "-lt", "--limitTime" }, + @Option(names = { "-lt", "--limitTime" }, description = "Time limit in second (or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w), " + "non-positive number for disabling time limit.", - converter = TimeUnitToSecondsConverter.class, - validateValueWith = IntegerMaxValueLongValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long limitTimeInSec; - @Parameter(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. " + @Option(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. " + "Valid options are: [producer_request_hold, producer_exception, consumer_backlog_eviction]", required = true) private String policyStr; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + @Option(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + "destination_storage (default) and message_age. " + "destination_storage limits backlog by size. " + "message_age limits backlog by time, that is, message timestamp (broker or publish timestamp). " + "You can set size or time to control the backlog, or combine them together to control the backlog. ") private String backlogQuotaTypeStr = BacklogQuota.BacklogQuotaType.destination_storage.name(); - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @@ -1005,7 +999,7 @@ void run() throws PulsarAdminException { throw new ParameterException(String.format("Invalid backlog quota type '%s'. Valid options are: %s", backlogQuotaTypeStr, Arrays.toString(BacklogQuota.BacklogQuotaType.values()))); } - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); BacklogQuota.Builder builder = BacklogQuota.builder().retentionPolicy(policy); if (backlogQuotaType == BacklogQuota.BacklogQuotaType.destination_storage) { @@ -1027,185 +1021,185 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove a backlog quota policy from a topic") + @Command(description = "Remove a backlog quota policy from a topic") private class RemoveBacklogQuota extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to remove") + @Option(names = {"-t", "--type"}, description = "Backlog quota type to remove") private String backlogQuotaType = BacklogQuota.BacklogQuotaType.destination_storage.name(); - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal) .removeBacklogQuota(persistentTopic, BacklogQuota.BacklogQuotaType.valueOf(backlogQuotaType)); } } - @Parameters(commandDescription = "Get publish rate for a topic") + @Command(description = "Get publish rate for a topic") private class GetPublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returns global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getPublishRate(persistentTopic)); } } - @Parameters(commandDescription = "Set publish rate for a topic") + @Command(description = "Set publish rate for a topic") private class SetPublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--msg-publish-rate", "-m"}, description = "message-publish-rate (default -1 will be " + @Option(names = {"--msg-publish-rate", "-m"}, description = "message-publish-rate (default -1 will be " + "overwrite if not passed)", required = false) private int msgPublishRate = -1; - @Parameter(names = {"--byte-publish-rate", "-b"}, description = "byte-publish-rate " + @Option(names = {"--byte-publish-rate", "-b"}, description = "byte-publish-rate " + "(default -1 will be overwrite if not passed)", required = false) private long bytePublishRate = -1; - @Parameter(names = {"--global", "-g"}, description = "Whether to set this policy globally.") + @Option(names = {"--global", "-g"}, description = "Whether to set this policy globally.") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setPublishRate(persistentTopic, new PublishRate(msgPublishRate, bytePublishRate)); } } - @Parameters(commandDescription = "Remove publish rate for a topic") + @Command(description = "Remove publish rate for a topic") private class RemovePublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removePublishRate(persistentTopic); } } - @Parameters(commandDescription = "Get consumer subscribe rate for a topic") + @Command(description = "Get consumer subscribe rate for a topic") private class GetSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returns global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getSubscribeRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set consumer subscribe rate for a topic") + @Command(description = "Set consumer subscribe rate for a topic") private class SetSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--subscribe-rate", + @Option(names = { "--subscribe-rate", "-sr" }, description = "subscribe-rate (default -1 will be overwrite if not passed)", required = false) private int subscribeRate = -1; - @Parameter(names = { "--subscribe-rate-period", + @Option(names = { "--subscribe-rate-period", "-st" }, description = "subscribe-rate-period in second type " + "(default 30 second will be overwrite if not passed)", required = false) private int subscribeRatePeriodSec = 30; - @Parameter(names = {"--global", "-g"}, description = "Whether to set this policy globally.") + @Option(names = {"--global", "-g"}, description = "Whether to set this policy globally.") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setSubscribeRate(persistentTopic, new SubscribeRate(subscribeRate, subscribeRatePeriodSec)); } } - @Parameters(commandDescription = "Remove consumer subscribe rate for a topic") + @Command(description = "Remove consumer subscribe rate for a topic") private class RemoveSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. ") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeSubscribeRate(persistentTopic); } } - @Parameters(commandDescription = "Get the persistence policies for a topic") + @Command(description = "Get the persistence policies for a topic") private class GetPersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " - + "If set to true, broker returned global topic policies", arity = 0) + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getPersistence(persistentTopic)); } } - @Parameters(commandDescription = "Set the persistence policies for a topic") + @Command(description = "Set the persistence policies for a topic") private class SetPersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-e", + @Option(names = { "-e", "--bookkeeper-ensemble" }, description = "Number of bookies to use for a topic") private int bookkeeperEnsemble = 2; - @Parameter(names = { "-w", + @Option(names = { "-w", "--bookkeeper-write-quorum" }, description = "How many writes to make of each entry") private int bookkeeperWriteQuorum = 2; - @Parameter(names = { "-a", "--bookkeeper-ack-quorum" }, + @Option(names = { "-a", "--bookkeeper-ack-quorum" }, description = "Number of acks (guaranteed copies) to wait for each entry") private int bookkeeperAckQuorum = 2; - @Parameter(names = { "-r", "--ml-mark-delete-max-rate" }, + @Option(names = { "-r", "--ml-mark-delete-max-rate" }, description = "Throttling rate of mark-delete operation (0 means no throttle)") private double managedLedgerMaxMarkDeleteRate = 0; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " - + "If set to true, the policy will be replicate to other clusters asynchronously", arity = 0) + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + + "If set to true, the policy will be replicate to other clusters asynchronously", arity = "0") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (bookkeeperEnsemble <= 0 || bookkeeperWriteQuorum <= 0 || bookkeeperAckQuorum <= 0) { throw new ParameterException("[--bookkeeper-ensemble], [--bookkeeper-write-quorum] " + "and [--bookkeeper-ack-quorum] must greater than 0."); @@ -1218,129 +1212,129 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the persistence policy for a topic") + @Command(description = "Remove the persistence policy for a topic") private class RemovePersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously" - , arity = 0) + , arity = "0") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removePersistence(persistentTopic); } } - @Parameters(commandDescription = "Get compaction threshold for a topic") + @Command(description = "Get compaction threshold for a topic") private class GetCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getCompactionThreshold(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set compaction threshold for a topic") + @Command(description = "Set compaction threshold for a topic") private class SetCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--threshold", "-t" }, + @Option(names = { "--threshold", "-t" }, description = "Maximum number of bytes in a topic backlog before compaction is triggered " + "(eg: 10M, 16G, 3T). 0 disables automatic compaction", required = true, converter = ByteUnitToLongConverter.class) private Long threshold = 0L; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setCompactionThreshold(persistentTopic, threshold); } } - @Parameters(commandDescription = "Remove compaction threshold for a topic") + @Command(description = "Remove compaction threshold for a topic") private class RemoveCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeCompactionThreshold(persistentTopic); } } - @Parameters(commandDescription = "Get message dispatch rate for a topic") + @Command(description = "Get message dispatch rate for a topic") private class GetDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getDispatchRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set message dispatch rate for a topic") + @Command(description = "Set message dispatch rate for a topic") private class SetDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate " + "(default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)", required = false) private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))", required = false) private boolean relativeToPublishRate = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setDispatchRate(persistentTopic, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -1351,70 +1345,69 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove message dispatch rate for a topic") + @Command(description = "Remove message dispatch rate for a topic") private class RemoveDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeDispatchRate(persistentTopic); } } - @Parameters(commandDescription = "Get the inactive topic policies on a topic") + @Command(description = "Get the inactive topic policies on a topic") private class GetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getInactiveTopicPolicies(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set the inactive topic policies on a topic") + @Command(description = "Set the inactive topic policies on a topic") private class SetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") + @Option(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") private boolean enableDeleteWhileInactive = false; - @Parameter(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") + @Option(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") private boolean disableDeleteWhileInactive = false; - @Parameter(names = {"--max-inactive-duration", "-t"}, + @Option(names = {"--max-inactive-duration", "-t"}, description = "Max duration of topic inactivity in seconds, topics that are inactive for longer than " + "this value will be deleted (eg: 1s, 10s, 1m, 5h, 3d)", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = IntegerMaxValueLongValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long maxInactiveDurationInSeconds; - @Parameter(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + @Option(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + "[delete_when_no_subscriptions, delete_when_subscriptions_caught_up]", required = true) private String inactiveTopicDeleteMode; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (enableDeleteWhileInactive == disableDeleteWhileInactive) { throw new ParameterException("Need to specify either enable-delete-while-inactive or " + "disable-delete-while-inactive"); @@ -1431,69 +1424,69 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove inactive topic policies from a topic") + @Command(description = "Remove inactive topic policies from a topic") private class RemoveInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeInactiveTopicPolicies(persistentTopic); } } - @Parameters(commandDescription = "Get replicator message-dispatch-rate for a topic") + @Command(description = "Get replicator message-dispatch-rate for a topic") private class GetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getReplicatorDispatchRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set replicator message-dispatch-rate for a topic") + @Command(description = "Set replicator message-dispatch-rate for a topic") private class SetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate (default -1 will be overwrite if not passed)") private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)") private long byteDispatchRate = -1; - @Parameter(names = {"--dispatch-rate-period", + @Option(names = {"--dispatch-rate-period", "-dt"}, description = "dispatch-rate-period in second type (default 1 second will be overwrite if not" + " passed)") private int dispatchRatePeriodSec = 1; - @Parameter(names = {"--relative-to-publish-rate", + @Option(names = {"--relative-to-publish-rate", "-rp"}, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))") private boolean relativeToPublishRate = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setReplicatorDispatchRate(persistentTopic, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -1504,42 +1497,42 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove replicator message-dispatch-rate for a topic") + @Command(description = "Remove replicator message-dispatch-rate for a topic") private class RemoveReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeReplicatorDispatchRate(persistentTopic); } } - @Parameters(commandDescription = "Get subscription message-dispatch-rate for a topic") + @Command(description = "Get subscription message-dispatch-rate for a topic") private class GetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; - @Parameter(names = {"--subscription", "-s"}, + @Option(names = {"--subscription", "-s"}, description = "Get message-dispatch-rate of a specific subscription") private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (StringUtils.isBlank(subName)) { print(getTopicPolicies(isGlobal).getSubscriptionDispatchRate(persistentTopic, applied)); } else { @@ -1548,40 +1541,40 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set subscription message-dispatch-rate for a topic") + @Command(description = "Set subscription message-dispatch-rate for a topic") private class SetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate (default -1 will be overwrite if not passed)") private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)") private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)") private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))") private boolean relativeToPublishRate = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; - @Parameter(names = {"--subscription", "-s"}, + @Option(names = {"--subscription", "-s"}, description = "Set message-dispatch-rate for a specific subscription") private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); DispatchRate rate = DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) .dispatchThrottlingRateInByte(byteDispatchRate) @@ -1596,22 +1589,22 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove subscription message-dispatch-rate for a topic") + @Command(description = "Remove subscription message-dispatch-rate for a topic") private class RemoveSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; - @Parameter(names = {"--subscription", "-s"}, + @Option(names = {"--subscription", "-s"}, description = "Remove message-dispatch-rate for a specific subscription") private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (StringUtils.isBlank(subName)) { getTopicPolicies(isGlobal).removeSubscriptionDispatchRate(persistentTopic); } else { @@ -1621,154 +1614,154 @@ void run() throws PulsarAdminException { } - @Parameters(commandDescription = "Get max subscriptions for a topic") + @Command(description = "Get max subscriptions for a topic") private class GetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getMaxSubscriptionsPerTopic(persistentTopic)); } } - @Parameters(commandDescription = "Set max subscriptions for a topic") + @Command(description = "Set max subscriptions for a topic") private class SetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-subscriptions-per-topic", + @Option(names = {"--max-subscriptions-per-topic", "-s"}, description = "max subscriptions for a topic (default -1 will be overwrite if not passed)", required = true) private int maxSubscriptionPerTopic; - @Parameter(names = {"--global", "-g"}, description = "Whether to set this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setMaxSubscriptionsPerTopic(persistentTopic, maxSubscriptionPerTopic); } } - @Parameters(commandDescription = "Remove max subscriptions for a topic") + @Command(description = "Remove max subscriptions for a topic") private class RemoveMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeMaxSubscriptionsPerTopic(persistentTopic); } } - @Parameters(commandDescription = "Get the offload policies for a topic") + @Command(description = "Get the offload policies for a topic") private class GetOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getOffloadPolicies(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove the offload policies for a topic") + @Command(description = "Remove the offload policies for a topic") private class RemoveOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to remove this policy globally. " + "If set to true, the removing operation will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeOffloadPolicies(persistentTopic); } } - @Parameters(commandDescription = "Set the offload policies for a topic") + @Command(description = "Set the offload policies for a topic") private class SetOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-d", "--driver"}, description = "ManagedLedger offload driver", required = true) + @Option(names = {"-d", "--driver"}, description = "ManagedLedger offload driver", required = true) private String driver; - @Parameter(names = {"-r", "--region"} + @Option(names = {"-r", "--region"} , description = "ManagedLedger offload region, s3 and google-cloud-storage requires this parameter") private String region; - @Parameter(names = {"-b", "--bucket"} + @Option(names = {"-b", "--bucket"} , description = "ManagedLedger offload bucket, s3 and google-cloud-storage requires this parameter") private String bucket; - @Parameter(names = {"-e", "--endpoint"} + @Option(names = {"-e", "--endpoint"} , description = "ManagedLedger offload service endpoint, only s3 requires this parameter") private String endpoint; - @Parameter(names = {"-i", "--aws-id"} + @Option(names = {"-i", "--aws-id"} , description = "AWS Credential Id to use when using driver S3 or aws-s3") private String awsId; - @Parameter(names = {"-s", "--aws-secret"} + @Option(names = {"-s", "--aws-secret"} , description = "AWS Credential Secret to use when using driver S3 or aws-s3") private String awsSecret; - @Parameter(names = {"--ro", "--s3-role"} + @Option(names = {"--ro", "--s3-role"} , description = "S3 Role used for STSAssumeRoleSessionCredentialsProvider") private String s3Role; - @Parameter(names = {"--s3-role-session-name", "-rsn"} + @Option(names = {"--s3-role-session-name", "-rsn"} , description = "S3 role session name used for STSAssumeRoleSessionCredentialsProvider") private String s3RoleSessionName; - @Parameter(names = {"-m", "--maxBlockSizeInBytes"}, + @Option(names = {"-m", "--maxBlockSizeInBytes"}, description = "ManagedLedger offload max block Size in bytes," + "s3 and google-cloud-storage requires this parameter") private int maxBlockSizeInBytes = OffloadPoliciesImpl.DEFAULT_MAX_BLOCK_SIZE_IN_BYTES; - @Parameter(names = {"-rb", "--readBufferSizeInBytes"}, + @Option(names = {"-rb", "--readBufferSizeInBytes"}, description = "ManagedLedger offload read buffer size in bytes," + "s3 and google-cloud-storage requires this parameter") private int readBufferSizeInBytes = OffloadPoliciesImpl.DEFAULT_READ_BUFFER_SIZE_IN_BYTES; - @Parameter(names = {"-t", "--offloadThresholdInBytes"} + @Option(names = {"-t", "--offloadThresholdInBytes"} , description = "ManagedLedger offload threshold in bytes") private Long offloadThresholdInBytes = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_BYTES; - @Parameter(names = {"-ts", "--offloadThresholdInSeconds"} + @Option(names = {"-ts", "--offloadThresholdInSeconds"} , description = "ManagedLedger offload threshold in seconds") private Long offloadThresholdInSeconds = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_SECONDS; - @Parameter(names = {"-dl", "--offloadDeletionLagInMillis"} + @Option(names = {"-dl", "--offloadDeletionLagInMillis"} , description = "ManagedLedger offload deletion lag in bytes") private Long offloadDeletionLagInMillis = OffloadPoliciesImpl.DEFAULT_OFFLOAD_DELETION_LAG_IN_MILLIS; - @Parameter( + @Option( names = {"--offloadedReadPriority", "-orp"}, description = "Read priority for offloaded messages. By default, once messages are offloaded to" + " long-term storage, brokers read messages from long-term storage, but messages can still" @@ -1779,7 +1772,7 @@ private class SetOffloadPolicies extends CliCommand { ) private String offloadReadPriorityStr; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @@ -1798,7 +1791,7 @@ public boolean isS3Driver(String driver) { @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (!driverSupported(driver)) { throw new ParameterException("The driver " + driver + " is not supported, " @@ -1841,67 +1834,67 @@ void run() throws PulsarAdminException { } - @Parameters(commandDescription = "Remove schema compatibility strategy on a topic") + @Command(description = "Remove schema compatibility strategy on a topic") private class RemoveSchemaCompatibilityStrategy extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getAdmin().topicPolicies().removeSchemaCompatibilityStrategy(persistentTopic); } } - @Parameters(commandDescription = "Set schema compatibility strategy on a topic") + @Command(description = "Set schema compatibility strategy on a topic") private class SetSchemaCompatibilityStrategy extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--strategy", "-s"}, description = "Schema compatibility strategy: [UNDEFINED, " + @Option(names = {"--strategy", "-s"}, description = "Schema compatibility strategy: [UNDEFINED, " + "ALWAYS_INCOMPATIBLE, ALWAYS_COMPATIBLE, BACKWARD, FORWARD, FULL, BACKWARD_TRANSITIVE, " + "FORWARD_TRANSITIVE, FULL_TRANSITIVE]", required = true) private SchemaCompatibilityStrategy strategy; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getAdmin().topicPolicies().setSchemaCompatibilityStrategy(persistentTopic, strategy); } } - @Parameters(commandDescription = "Get schema compatibility strategy on a topic") + @Command(description = "Get schema compatibility strategy on a topic") private class GetSchemaCompatibilityStrategy extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") + @Option(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); SchemaCompatibilityStrategy strategy = getAdmin().topicPolicies().getSchemaCompatibilityStrategy(persistentTopic, applied); print(strategy == null ? "null" : strategy.name()); } } - @Parameters(commandDescription = "Enable autoSubscriptionCreation for a topic") + @Command(description = "Enable autoSubscriptionCreation for a topic") private class SetAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--enable", "-e"}, description = "Enable allowAutoSubscriptionCreation on topic") + @Option(names = {"--enable", "-e"}, description = "Enable allowAutoSubscriptionCreation on topic") private boolean enable = false; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setAutoSubscriptionCreation(persistentTopic, AutoSubscriptionCreationOverride.builder() .allowAutoSubscriptionCreation(enable) @@ -1909,88 +1902,88 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the autoSubscriptionCreation for a topic") + @Command(description = "Get the autoSubscriptionCreation for a topic") private class GetAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--applied", "-a"}, description = "Get the applied policy of the topic") + @Option(names = {"--applied", "-a"}, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getAutoSubscriptionCreation(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove override of autoSubscriptionCreation for a topic") + @Command(description = "Remove override of autoSubscriptionCreation for a topic") private class RemoveAutoSubscriptionCreation extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeAutoSubscriptionCreation(persistentTopic); } } - @Parameters(commandDescription = "Enable dispatcherPauseOnAckStatePersistent for a topic") + @Command(description = "Enable dispatcherPauseOnAckStatePersistent for a topic") private class SetDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + @Option(names = { "--global", "-g" }, description = "Whether to set this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).setDispatcherPauseOnAckStatePersistent(persistentTopic); } } - @Parameters(commandDescription = "Get the dispatcherPauseOnAckStatePersistent for a topic") + @Command(description = "Get the dispatcherPauseOnAckStatePersistent for a topic") private class GetDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--applied", "-a"}, description = "Get the applied policy of the topic") + @Option(names = {"--applied", "-a"}, description = "Get the applied policy of the topic") private boolean applied = false; - @Parameter(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to get this policy globally. " + "If set to true, broker returned global topic policies") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopicPolicies(isGlobal).getDispatcherPauseOnAckStatePersistent(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove dispatcherPauseOnAckStatePersistent for a topic") + @Command(description = "Remove dispatcherPauseOnAckStatePersistent for a topic") private class RemoveDispatcherPauseOnAckStatePersistent extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + @Option(names = {"--global", "-g"}, description = "Whether to remove this policy globally. " + "If set to true, the policy will be replicate to other clusters asynchronously") private boolean isGlobal = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopicPolicies(isGlobal).removeDispatcherPauseOnAckStatePersistent(persistentTopic); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java index 3e2d9d1c13c59..e1e85c68f7e5e 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java @@ -19,10 +19,6 @@ package org.apache.pulsar.admin.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.IUsageFormatter; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gson.Gson; @@ -53,14 +49,10 @@ import java.util.stream.Collectors; import lombok.Getter; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.cli.converters.ByteUnitIntegerConverter; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; -import org.apache.pulsar.cli.converters.TimeUnitToMillisConverter; -import org.apache.pulsar.cli.converters.TimeUnitToSecondsConverter; -import org.apache.pulsar.cli.validators.IntegerMaxValueLongValidator; -import org.apache.pulsar.cli.validators.MinNegativeOneValidator; -import org.apache.pulsar.cli.validators.NonNegativeValueValidator; -import org.apache.pulsar.cli.validators.PositiveIntegerValueValidator; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToIntegerConverter; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToSecondsConverter; import org.apache.pulsar.client.admin.ListTopicsOptions; import org.apache.pulsar.client.admin.LongRunningProcessStatus; import org.apache.pulsar.client.admin.OffloadProcessStatus; @@ -70,7 +62,6 @@ import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.MessageId; import org.apache.pulsar.client.api.SubscriptionType; -import org.apache.pulsar.client.cli.NoSplitter; import org.apache.pulsar.client.impl.BatchMessageIdImpl; import org.apache.pulsar.client.impl.MessageIdImpl; import org.apache.pulsar.client.impl.MessageImpl; @@ -91,9 +82,12 @@ import org.apache.pulsar.common.policies.data.SubscribeRate; import org.apache.pulsar.common.util.DateFormatter; import org.apache.pulsar.common.util.ObjectMapperFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @Getter -@Parameters(commandDescription = "Operations on persistent topics") +@Command(description = "Operations on persistent topics") public class CmdTopics extends CmdBase { private final CmdTopics.PartitionedLookup partitionedLookup; private final CmdTopics.DeleteCmd deleteCmd; @@ -102,320 +96,204 @@ public CmdTopics(Supplier admin) { super("topics", admin); partitionedLookup = new PartitionedLookup(); deleteCmd = new DeleteCmd(); - jcommander.addCommand("list", new ListCmd()); - jcommander.addCommand("list-partitioned-topics", new PartitionedTopicListCmd()); - jcommander.addCommand("permissions", new Permissions()); - jcommander.addCommand("grant-permission", new GrantPermissions()); - jcommander.addCommand("revoke-permission", new RevokePermissions()); - jcommander.addCommand("lookup", new Lookup()); - jcommander.addCommand("partitioned-lookup", partitionedLookup); - jcommander.addCommand("bundle-range", new GetBundleRange()); - jcommander.addCommand("delete", deleteCmd); - jcommander.addCommand("truncate", new TruncateCmd()); - jcommander.addCommand("unload", new UnloadCmd()); - jcommander.addCommand("subscriptions", new ListSubscriptions()); - jcommander.addCommand("unsubscribe", new DeleteSubscription()); - jcommander.addCommand("create-subscription", new CreateSubscription()); - jcommander.addCommand("update-subscription-properties", new UpdateSubscriptionProperties()); - jcommander.addCommand("get-subscription-properties", new GetSubscriptionProperties()); - - jcommander.addCommand("stats", new GetStats()); - jcommander.addCommand("stats-internal", new GetInternalStats()); - jcommander.addCommand("info-internal", new GetInternalInfo()); - - jcommander.addCommand("partitioned-stats", new GetPartitionedStats()); - jcommander.addCommand("partitioned-stats-internal", new GetPartitionedStatsInternal()); - - jcommander.addCommand("skip", new Skip()); - jcommander.addCommand("clear-backlog", new ClearBacklog()); - - jcommander.addCommand("expire-messages", new ExpireMessages()); - jcommander.addCommand("expire-messages-all-subscriptions", new ExpireMessagesForAllSubscriptions()); - - jcommander.addCommand("create-partitioned-topic", new CreatePartitionedCmd()); - jcommander.addCommand("create-missed-partitions", new CreateMissedPartitionsCmd()); - jcommander.addCommand("create", new CreateNonPartitionedCmd()); - jcommander.addCommand("update-partitioned-topic", new UpdatePartitionedCmd()); - jcommander.addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); - jcommander.addCommand("get-properties", new GetPropertiesCmd()); - jcommander.addCommand("update-properties", new UpdateProperties()); - jcommander.addCommand("remove-properties", new RemoveProperties()); - - jcommander.addCommand("delete-partitioned-topic", new DeletePartitionedCmd()); - jcommander.addCommand("peek-messages", new PeekMessages()); - jcommander.addCommand("examine-messages", new ExamineMessages()); - jcommander.addCommand("get-message-by-id", new GetMessageById()); - jcommander.addCommand("get-message-id", new GetMessageId()); - jcommander.addCommand("reset-cursor", new ResetCursor()); - jcommander.addCommand("terminate", new Terminate()); - jcommander.addCommand("partitioned-terminate", new PartitionedTerminate()); - jcommander.addCommand("compact", new Compact()); - jcommander.addCommand("compaction-status", new CompactionStatusCmd()); - jcommander.addCommand("offload", new Offload()); - jcommander.addCommand("offload-status", new OffloadStatusCmd()); - jcommander.addCommand("last-message-id", new GetLastMessageId()); - jcommander.addCommand("get-backlog-quotas", new GetBacklogQuotaMap()); - jcommander.addCommand("set-backlog-quota", new SetBacklogQuota()); - jcommander.addCommand("remove-backlog-quota", new RemoveBacklogQuota()); - jcommander.addCommand("get-message-ttl", new GetMessageTTL()); - jcommander.addCommand("set-message-ttl", new SetMessageTTL()); - jcommander.addCommand("remove-message-ttl", new RemoveMessageTTL()); - jcommander.addCommand("get-retention", new GetRetention()); - jcommander.addCommand("set-retention", new SetRetention()); - jcommander.addCommand("remove-retention", new RemoveRetention()); + addCommand("list", new ListCmd()); + addCommand("list-partitioned-topics", new PartitionedTopicListCmd()); + addCommand("permissions", new Permissions()); + addCommand("grant-permission", new GrantPermissions()); + addCommand("revoke-permission", new RevokePermissions()); + addCommand("lookup", new Lookup()); + addCommand("partitioned-lookup", partitionedLookup); + addCommand("bundle-range", new GetBundleRange()); + addCommand("delete", deleteCmd); + addCommand("truncate", new TruncateCmd()); + addCommand("unload", new UnloadCmd()); + addCommand("subscriptions", new ListSubscriptions()); + addCommand("unsubscribe", new DeleteSubscription()); + addCommand("create-subscription", new CreateSubscription()); + addCommand("update-subscription-properties", new UpdateSubscriptionProperties()); + addCommand("get-subscription-properties", new GetSubscriptionProperties()); + + addCommand("stats", new GetStats()); + addCommand("stats-internal", new GetInternalStats()); + addCommand("info-internal", new GetInternalInfo()); + + addCommand("partitioned-stats", new GetPartitionedStats()); + addCommand("partitioned-stats-internal", new GetPartitionedStatsInternal()); + + addCommand("skip", new Skip()); + addCommand("clear-backlog", new ClearBacklog()); + + addCommand("expire-messages", new ExpireMessages()); + addCommand("expire-messages-all-subscriptions", new ExpireMessagesForAllSubscriptions()); + + addCommand("create-partitioned-topic", new CreatePartitionedCmd()); + addCommand("create-missed-partitions", new CreateMissedPartitionsCmd()); + addCommand("create", new CreateNonPartitionedCmd()); + addCommand("update-partitioned-topic", new UpdatePartitionedCmd()); + addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd()); + addCommand("get-properties", new GetPropertiesCmd()); + addCommand("update-properties", new UpdateProperties()); + addCommand("remove-properties", new RemoveProperties()); + + addCommand("delete-partitioned-topic", new DeletePartitionedCmd()); + addCommand("peek-messages", new PeekMessages()); + addCommand("examine-messages", new ExamineMessages()); + addCommand("get-message-by-id", new GetMessageById()); + addCommand("get-message-id", new GetMessageId()); + addCommand("reset-cursor", new ResetCursor()); + addCommand("terminate", new Terminate()); + addCommand("partitioned-terminate", new PartitionedTerminate()); + addCommand("compact", new Compact()); + addCommand("compaction-status", new CompactionStatusCmd()); + addCommand("offload", new Offload()); + addCommand("offload-status", new OffloadStatusCmd()); + addCommand("last-message-id", new GetLastMessageId()); + addCommand("get-backlog-quotas", new GetBacklogQuotaMap()); + addCommand("set-backlog-quota", new SetBacklogQuota()); + addCommand("remove-backlog-quota", new RemoveBacklogQuota()); + addCommand("get-message-ttl", new GetMessageTTL()); + addCommand("set-message-ttl", new SetMessageTTL()); + addCommand("remove-message-ttl", new RemoveMessageTTL()); + addCommand("get-retention", new GetRetention()); + addCommand("set-retention", new SetRetention()); + addCommand("remove-retention", new RemoveRetention()); //deprecated commands - jcommander.addCommand("enable-deduplication", new EnableDeduplication()); - jcommander.addCommand("disable-deduplication", new DisableDeduplication()); - jcommander.addCommand("get-deduplication-enabled", new GetDeduplicationStatus()); - - jcommander.addCommand("set-deduplication", new SetDeduplicationStatus()); - jcommander.addCommand("get-deduplication", new GetDeduplicationStatus()); - jcommander.addCommand("remove-deduplication", new RemoveDeduplicationStatus()); - - jcommander.addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); - jcommander.addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); - jcommander.addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); - - jcommander.addCommand("get-delayed-delivery", new GetDelayedDelivery()); - jcommander.addCommand("set-delayed-delivery", new SetDelayedDelivery()); - jcommander.addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); - jcommander.addCommand("get-persistence", new GetPersistence()); - jcommander.addCommand("set-persistence", new SetPersistence()); - jcommander.addCommand("remove-persistence", new RemovePersistence()); - jcommander.addCommand("get-offload-policies", new GetOffloadPolicies()); - jcommander.addCommand("set-offload-policies", new SetOffloadPolicies()); - jcommander.addCommand("remove-offload-policies", new RemoveOffloadPolicies()); - - jcommander.addCommand("get-dispatch-rate", new GetDispatchRate()); - jcommander.addCommand("set-dispatch-rate", new SetDispatchRate()); - jcommander.addCommand("remove-dispatch-rate", new RemoveDispatchRate()); - - jcommander.addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); - jcommander.addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); - jcommander.addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); - - jcommander.addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); - jcommander.addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); - jcommander.addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); - - jcommander.addCommand("get-compaction-threshold", new GetCompactionThreshold()); - jcommander.addCommand("set-compaction-threshold", new SetCompactionThreshold()); - jcommander.addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); + addCommand("enable-deduplication", new EnableDeduplication()); + addCommand("disable-deduplication", new DisableDeduplication()); + addCommand("get-deduplication-enabled", new GetDeduplicationStatus()); + + addCommand("set-deduplication", new SetDeduplicationStatus()); + addCommand("get-deduplication", new GetDeduplicationStatus()); + addCommand("remove-deduplication", new RemoveDeduplicationStatus()); + + addCommand("get-deduplication-snapshot-interval", new GetDeduplicationSnapshotInterval()); + addCommand("set-deduplication-snapshot-interval", new SetDeduplicationSnapshotInterval()); + addCommand("remove-deduplication-snapshot-interval", new RemoveDeduplicationSnapshotInterval()); + + addCommand("get-delayed-delivery", new GetDelayedDelivery()); + addCommand("set-delayed-delivery", new SetDelayedDelivery()); + addCommand("remove-delayed-delivery", new RemoveDelayedDelivery()); + addCommand("get-persistence", new GetPersistence()); + addCommand("set-persistence", new SetPersistence()); + addCommand("remove-persistence", new RemovePersistence()); + addCommand("get-offload-policies", new GetOffloadPolicies()); + addCommand("set-offload-policies", new SetOffloadPolicies()); + addCommand("remove-offload-policies", new RemoveOffloadPolicies()); + + addCommand("get-dispatch-rate", new GetDispatchRate()); + addCommand("set-dispatch-rate", new SetDispatchRate()); + addCommand("remove-dispatch-rate", new RemoveDispatchRate()); + + addCommand("get-subscription-dispatch-rate", new GetSubscriptionDispatchRate()); + addCommand("set-subscription-dispatch-rate", new SetSubscriptionDispatchRate()); + addCommand("remove-subscription-dispatch-rate", new RemoveSubscriptionDispatchRate()); + + addCommand("get-replicator-dispatch-rate", new GetReplicatorDispatchRate()); + addCommand("set-replicator-dispatch-rate", new SetReplicatorDispatchRate()); + addCommand("remove-replicator-dispatch-rate", new RemoveReplicatorDispatchRate()); + + addCommand("get-compaction-threshold", new GetCompactionThreshold()); + addCommand("set-compaction-threshold", new SetCompactionThreshold()); + addCommand("remove-compaction-threshold", new RemoveCompactionThreshold()); //deprecated commands - jcommander.addCommand("get-max-unacked-messages-on-consumer", new GetMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("set-max-unacked-messages-on-consumer", new SetMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("remove-max-unacked-messages-on-consumer", new RemoveMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("get-max-unacked-messages-on-subscription", new GetMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("set-max-unacked-messages-on-subscription", new SetMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("remove-max-unacked-messages-on-subscription", + addCommand("get-max-unacked-messages-on-consumer", new GetMaxUnackedMessagesOnConsumer()); + addCommand("set-max-unacked-messages-on-consumer", new SetMaxUnackedMessagesOnConsumer()); + addCommand("remove-max-unacked-messages-on-consumer", new RemoveMaxUnackedMessagesOnConsumer()); + addCommand("get-max-unacked-messages-on-subscription", new GetMaxUnackedMessagesOnSubscription()); + addCommand("set-max-unacked-messages-on-subscription", new SetMaxUnackedMessagesOnSubscription()); + addCommand("remove-max-unacked-messages-on-subscription", new RemoveMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesOnConsumer()); - jcommander.addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("remove-max-unacked-messages-per-subscription", + addCommand("get-max-unacked-messages-per-consumer", new GetMaxUnackedMessagesOnConsumer()); + addCommand("set-max-unacked-messages-per-consumer", new SetMaxUnackedMessagesOnConsumer()); + addCommand("remove-max-unacked-messages-per-consumer", new RemoveMaxUnackedMessagesOnConsumer()); + addCommand("get-max-unacked-messages-per-subscription", new GetMaxUnackedMessagesOnSubscription()); + addCommand("set-max-unacked-messages-per-subscription", new SetMaxUnackedMessagesOnSubscription()); + addCommand("remove-max-unacked-messages-per-subscription", new RemoveMaxUnackedMessagesOnSubscription()); - jcommander.addCommand("get-publish-rate", new GetPublishRate()); - jcommander.addCommand("set-publish-rate", new SetPublishRate()); - jcommander.addCommand("remove-publish-rate", new RemovePublishRate()); + addCommand("get-publish-rate", new GetPublishRate()); + addCommand("set-publish-rate", new SetPublishRate()); + addCommand("remove-publish-rate", new RemovePublishRate()); - jcommander.addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); - jcommander.addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); - jcommander.addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); + addCommand("set-subscription-types-enabled", new SetSubscriptionTypesEnabled()); + addCommand("get-subscription-types-enabled", new GetSubscriptionTypesEnabled()); + addCommand("remove-subscription-types-enabled", new RemoveSubscriptionTypesEnabled()); //deprecated commands - jcommander.addCommand("get-maxProducers", new GetMaxProducers()); - jcommander.addCommand("set-maxProducers", new SetMaxProducers()); - jcommander.addCommand("remove-maxProducers", new RemoveMaxProducers()); + addCommand("get-maxProducers", new GetMaxProducers()); + addCommand("set-maxProducers", new SetMaxProducers()); + addCommand("remove-maxProducers", new RemoveMaxProducers()); - jcommander.addCommand("get-max-producers", new GetMaxProducers()); - jcommander.addCommand("set-max-producers", new SetMaxProducers()); - jcommander.addCommand("remove-max-producers", new RemoveMaxProducers()); + addCommand("get-max-producers", new GetMaxProducers()); + addCommand("set-max-producers", new SetMaxProducers()); + addCommand("remove-max-producers", new RemoveMaxProducers()); - jcommander.addCommand("get-max-subscriptions", new GetMaxSubscriptionsPerTopic()); - jcommander.addCommand("set-max-subscriptions", new SetMaxSubscriptionsPerTopic()); - jcommander.addCommand("remove-max-subscriptions", new RemoveMaxSubscriptionsPerTopic()); + addCommand("get-max-subscriptions", new GetMaxSubscriptionsPerTopic()); + addCommand("set-max-subscriptions", new SetMaxSubscriptionsPerTopic()); + addCommand("remove-max-subscriptions", new RemoveMaxSubscriptionsPerTopic()); - jcommander.addCommand("get-max-message-size", new GetMaxMessageSize()); - jcommander.addCommand("set-max-message-size", new SetMaxMessageSize()); - jcommander.addCommand("remove-max-message-size", new RemoveMaxMessageSize()); + addCommand("get-max-message-size", new GetMaxMessageSize()); + addCommand("set-max-message-size", new SetMaxMessageSize()); + addCommand("remove-max-message-size", new RemoveMaxMessageSize()); - jcommander.addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); - jcommander.addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); - jcommander.addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); + addCommand("get-max-consumers-per-subscription", new GetMaxConsumersPerSubscription()); + addCommand("set-max-consumers-per-subscription", new SetMaxConsumersPerSubscription()); + addCommand("remove-max-consumers-per-subscription", new RemoveMaxConsumersPerSubscription()); - jcommander.addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); - jcommander.addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); - jcommander.addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); + addCommand("get-inactive-topic-policies", new GetInactiveTopicPolicies()); + addCommand("set-inactive-topic-policies", new SetInactiveTopicPolicies()); + addCommand("remove-inactive-topic-policies", new RemoveInactiveTopicPolicies()); - jcommander.addCommand("get-max-consumers", new GetMaxConsumers()); - jcommander.addCommand("set-max-consumers", new SetMaxConsumers()); - jcommander.addCommand("remove-max-consumers", new RemoveMaxConsumers()); + addCommand("get-max-consumers", new GetMaxConsumers()); + addCommand("set-max-consumers", new SetMaxConsumers()); + addCommand("remove-max-consumers", new RemoveMaxConsumers()); - jcommander.addCommand("get-subscribe-rate", new GetSubscribeRate()); - jcommander.addCommand("set-subscribe-rate", new SetSubscribeRate()); - jcommander.addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); + addCommand("get-subscribe-rate", new GetSubscribeRate()); + addCommand("set-subscribe-rate", new SetSubscribeRate()); + addCommand("remove-subscribe-rate", new RemoveSubscribeRate()); - jcommander.addCommand("set-replicated-subscription-status", new SetReplicatedSubscriptionStatus()); - jcommander.addCommand("get-replicated-subscription-status", new GetReplicatedSubscriptionStatus()); - jcommander.addCommand("get-backlog-size", new GetBacklogSizeByMessageId()); - jcommander.addCommand("analyze-backlog", new AnalyzeBacklog()); + addCommand("set-replicated-subscription-status", new SetReplicatedSubscriptionStatus()); + addCommand("get-replicated-subscription-status", new GetReplicatedSubscriptionStatus()); + addCommand("get-backlog-size", new GetBacklogSizeByMessageId()); + addCommand("analyze-backlog", new AnalyzeBacklog()); - jcommander.addCommand("get-replication-clusters", new GetReplicationClusters()); - jcommander.addCommand("set-replication-clusters", new SetReplicationClusters()); - jcommander.addCommand("remove-replication-clusters", new RemoveReplicationClusters()); + addCommand("get-replication-clusters", new GetReplicationClusters()); + addCommand("set-replication-clusters", new SetReplicationClusters()); + addCommand("remove-replication-clusters", new RemoveReplicationClusters()); - jcommander.addCommand("get-shadow-topics", new GetShadowTopics()); - jcommander.addCommand("set-shadow-topics", new SetShadowTopics()); - jcommander.addCommand("remove-shadow-topics", new RemoveShadowTopics()); - jcommander.addCommand("create-shadow-topic", new CreateShadowTopic()); - jcommander.addCommand("get-shadow-source", new GetShadowSource()); + addCommand("get-shadow-topics", new GetShadowTopics()); + addCommand("set-shadow-topics", new SetShadowTopics()); + addCommand("remove-shadow-topics", new RemoveShadowTopics()); + addCommand("create-shadow-topic", new CreateShadowTopic()); + addCommand("get-shadow-source", new GetShadowSource()); - jcommander.addCommand("get-schema-validation-enforce", new GetSchemaValidationEnforced()); - jcommander.addCommand("set-schema-validation-enforce", new SetSchemaValidationEnforced()); + addCommand("get-schema-validation-enforce", new GetSchemaValidationEnforced()); + addCommand("set-schema-validation-enforce", new SetSchemaValidationEnforced()); - jcommander.addCommand("trim-topic", new TrimTopic()); - - initDeprecatedCommands(); + addCommand("trim-topic", new TrimTopic()); } - private void initDeprecatedCommands() { - IUsageFormatter usageFormatter = jcommander.getUsageFormatter(); - if (usageFormatter instanceof CmdUsageFormatter) { - CmdUsageFormatter cmdUsageFormatter = (CmdUsageFormatter) usageFormatter; - cmdUsageFormatter.addDeprecatedCommand("enable-deduplication"); - cmdUsageFormatter.addDeprecatedCommand("disable-deduplication"); - cmdUsageFormatter.addDeprecatedCommand("get-deduplication-enabled"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-consumers"); - cmdUsageFormatter.addDeprecatedCommand("set-max-consumers"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-consumers"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-unacked-messages-per-consumer"); - cmdUsageFormatter.addDeprecatedCommand("set-max-unacked-messages-per-consumer"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-unacked-messages-per-consumer"); - - cmdUsageFormatter.addDeprecatedCommand("get-message-ttl"); - cmdUsageFormatter.addDeprecatedCommand("set-message-ttl"); - cmdUsageFormatter.addDeprecatedCommand("remove-message-ttl"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-consumers-per-subscription"); - cmdUsageFormatter.addDeprecatedCommand("set-max-consumers-per-subscription"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-consumers-per-subscription"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-unacked-messages-on-consumer"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-unacked-messages-on-consumer"); - cmdUsageFormatter.addDeprecatedCommand("set-max-unacked-messages-on-consumer"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-unacked-messages-on-subscription"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-unacked-messages-on-subscription"); - cmdUsageFormatter.addDeprecatedCommand("set-max-unacked-messages-on-subscription"); - - cmdUsageFormatter.addDeprecatedCommand("get-publish-rate"); - cmdUsageFormatter.addDeprecatedCommand("set-publish-rate"); - cmdUsageFormatter.addDeprecatedCommand("remove-publish-rate"); - - cmdUsageFormatter.addDeprecatedCommand("get-subscribe-rate"); - cmdUsageFormatter.addDeprecatedCommand("set-subscribe-rate"); - cmdUsageFormatter.addDeprecatedCommand("remove-subscribe-rate"); - - cmdUsageFormatter.addDeprecatedCommand("get-maxProducers"); - cmdUsageFormatter.addDeprecatedCommand("set-maxProducers"); - cmdUsageFormatter.addDeprecatedCommand("remove-maxProducers"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-message-size"); - cmdUsageFormatter.addDeprecatedCommand("set-max-message-size"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-message-size"); - - cmdUsageFormatter.addDeprecatedCommand("get-retention"); - cmdUsageFormatter.addDeprecatedCommand("set-retention"); - cmdUsageFormatter.addDeprecatedCommand("remove-retention"); - - cmdUsageFormatter.addDeprecatedCommand("get-backlog-quotas"); - cmdUsageFormatter.addDeprecatedCommand("set-backlog-quota"); - cmdUsageFormatter.addDeprecatedCommand("remove-backlog-quota"); - - cmdUsageFormatter.addDeprecatedCommand("get-persistence"); - cmdUsageFormatter.addDeprecatedCommand("set-persistence"); - cmdUsageFormatter.addDeprecatedCommand("remove-persistence"); - - cmdUsageFormatter.addDeprecatedCommand("get-inactive-topic-policies"); - cmdUsageFormatter.addDeprecatedCommand("set-inactive-topic-policies"); - cmdUsageFormatter.addDeprecatedCommand("remove-inactive-topic-policies"); - - cmdUsageFormatter.addDeprecatedCommand("get-compaction-threshold"); - cmdUsageFormatter.addDeprecatedCommand("set-compaction-threshold"); - cmdUsageFormatter.addDeprecatedCommand("remove-compaction-threshold"); - - cmdUsageFormatter.addDeprecatedCommand("get-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("set-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("remove-dispatch-rate"); - - cmdUsageFormatter.addDeprecatedCommand("get-deduplication"); - cmdUsageFormatter.addDeprecatedCommand("set-deduplication"); - cmdUsageFormatter.addDeprecatedCommand("remove-deduplication"); - - cmdUsageFormatter.addDeprecatedCommand("get-deduplication-snapshot-interval"); - cmdUsageFormatter.addDeprecatedCommand("set-deduplication-snapshot-interval"); - cmdUsageFormatter.addDeprecatedCommand("remove-deduplication-snapshot-interval"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-unacked-messages-on-subscription"); - cmdUsageFormatter.addDeprecatedCommand("set-max-unacked-messages-on-subscription"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-unacked-messages-on-subscription"); - - cmdUsageFormatter.addDeprecatedCommand("set-subscription-types-enabled"); - cmdUsageFormatter.addDeprecatedCommand("get-subscription-types-enabled"); - cmdUsageFormatter.addDeprecatedCommand("remove-subscription-types-enabled"); - - cmdUsageFormatter.addDeprecatedCommand("get-delayed-delivery"); - cmdUsageFormatter.addDeprecatedCommand("set-delayed-delivery"); - cmdUsageFormatter.addDeprecatedCommand("remove-delayed-delivery"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-producers"); - cmdUsageFormatter.addDeprecatedCommand("set-max-producers"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-producers"); - - cmdUsageFormatter.addDeprecatedCommand("get-replicator-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("set-replicator-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("remove-replicator-dispatch-rate"); - - cmdUsageFormatter.addDeprecatedCommand("get-subscription-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("set-subscription-dispatch-rate"); - cmdUsageFormatter.addDeprecatedCommand("remove-subscription-dispatch-rate"); - - cmdUsageFormatter.addDeprecatedCommand("get-max-subscriptions-per-topic"); - cmdUsageFormatter.addDeprecatedCommand("set-max-subscriptions-per-topic"); - cmdUsageFormatter.addDeprecatedCommand("remove-max-subscriptions-per-topic"); - - cmdUsageFormatter.addDeprecatedCommand("get-offload-policies"); - cmdUsageFormatter.addDeprecatedCommand("set-offload-policies"); - cmdUsageFormatter.addDeprecatedCommand("remove-offload-policies"); - } - } - - @Parameters(commandDescription = "Get the list of topics under a namespace.") + @Command(description = "Get the list of topics under a namespace.") private class ListCmd extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = {"-td", "--topic-domain"}, + @Option(names = {"-td", "--topic-domain"}, description = "Allowed topic domain (persistent, non_persistent).") private TopicDomain topicDomain; - @Parameter(names = { "-b", + @Option(names = { "-b", "--bundle" }, description = "Namespace bundle to get list of topics") private String bundle; - @Parameter(names = { "-ist", + @Option(names = { "-ist", "--include-system-topic" }, description = "Include system topic") private boolean includeSystemTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); ListTopicsOptions options = ListTopicsOptions.builder() .bundle(bundle) .includeSystemTopic(includeSystemTopic) @@ -424,321 +302,315 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the list of partitioned topics under a namespace.") + @Command(description = "Get the list of partitioned topics under a namespace.") private class PartitionedTopicListCmd extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "tenant/namespace", arity = "1") + private String namespaceName; - @Parameter(names = { "-ist", + @Option(names = { "-ist", "--include-system-topic" }, description = "Include system topic") private boolean includeSystemTopic; @Override void run() throws PulsarAdminException { - String namespace = validateNamespace(params); + String namespace = validateNamespace(namespaceName); ListTopicsOptions options = ListTopicsOptions.builder().includeSystemTopic(includeSystemTopic).build(); print(getTopics().getPartitionedTopicList(namespace, options)); } } - @Parameters(commandDescription = "Grant a new permission to a client role on a single topic.") + @Command(description = "Grant a new permission to a client role on a single topic.") private class GrantPermissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-r", "--role"}, description = "Client role to which grant permissions", required = true) + @Option(names = {"-r", "--role"}, description = "Client role to which grant permissions", required = true) private String role; - @Parameter(names = {"-a", "--actions"}, description = "Actions to be granted (produce,consume,sources,sinks," - + "functions,packages)", required = true) + @Option(names = {"-a", "--actions"}, description = "Actions to be granted (produce,consume,sources,sinks," + + "functions,packages)", required = true, split = ",") private List actions; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().grantPermission(topic, role, getAuthActions(actions)); } } - @Parameters(commandDescription = "Revoke permissions on a topic. " + @Command(description = "Revoke permissions on a topic. " + "Revoke permissions to a client role on a single topic. If the permission " + "was not set at the topic level, but rather at the namespace level, this " + "operation will return an error (HTTP status code 412).") private class RevokePermissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-r", "--role"}, description = "Client role to which revoke permissions", required = true) + @Option(names = {"-r", "--role"}, description = "Client role to which revoke permissions", required = true) private String role; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().revokePermissions(topic, role); } } - @Parameters(commandDescription = "Get the permissions on a topic. " + @Command(description = "Get the permissions on a topic. " + "Retrieve the effective permissions for a topic. These permissions are defined " + "by the permissions set at the namespace level combined (union) with any eventual " + "specific permission set on the topic.") private class Permissions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getPermissions(topic)); } } - @Parameters(commandDescription = "Lookup a topic from the current serving broker") + @Command(description = "Lookup a topic from the current serving broker") private class Lookup extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getAdmin().lookups().lookupTopic(topic)); } } - @Parameters(commandDescription = "Lookup a partitioned topic from the current serving broker") + @Command(description = "Lookup a partitioned topic from the current serving broker") protected class PartitionedLookup extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/partitionedTopic", required = true) - protected java.util.List params; - @Parameter(names = { "-s", - "--sort-by-broker" }, description = "Sort partitioned-topic by Broker Url") + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + protected String topicName; + @Option(names = { "-s", + "--sort-by-broker" }, description = "Sort partitioned-topic by Broker Url") protected boolean sortByBroker = false; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); if (sortByBroker) { - print(lookupPartitionedTopicSortByBroker(topic)); + Map partitionLookup = getAdmin().lookups().lookupPartitionedTopic(topic); + Map> result = new HashMap<>(); + for (Map.Entry entry : partitionLookup.entrySet()) { + List topics = result.getOrDefault(entry.getValue(), new ArrayList()); + topics.add(entry.getKey()); + result.put(entry.getValue(), topics); + } + print(result); } else { print(getAdmin().lookups().lookupPartitionedTopic(topic)); } } } - private Map> lookupPartitionedTopicSortByBroker(String topic) throws PulsarAdminException { - Map partitionLookup = getAdmin().lookups().lookupPartitionedTopic(topic); - Map> result = new HashMap<>(); - for (Map.Entry entry : partitionLookup.entrySet()) { - List topics = result.getOrDefault(entry.getValue(), new ArrayList()); - topics.add(entry.getKey()); - result.put(entry.getValue(), topics); - } - return result; - } - - @Parameters(commandDescription = "Get Namespace bundle range of a topic") + @Command(description = "Get Namespace bundle range of a topic") private class GetBundleRange extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getAdmin().lookups().getBundleRange(topic)); } } - @Parameters(commandDescription = "Create a partitioned topic. " + @Command(description = "Create a partitioned topic. " + "The partitioned topic has to be created before creating a producer on it.") private class CreatePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-p", + @Option(names = { "-p", "--partitions" }, description = "Number of partitions for the topic", required = true) private int numPartitions; - @Parameter(names = {"--metadata", "-m"}, description = "key value pair properties(a=a,b=b,c=c)") + @Option(names = {"--metadata", "-m"}, description = "key value pair properties(a=a,b=b,c=c)") private java.util.List metadata; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); Map map = parseListKeyValueMap(metadata); getTopics().createPartitionedTopic(topic, numPartitions, map); } } - @Parameters(commandDescription = "Try to create partitions for partitioned topic. " + @Command(description = "Try to create partitions for partitioned topic. " + "The partitions of partition topic has to be created, can be used by repair partitions when " + "topic auto creation is disabled") private class CreateMissedPartitionsCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().createMissedPartitions(topic); } } - @Parameters(commandDescription = "Create a non-partitioned topic.") + @Command(description = "Create a non-partitioned topic.") private class CreateNonPartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--metadata", "-m"}, description = "key value pair properties(a=a,b=b,c=c)") + @Option(names = {"--metadata", "-m"}, description = "key value pair properties(a=a,b=b,c=c)") private java.util.List metadata; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); Map map = parseListKeyValueMap(metadata); getTopics().createNonPartitionedTopic(topic, map); } } - @Parameters(commandDescription = "Update existing partitioned topic. " + @Command(description = "Update existing partitioned topic. " + "New updating number of partitions must be greater than existing number of partitions.") private class UpdatePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-p", + @Option(names = { "-p", "--partitions" }, description = "Number of partitions for the topic", required = true) private int numPartitions; - @Parameter(names = { "-ulo", + @Option(names = { "-ulo", "--update-local-only"}, description = "Update partitions number for topic in local cluster only") private boolean updateLocalOnly = false; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Update forcefully without validating existing partitioned topic") private boolean force; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().updatePartitionedTopic(topic, numPartitions, updateLocalOnly, force); } } - @Parameters(commandDescription = "Get the partitioned topic metadata. " + @Command(description = "Get the partitioned topic metadata. " + "If the topic is not created or is a non-partitioned topic, it returns empty topic with 0 partitions") private class GetPartitionedTopicMetadataCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getPartitionedTopicMetadata(topic)); } } - @Parameters(commandDescription = "Get the topic properties.") + @Command(description = "Get the topic properties.") private class GetPropertiesCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getProperties(topic)); } } - @Parameters(commandDescription = "Update the properties of on a topic") + @Command(description = "Update the properties of on a topic") private class UpdateProperties extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", - required = false, splitter = NoSplitter.class) - private java.util.List properties; + @Option(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", + required = false) + private Map properties; @Override void run() throws Exception { - String topic = validateTopicName(params); - Map map = parseListKeyValueMap(properties); - if (map == null) { - map = Collections.emptyMap(); + String topic = validateTopicName(topicName); + if (properties == null) { + properties = Collections.emptyMap(); } - getTopics().updateProperties(topic, map); + getTopics().updateProperties(topic, properties); } } - @Parameters(commandDescription = "Remove the key in properties of a topic") + @Command(description = "Remove the key in properties of a topic") private class RemoveProperties extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--key", "-k"}, description = "The key to remove in the properties of topic") + @Option(names = {"--key", "-k"}, description = "The key to remove in the properties of topic") private String key; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().removeProperties(topic, key); } } - @Parameters(commandDescription = "Delete a partitioned topic. " + @Command(description = "Delete a partitioned topic. " + "It will also delete all the partitions of the topic if it exists." + "And the application is not able to connect to the topic(delete then re-create with same name) again " + "if the schema auto uploading is disabled. Besides, users should to use the truncate cmd to clean up " + "data of the topic instead of delete cmd if users continue to use this topic later.") private class DeletePartitionedCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Close all producer/consumer/replicator and delete topic forcefully") private boolean force = false; - @Parameter(names = {"-d", "--deleteSchema"}, description = "Delete schema while deleting topic, " + @Option(names = {"-d", "--deleteSchema"}, description = "Delete schema while deleting topic, " + "but the parameter is invalid and the schema is always deleted", hidden = true) private boolean deleteSchema = false; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().deletePartitionedTopic(topic, force); } } - @Parameters(commandDescription = "Delete a topic. " + @Command(description = "Delete a topic. " + "The topic cannot be deleted if there's any active subscription or producers connected to it." + "And the application is not able to connect to the topic(delete then re-create with same name) again " + "if the schema auto uploading is disabled. Besides, users should to use the truncate cmd to clean up " + "data of the topic instead of delete cmd if users continue to use this topic later.") protected class DeleteCmd extends CliCommand { - @Parameter(description = "Provide either a single topic in the format 'persistent://tenant/namespace/topic', " + @Parameters(description = "Provide either a single topic in the format 'persistent://tenant/namespace/topic', " + "or a path to a file containing a list of topics, e.g., 'path://resources/topics.txt'. " - + "This parameter is required.", - required = true) - java.util.List params; + + "This parameter is required.", arity = "1") + protected String topic; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Close all producer/consumer/replicator and delete topic forcefully") private boolean force = false; - @Parameter(names = {"-d", "--deleteSchema"}, description = "Delete schema while deleting topic, " + @Option(names = {"-d", "--deleteSchema"}, description = "Delete schema while deleting topic, " + "but the parameter is invalid and the schema is always deleted", hidden = true) private boolean deleteSchema = false; - @Parameter(names = {"-r", "regex"}, + @Option(names = {"-r", "regex"}, description = "Use a regex expression to match multiple topics for deletion.") boolean regex = false; - @Parameter(names = {"--from-file"}, description = "Read a list of topics from a file for deletion.") + @Option(names = {"--from-file"}, description = "Read a list of topics from a file for deletion.") boolean readFromFile; @@ -748,8 +620,7 @@ void run() throws PulsarAdminException, IOException { throw new ParameterException("Could not apply regex when read topics from file."); } if (readFromFile) { - String path = checkArgument(params); - List topicsFromFile = Files.readAllLines(Path.of(path)); + List topicsFromFile = Files.readAllLines(Path.of(topic)); for (String t : topicsFromFile) { try { getTopics().delete(t, force); @@ -758,11 +629,11 @@ void run() throws PulsarAdminException, IOException { } } } else { - String topic = validateTopicName(params); + String topicName = validateTopicName(topic); if (regex) { String namespace = TopicName.get(topic).getNamespace(); List topics = getTopics().getList(namespace); - topics = topics.stream().filter(s -> s.matches(topic)).toList(); + topics = topics.stream().filter(s -> s.matches(topicName)).toList(); for (String t : topics) { try { getTopics().delete(t, force); @@ -772,7 +643,7 @@ void run() throws PulsarAdminException, IOException { } } else { try { - getTopics().delete(topic, force); + getTopics().delete(topicName, force); } catch (Exception e) { print("Failed to delete topic: " + topic + ". Exception: " + e); } @@ -781,114 +652,114 @@ void run() throws PulsarAdminException, IOException { } } - @Parameters(commandDescription = "Truncate a topic. \n" + @Command(description = "Truncate a topic. \n" + "\t\tThe truncate operation will move all cursors to the end of the topic " + "and delete all inactive ledgers. ") private class TruncateCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic\n", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().truncate(topic); } } - @Parameters(commandDescription = "Unload a topic.") + @Command(description = "Unload a topic.") private class UnloadCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().unload(topic); } } - @Parameters(commandDescription = "Get the list of subscriptions on the topic") + @Command(description = "Get the list of subscriptions on the topic") private class ListSubscriptions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getSubscriptions(topic)); } } - @Parameters(commandDescription = "Delete a durable subscriber from a topic. " + @Command(description = "Delete a durable subscriber from a topic. " + "The subscription cannot be deleted if there are any active consumers attached to it") private class DeleteSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-f", + @Option(names = { "-f", "--force" }, description = "Disconnect and close all consumers and delete subscription forcefully") private boolean force = false; - @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be deleted", required = true) + @Option(names = {"-s", "--subscription"}, description = "Subscription to be deleted", required = true) private String subName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().deleteSubscription(topic, subName, force); } } - @Parameters(commandDescription = "Get the stats for the topic and its connected producers and consumers. " + @Command(name = "stats", description = "Get the stats for the topic and its connected producers and consumers. " + "All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.") private class GetStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-gpb", + @Option(names = { "-gpb", "--get-precise-backlog" }, description = "Set true to get precise backlog") private boolean getPreciseBacklog = false; - @Parameter(names = { "-sbs", + @Option(names = { "-sbs", "--get-subscription-backlog-size" }, description = "Set true to get backlog size for each subscription" - + ", locking required. If set to false, the attribute 'backlogSize' in the response will be -1") + + ", locking required. If set to false, the attribute 'backlogSize' in the response will be -1") private boolean subscriptionBacklogSize = true; - @Parameter(names = { "-etb", + @Option(names = { "-etb", "--get-earliest-time-in-backlog" }, description = "Set true to get earliest time in backlog") private boolean getEarliestTimeInBacklog = false; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getStats(topic, getPreciseBacklog, subscriptionBacklogSize, getEarliestTimeInBacklog)); } } - @Parameters(commandDescription = "Get the internal stats for the topic") + @Command(description = "Get the internal stats for the topic") private class GetInternalStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-m", + @Option(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") private boolean metadata = false; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getInternalStats(topic, metadata)); } } - @Parameters(commandDescription = "Get the internal metadata info for the topic") + @Command(description = "Get the internal metadata info for the topic") private class GetInternalInfo extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); String internalInfo = getTopics().getInternalInfo(topic); if (internalInfo == null) { System.out.println("Did not find any internal metadata info"); @@ -900,105 +771,105 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the stats for the partitioned topic " + @Command(description = "Get the stats for the partitioned topic " + "and its connected producers and consumers. All the rates are computed over a 1 minute window " + "and are relative the last completed 1 minute period.") private class GetPartitionedStats extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = "--per-partition", description = "Get per partition stats") + @Option(names = "--per-partition", description = "Get per partition stats") private boolean perPartition = false; - @Parameter(names = { "-gpb", + @Option(names = { "-gpb", "--get-precise-backlog" }, description = "Set true to get precise backlog") private boolean getPreciseBacklog = false; - @Parameter(names = { "-sbs", + @Option(names = { "-sbs", "--get-subscription-backlog-size" }, description = "Set true to get backlog size for each subscription" + ", locking required.") private boolean subscriptionBacklogSize = true; - @Parameter(names = { "-etb", + @Option(names = { "-etb", "--get-earliest-time-in-backlog" }, description = "Set true to get earliest time in backlog") private boolean getEarliestTimeInBacklog = false; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getPartitionedStats(topic, perPartition, getPreciseBacklog, subscriptionBacklogSize, getEarliestTimeInBacklog)); } } - @Parameters(commandDescription = "Get the internal stats for the partitioned topic " + @Command(description = "Get the internal stats for the partitioned topic " + "and its connected producers and consumers. All the rates are computed over a 1 minute window " + "and are relative the last completed 1 minute period.") private class GetPartitionedStatsInternal extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); print(getTopics().getPartitionedInternalStats(topic)); } } - @Parameters(commandDescription = "Skip all the messages for the subscription") + @Command(description = "Skip all the messages for the subscription") private class ClearBacklog extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be cleared", required = true) + @Option(names = { "-s", "--subscription" }, description = "Subscription to be cleared", required = true) private String subName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().skipAllMessages(topic, subName); } } - @Parameters(commandDescription = "Skip some messages for the subscription") + @Command(description = "Skip some messages for the subscription") private class Skip extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to be skip messages on", required = true) private String subName; - @Parameter(names = { "-n", "--count" }, description = "Number of messages to skip", required = true) + @Option(names = { "-n", "--count" }, description = "Number of messages to skip", required = true) private long numMessages; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().skipMessages(topic, subName, numMessages); } } - @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) " + @Command(description = "Expire messages that older than given expiry time (in seconds) " + "for the subscription") private class ExpireMessages extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to be skip messages on", required = true) private String subName; - @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + @Option(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w)", converter = TimeUnitToSecondsConverter.class) private Long expireTimeInSeconds = -1L; - @Parameter(names = { "--position", + @Option(names = { "--position", "-p" }, description = "message position to reset back to (ledgerId:entryId)", required = false) private String messagePosition; - @Parameter(names = { "-e", "--exclude-reset-position" }, + @Option(names = { "-e", "--exclude-reset-position" }, description = "Exclude the reset position, start consume messages from the next position.") private boolean excludeResetPosition = false; @@ -1008,7 +879,7 @@ void run() throws PulsarAdminException { throw new ParameterException(String.format("Can't expire message by time and " + "by message position at the same time.")); } - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); if (expireTimeInSeconds >= 0) { getTopics().expireMessages(topic, subName, expireTimeInSeconds); } else if (isNotBlank(messagePosition)) { @@ -1023,47 +894,47 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) " + @Command(description = "Expire messages that older than given expiry time (in seconds) " + "for all subscriptions") private class ExpireMessagesForAllSubscriptions extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds " + @Option(names = {"-t", "--expireTime"}, description = "Expire messages older than time in seconds " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w)", required = true, converter = TimeUnitToSecondsConverter.class) private Long expireTimeInSeconds; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getTopics().expireMessagesForAllSubscriptions(topic, expireTimeInSeconds); } } - @Parameters(commandDescription = "Create a new subscription on a topic") + @Command(description = "Create a new subscription on a topic") private class CreateSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Name of subscription to be created", required = true) private String subscriptionName; - @Parameter(names = { "-m" , "--messageId" }, description = "messageId where to create the subscription. " + @Option(names = { "-m" , "--messageId" }, description = "messageId where to create the subscription. " + "It can be either 'latest', 'earliest' or (ledgerId:entryId)", required = false) private String messageIdStr = "latest"; - @Parameter(names = { "-r", "--replicated" }, description = "replicated subscriptions", required = false) + @Option(names = { "-r", "--replicated" }, description = "replicated subscriptions", required = false) private boolean replicated = false; - @Parameter(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", - required = false, splitter = NoSplitter.class) - private java.util.List properties; + @Option(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", + required = false) + private Map properties; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); MessageId messageId; if (messageIdStr.equals("latest")) { messageId = MessageId.latest; @@ -1072,57 +943,55 @@ void run() throws PulsarAdminException { } else { messageId = validateMessageIdString(messageIdStr); } - Map map = parseListKeyValueMap(properties); - getTopics().createSubscription(topic, subscriptionName, messageId, replicated, map); + getTopics().createSubscription(topic, subscriptionName, messageId, replicated, properties); } } - @Parameters(commandDescription = "Update the properties of a subscription on a topic") + @Command(description = "Update the properties of a subscription on a topic") private class UpdateSubscriptionProperties extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to update", required = true) private String subscriptionName; - @Parameter(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", - required = false, splitter = NoSplitter.class) - private java.util.List properties; + @Option(names = {"--property", "-p"}, description = "key value pair properties(-p a=b -p c=d)", + required = false) + private Map properties; - @Parameter(names = {"--clear", "-c"}, description = "Remove all properties", + @Option(names = {"--clear", "-c"}, description = "Remove all properties", required = false) private boolean clear; @Override void run() throws Exception { - String topic = validateTopicName(params); - Map map = parseListKeyValueMap(properties); - if (map == null) { - map = Collections.emptyMap(); + String topic = validateTopicName(topicName); + if (properties == null) { + properties = Collections.emptyMap(); } - if ((map.isEmpty()) && !clear) { - throw new ParameterException("If you want to clear the properties you have to use --clear"); + if ((properties.isEmpty()) && !clear) { + throw new IllegalArgumentException("If you want to clear the properties you have to use --clear"); } - if (clear && !map.isEmpty()) { - throw new ParameterException("If you set --clear then you should not pass any properties"); + if (clear && !properties.isEmpty()) { + throw new IllegalArgumentException("If you set --clear then you should not pass any properties"); } - getTopics().updateSubscriptionProperties(topic, subscriptionName, map); + getTopics().updateSubscriptionProperties(topic, subscriptionName, properties); } } - @Parameters(commandDescription = "Get the properties of a subscription on a topic") + @Command(description = "Get the properties of a subscription on a topic") private class GetSubscriptionProperties extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to describe", required = true) private String subscriptionName; @Override void run() throws Exception { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); Map result = getTopics().getSubscriptionProperties(topic, subscriptionName); // Ensure we are using JSON and not Java toString() System.out.println(ObjectMapperFactory.getMapper().writer().writeValueAsString(result)); @@ -1130,33 +999,33 @@ void run() throws Exception { } - @Parameters(commandDescription = "Reset position for subscription to a position that is closest to " + @Command(description = "Reset position for subscription to a position that is closest to " + "timestamp or messageId.") private class ResetCursor extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", - "--subscription" }, description = "Subscription to reset position on", required = true) + @Option(names = {"-s", + "--subscription"}, description = "Subscription to reset position on", required = true) private String subName; - @Parameter(names = { "--time", + @Option(names = { "--time", "-t" }, description = "time in minutes to reset back to " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w)", required = false, converter = TimeUnitToMillisConverter.class) private Long resetTimeInMillis = null; - @Parameter(names = { "--messageId", + @Option(names = { "--messageId", "-m" }, description = "messageId to reset back to ('latest', 'earliest', or 'ledgerId:entryId')") private String resetMessageIdStr; - @Parameter(names = { "-e", "--exclude-reset-position" }, + @Option(names = { "-e", "--exclude-reset-position" }, description = "Exclude the reset position, start consume messages from the next position.") private boolean excludeResetPosition = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (isNotBlank(resetMessageIdStr)) { MessageId messageId; if ("earliest".equals(resetMessageIdStr)) { @@ -1182,14 +1051,14 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Terminate a topic and don't allow any more messages to be published") + @Command(description = "Terminate a topic and don't allow any more messages to be published") private class Terminate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); try { MessageId lastMessageId = getTopics().terminateTopicAsync(persistentTopic).get(); @@ -1200,37 +1069,37 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Terminate a partitioned topic and don't allow any more messages to be published") + @Command(description = "Terminate a partitioned topic and don't allow any more messages to be published") private class PartitionedTerminate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException, TimeoutException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); Map messageIds = getTopics().terminatePartitionedTopic(persistentTopic); - for (Map.Entry entry: messageIds.entrySet()) { + for (Map.Entry entry : messageIds.entrySet()) { String topicName = persistentTopic + "-partition-" + entry.getKey(); - System.out.println("Topic " + topicName + " successfully terminated at " + entry.getValue()); + System.out.println("Topic " + topicName + " successfully terminated at " + entry.getValue()); } } } - @Parameters(commandDescription = "Peek some messages for the subscription") + @Command(description = "Peek some messages for the subscription") private class PeekMessages extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription to get messages from", required = true) private String subName; - @Parameter(names = { "-n", "--count" }, description = "Number of messages (default 1)", required = false) + @Option(names = { "-n", "--count" }, description = "Number of messages (default 1)", required = false) private int numMessages = 1; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); List> messages = getTopics().peekMessages(persistentTopic, subName, numMessages); int position = 0; for (Message msg : messages) { @@ -1276,24 +1145,24 @@ void run() throws PulsarAdminException { } - @Parameters(commandDescription = "Examine a specific message on a topic by position relative to the" + @Command(description = "Examine a specific message on a topic by position relative to the" + " earliest or the latest message.") private class ExamineMessages extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-i", "--initialPosition" }, + @Option(names = { "-i", "--initialPosition" }, description = "Relative start position to examine message." + "It can be 'latest' or 'earliest', default is latest") private String initialPosition = "latest"; - @Parameter(names = { "-m", "--messagePosition" }, + @Option(names = { "-m", "--messagePosition" }, description = "The position of messages (default 1)", required = false) private long messagePosition = 1; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); MessageImpl message = (MessageImpl) getTopics().examineMessage(persistentTopic, initialPosition, messagePosition); @@ -1332,24 +1201,24 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get message by its ledgerId and entryId") + @Command(description = "Get message by its ledgerId and entryId") private class GetMessageById extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-l", "--ledgerId" }, + @Option(names = { "-l", "--ledgerId" }, description = "ledger id pointing to the desired ledger", required = true) private long ledgerId; - @Parameter(names = { "-e", "--entryId" }, + @Option(names = { "-e", "--entryId" }, description = "entry id pointing to the desired entry", required = true) private long entryId; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); MessageImpl message = (MessageImpl) getTopics().getMessageById(persistentTopic, ledgerId, entryId); if (message == null) { @@ -1394,12 +1263,12 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get message ID") + @Command(description = "Get message ID") private class GetMessageId extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-d", "--datetime" }, + @Option(names = { "-d", "--datetime" }, description = "datetime at or before this messageId. This datetime is in format of " + "ISO_OFFSET_DATE_TIME, e.g. 2021-06-28T16:53:08Z or 2021-06-28T16:53:08.123456789+08:00", required = true) @@ -1407,7 +1276,7 @@ private class GetMessageId extends CliCommand { @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); long timestamp = DateFormatter.parse(datetime); MessageId messageId = getTopics().getMessageIdByTimestamp(persistentTopic, timestamp); @@ -1419,32 +1288,32 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Compact a topic") + @Command(description = "Compact a topic") private class Compact extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().triggerCompaction(persistentTopic); System.out.println("Topic compaction requested for " + persistentTopic); } } - @Parameters(commandDescription = "Status of compaction on a topic") + @Command(description = "Status of compaction on a topic") private class CompactionStatusCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-w", "--wait-complete" }, + @Option(names = { "-w", "--wait-complete" }, description = "Wait for compaction to complete", required = false) private boolean wait = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); try { LongRunningProcessStatus status = getTopics().compactionStatus(persistentTopic); @@ -1490,20 +1359,20 @@ static MessageId findFirstLedgerWithinThreshold(List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); PersistentTopicInternalStats stats = getTopics().getInternalStats(persistentTopic, false); if (stats.ledgers.size() < 1) { @@ -1524,18 +1393,18 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Check the status of data offloading from a topic to long-term storage") + @Command(description = "Check the status of data offloading from a topic to long-term storage") private class OffloadStatusCmd extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-w", "--wait-complete" }, + @Option(names = { "-w", "--wait-complete" }, description = "Wait for offloading to complete", required = false) private boolean wait = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); try { OffloadProcessStatus status = getTopics().offloadStatus(persistentTopic); @@ -1565,54 +1434,54 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "get the last commit message id of topic") + @Command(description = "get the last commit message id of topic") private class GetLastMessageId extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getLastMessageId(persistentTopic)); } } - @Parameters(commandDescription = "Get the backlog quota policies for a topic") + @Command(description = "Get the backlog quota policies for a topic", hidden = true) private class GetBacklogQuotaMap extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") + @Option(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getBacklogQuotaMap(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set a backlog quota policy for a topic") + @Command(description = "Set a backlog quota policy for a topic", hidden = true) private class SetBacklogQuota extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", + @Option(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", converter = ByteUnitToLongConverter.class) private Long limit = -1L; - @Parameter(names = { "-lt", "--limitTime" }, + @Option(names = { "-lt", "--limitTime" }, description = "Time limit in second (or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w), " + "non-positive number for disabling time limit.", - converter = TimeUnitToSecondsConverter.class, validateValueWith = IntegerMaxValueLongValidator.class) + converter = TimeUnitToSecondsConverter.class) private Long limitTimeInSec = -1L; - @Parameter(names = { "-p", "--policy" }, + @Option(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. Valid options are: " + "[producer_request_hold, producer_exception, consumer_backlog_eviction]", required = true) private String policyStr; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + @Option(names = {"-t", "--type"}, description = "Backlog quota type to set. Valid options are: " + "destination_storage and message_age. " + "destination_storage limits backlog by size (in bytes). " + "message_age limits backlog by time, that is, message timestamp (broker or publish timestamp). " @@ -1638,7 +1507,7 @@ void run() throws PulsarAdminException { backlogQuotaTypeStr, Arrays.toString(BacklogQuota.BacklogQuotaType.values()))); } - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setBacklogQuota(persistentTopic, BacklogQuota.builder().limitSize(limit) .limitTime(limitTimeInSec.intValue()) @@ -1648,184 +1517,184 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove a backlog quota policy from a topic") + @Command(description = "Remove a backlog quota policy from a topic", hidden = true) private class RemoveBacklogQuota extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-t", "--type"}, description = "Backlog quota type to remove") + @Option(names = {"-t", "--type"}, description = "Backlog quota type to remove") private String backlogQuotaType = BacklogQuota.BacklogQuotaType.destination_storage.name(); @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeBacklogQuota(persistentTopic, BacklogQuota.BacklogQuotaType.valueOf(backlogQuotaType)); } } - @Parameters(commandDescription = "Get the replication clusters for a topic") + @Command(description = "Get the replication clusters for a topic") private class GetReplicationClusters extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getReplicationClusters(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set the replication clusters for a topic") + @Command(description = "Set the replication clusters for a topic") private class SetReplicationClusters extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--clusters", + @Option(names = { "--clusters", "-c" }, description = "Replication Cluster Ids list (comma separated values)", required = true) private String clusterIds; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); List clusters = Lists.newArrayList(clusterIds.split(",")); getTopics().setReplicationClusters(persistentTopic, clusters); } } - @Parameters(commandDescription = "Remove the replication clusters for a topic") + @Command(description = "Remove the replication clusters for a topic") private class RemoveReplicationClusters extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeReplicationClusters(persistentTopic); } } - @Parameters(commandDescription = "Get the shadow topics for a topic") + @Command(description = "Get the shadow topics for a topic") private class GetShadowTopics extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getShadowTopics(persistentTopic)); } } - @Parameters(commandDescription = "Set the shadow topics for a topic") + @Command(description = "Set the shadow topics for a topic") private class SetShadowTopics extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--topics", + @Option(names = { "--topics", "-t" }, description = "Shadow topic list (comma separated values)", required = true) private String shadowTopics; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); List topics = Lists.newArrayList(shadowTopics.split(",")); getTopics().setShadowTopics(persistentTopic, topics); } } - @Parameters(commandDescription = "Remove the shadow topics for a topic") + @Command(description = "Remove the shadow topics for a topic") private class RemoveShadowTopics extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeShadowTopics(persistentTopic); } } - @Parameters(commandDescription = "Create a shadow topic for an existing source topic.") + @Command(description = "Create a shadow topic for an existing source topic.") private class CreateShadowTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--source", "-s"}, description = "source topic name", required = true) + @Option(names = {"--source", "-s"}, description = "source topic name", required = true) private String sourceTopic; - @Parameter(names = {"--properties", "-p"}, description = "key value pair properties(eg: a=a b=b c=c)") - private java.util.List propertyList; + @Option(names = {"--properties", "-p"}, description = "key value pair properties(eg: a=a,b=b,c=c)", split = ",") + private Map properties; @Override void run() throws Exception { - String topic = validateTopicName(params); - Map properties = parseListKeyValueMap(propertyList); + String topic = validateTopicName(topicName); getTopics().createShadowTopic(topic, TopicName.get(sourceTopic).toString(), properties); } } - @Parameters(commandDescription = "Get the source topic for a shadow topic") + @Command(description = "Get the source topic for a shadow topic") private class GetShadowSource extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; + @Override void run() throws PulsarAdminException { - String shadowTopic = validatePersistentTopic(params); + String shadowTopic = validatePersistentTopic(topicName); print(getTopics().getShadowSource(shadowTopic)); } } - @Parameters(commandDescription = "Get the delayed delivery policy for a topic") + @Command(description = "Get the delayed delivery policy for a topic", hidden = true) private class GetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); - print(getTopics().getDelayedDeliveryPolicy(topicName, applied)); + String topic = validateTopicName(topicName); + print(getTopics().getDelayedDeliveryPolicy(topic, applied)); } } - @Parameters(commandDescription = "Set the delayed delivery policy on a topic") + @Command(description = "Set the delayed delivery policy on a topic", hidden = true) private class SetDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") + @Option(names = { "--enable", "-e" }, description = "Enable delayed delivery messages") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") + @Option(names = { "--disable", "-d" }, description = "Disable delayed delivery messages") private boolean disable = false; - @Parameter(names = { "--time", "-t" }, + @Option(names = { "--time", "-t" }, description = "The tick time for when retrying on delayed delivery messages, affecting the accuracy of " + "the delivery time compared to the scheduled time. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryTimeInMills = 1_000L; - @Parameter(names = { "--maxDelay", "-md" }, + @Option(names = {"--maxDelay", "-md"}, description = "The max allowed delay for delayed delivery. (eg: 1s, 10s, 1m, 5h, 3d)", converter = TimeUnitToMillisConverter.class) private Long delayedDeliveryMaxDelayInMillis = 0L; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); + String topic = validateTopicName(topicName); if (enable == disable) { throw new ParameterException("Need to specify either --enable or --disable"); } - getTopics().setDelayedDeliveryPolicy(topicName, DelayedDeliveryPolicies.builder() + getTopics().setDelayedDeliveryPolicy(topic, DelayedDeliveryPolicies.builder() .tickTime(delayedDeliveryTimeInMills) .active(enable) .maxDeliveryDelayInMillis(delayedDeliveryMaxDelayInMillis) @@ -1833,151 +1702,149 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the delayed delivery policy on a topic") + @Command(description = "Remove the delayed delivery policy on a topic", hidden = true) private class RemoveDelayedDelivery extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topic; @Override void run() throws PulsarAdminException { - String topicName = validateTopicName(params); + String topicName = validateTopicName(topic); getTopics().removeDelayedDeliveryPolicy(topicName); } } - @Parameters(commandDescription = "Get the message TTL for a topic") + @Command(description = "Get the message TTL for a topic", hidden = true) private class GetMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMessageTTL(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set message TTL for a topic") + @Command(description = "Set message TTL for a topic", hidden = true) private class SetMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-t", "--ttl" }, description = "Message TTL for topic in second " + @Option(names = { "-t", "--ttl" }, description = "Message TTL for topic in second " + "(or minutes, hours, days, weeks eg: 100m, 3h, 2d, 5w), " - + "allowed range from 1 to Integer.MAX_VALUE", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = {NonNegativeValueValidator.class, IntegerMaxValueLongValidator.class}) + + "allowed range from 1 to Integer.MAX_VALUE", required = true, + converter = TimeUnitToSecondsConverter.class) private Long messageTTLInSecond; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMessageTTL(persistentTopic, messageTTLInSecond.intValue()); } } - @Parameters(commandDescription = "Remove message TTL for a topic") + @Command(description = "Remove message TTL for a topic", hidden = true) private class RemoveMessageTTL extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMessageTTL(persistentTopic); } } - @Parameters(commandDescription = "Get deduplication snapshot interval for a topic") + @Command(description = "Get deduplication snapshot interval for a topic", hidden = true) private class GetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getDeduplicationSnapshotInterval(persistentTopic)); } } - @Parameters(commandDescription = "Set deduplication snapshot interval for a topic") + @Command(description = "Set deduplication snapshot interval for a topic", hidden = true) private class SetDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-i", "--interval" }, description = "Deduplication snapshot interval for topic in second, " + @Option(names = { "-i", "--interval" }, description = "Deduplication snapshot interval for topic in second, " + "allowed range from 0 to Integer.MAX_VALUE", required = true) private int interval; @Override void run() throws PulsarAdminException { if (interval < 0) { - throw new ParameterException(String.format("Invalid interval '%d'. ", interval)); + throw new IllegalArgumentException(String.format("Invalid interval '%d'. ", interval)); } - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setDeduplicationSnapshotInterval(persistentTopic, interval); } } - @Parameters(commandDescription = "Remove deduplication snapshot interval for a topic") + @Command(description = "Remove deduplication snapshot interval for a topic", hidden = true) private class RemoveDeduplicationSnapshotInterval extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeDeduplicationSnapshotInterval(persistentTopic); } } - @Parameters(commandDescription = "Get the retention policy for a topic") + @Command(description = "Get the retention policy for a topic") private class GetRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getRetention(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set the retention policy for a topic") + @Command(description = "Set the retention policy for a topic", hidden = true) private class SetRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--time", + @Option(names = { "--time", "-t" }, description = "Retention time with optional time unit suffix. " + "For example, 100m, 3h, 2d, 5w. " + "If the time unit is not specified, the default unit is seconds. For example, " + "-t 120 will set retention to 2 minutes. " + "0 means no retention and -1 means infinite time retention.", required = true, - converter = TimeUnitToSecondsConverter.class, - validateValueWith = MinNegativeOneValidator.class) + converter = TimeUnitToSecondsConverter.class) private Integer retentionTimeInSec; - @Parameter(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + @Option(names = { "--size", "-s" }, description = "Retention size limit with optional size unit suffix. " + "For example, 4096, 10M, 16G, 3T. The size unit suffix character can be k/K, m/M, g/G, or t/T. " + "If the size unit suffix is not specified, the default unit is bytes. " + "0 or less than 1MB means no retention and -1 means infinite size retention", required = true, - converter = ByteUnitIntegerConverter.class) + converter = ByteUnitToIntegerConverter.class) private Integer sizeLimit; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); - final int retentionTimeInMin = retentionTimeInSec != -1 + String persistentTopic = validatePersistentTopic(topicName); + final int retentionTimeInMin = retentionTimeInSec != -1 ? (int) TimeUnit.SECONDS.toMinutes(retentionTimeInSec) : retentionTimeInSec.intValue(); final int retentionSizeInMB = sizeLimit != -1 @@ -1988,199 +1855,197 @@ void run() throws PulsarAdminException { } @Deprecated - @Parameters(commandDescription = "Enable the deduplication policy for a topic") + @Command(description = "Enable the deduplication policy for a topic", hidden = true) private class EnableDeduplication extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().enableDeduplication(persistentTopic, true); } } @Deprecated - @Parameters(commandDescription = "Disable the deduplication policy for a topic") + @Command(description = "Disable the deduplication policy for a topic", hidden = true) private class DisableDeduplication extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().enableDeduplication(persistentTopic, false); } } - @Parameters(commandDescription = "Enable or disable deduplication for a topic") + @Command(description = "Enable or disable deduplication for a topic", hidden = true) private class SetDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable", "-e" }, description = "Enable deduplication") + @Option(names = { "--enable", "-e" }, description = "Enable deduplication") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable deduplication") + @Option(names = { "--disable", "-d" }, description = "Disable deduplication") private boolean disable = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (enable == disable) { - throw new ParameterException("Need to specify either --enable or --disable"); + throw new IllegalArgumentException("Need to specify either --enable or --disable"); } getTopics().setDeduplicationStatus(persistentTopic, enable); } } - @Parameters(commandDescription = "Get the deduplication policy for a topic") + @Command(description = "Get the deduplication policy for a topic", hidden = true) private class GetDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getDeduplicationStatus(persistentTopic)); } } - @Parameters(commandDescription = "Remove the deduplication policy for a topic") + @Command(description = "Remove the deduplication policy for a topic", hidden = true) private class RemoveDeduplicationStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeDeduplicationStatus(persistentTopic); } } - @Parameters(commandDescription = "Remove the retention policy for a topic") + @Command(description = "Remove the retention policy for a topic", hidden = true) private class RemoveRetention extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeRetention(persistentTopic); } } - @Parameters(commandDescription = "Get the persistence policies for a topic") + @Command(description = "Get the persistence policies for a topic", hidden = true) private class GetPersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getPersistence(persistentTopic)); } } - @Parameters(commandDescription = "Get the offload policies for a topic") + @Command(description = "Get the offload policies for a topic", hidden = true) private class GetOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getOffloadPolicies(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove the offload policies for a topic") + @Command(description = "Remove the offload policies for a topic", hidden = true) private class RemoveOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeOffloadPolicies(persistentTopic); } } - @Parameters(commandDescription = "Set the offload policies for a topic") + @Command(description = "Set the offload policies for a topic", hidden = true) private class SetOffloadPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-d", "--driver"}, description = "ManagedLedger offload driver", required = true) + @Option(names = {"-d", "--driver"}, description = "ManagedLedger offload driver", required = true) private String driver; - @Parameter(names = {"-r", "--region"} + @Option(names = {"-r", "--region"} , description = "ManagedLedger offload region, s3 and google-cloud-storage requires this parameter") private String region; - @Parameter(names = {"-b", "--bucket"} + @Option(names = {"-b", "--bucket"} , description = "ManagedLedger offload bucket, s3 and google-cloud-storage requires this parameter") private String bucket; - @Parameter(names = {"-e", "--endpoint"} + @Option(names = {"-e", "--endpoint"} , description = "ManagedLedger offload service endpoint, only s3 requires this parameter") private String endpoint; - @Parameter(names = {"-i", "--aws-id"} + @Option(names = {"-i", "--aws-id"} , description = "AWS Credential Id to use when using driver S3 or aws-s3") private String awsId; - @Parameter(names = {"-s", "--aws-secret"} + @Option(names = {"-s", "--aws-secret"} , description = "AWS Credential Secret to use when using driver S3 or aws-s3") private String awsSecret; - @Parameter(names = {"--ro", "--s3-role"} + @Option(names = {"--ro", "--s3-role"} , description = "S3 Role used for STSAssumeRoleSessionCredentialsProvider") private String s3Role; - @Parameter(names = {"--s3-role-session-name", "-rsn"} + @Option(names = {"--s3-role-session-name", "-rsn"} , description = "S3 role session name used for STSAssumeRoleSessionCredentialsProvider") private String s3RoleSessionName; - @Parameter( + @Option( names = {"-m", "--maxBlockSizeInBytes", "--maxBlockSize", "-mbs"}, description = "Max block size (eg: 32M, 64M), default is 64MB" + "s3 and google-cloud-storage requires this parameter", required = false, - converter = ByteUnitIntegerConverter.class, - validateValueWith = PositiveIntegerValueValidator.class) + converter = ByteUnitToIntegerConverter.class) private Integer maxBlockSizeInBytes = OffloadPoliciesImpl.DEFAULT_MAX_BLOCK_SIZE_IN_BYTES; - @Parameter( + @Option( names = {"-rb", "--readBufferSizeInBytes", "--readBufferSize", "-rbs"}, description = "Read buffer size (eg: 1M, 5M), default is 1MB" + "s3 and google-cloud-storage requires this parameter", required = false, - converter = ByteUnitIntegerConverter.class, - validateValueWith = PositiveIntegerValueValidator.class) + converter = ByteUnitToIntegerConverter.class) private Integer readBufferSizeInBytes = OffloadPoliciesImpl.DEFAULT_READ_BUFFER_SIZE_IN_BYTES; - @Parameter(names = {"-t", "--offloadThresholdInBytes", "--offloadAfterThreshold", "-oat"} + @Option(names = {"-t", "--offloadThresholdInBytes", "--offloadAfterThreshold", "-oat"} , description = "Offload after threshold size (eg: 1M, 5M)", required = false, converter = ByteUnitToLongConverter.class) private Long offloadAfterThresholdInBytes = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_BYTES; - @Parameter(names = {"-ts", "--offloadThresholdInSeconds", "--offloadAfterThresholdInSeconds", "-oats"}, + @Option(names = {"-ts", "--offloadThresholdInSeconds", "--offloadAfterThresholdInSeconds", "-oats"}, description = "Offload after threshold seconds (or minutes,hours,days,weeks eg: 100m, 3h, 2d, 5w).", converter = TimeUnitToSecondsConverter.class) private Long offloadThresholdInSeconds = OffloadPoliciesImpl.DEFAULT_OFFLOAD_THRESHOLD_IN_SECONDS; - @Parameter(names = {"-dl", "--offloadDeletionLagInMillis", "--offloadAfterElapsed", "-oae"} + @Option(names = {"-dl", "--offloadDeletionLagInMillis", "--offloadAfterElapsed", "-oae"} , description = "Delay time in Millis for deleting the bookkeeper ledger after offload " + "(or seconds,minutes,hours,days,weeks eg: 10s, 100m, 3h, 2d, 5w).", converter = TimeUnitToMillisConverter.class) private Long offloadAfterElapsedInMillis = OffloadPoliciesImpl.DEFAULT_OFFLOAD_DELETION_LAG_IN_MILLIS; - @Parameter(names = {"--offloadedReadPriority", "-orp"}, + @Option(names = {"--offloadedReadPriority", "-orp"}, description = "Read priority for offloaded messages. " + "By default, once messages are offloaded to long-term storage, " + "brokers read messages from long-term storage, but messages can still exist in BookKeeper " @@ -2205,7 +2070,7 @@ public boolean isS3Driver(String driver) { @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (!driverSupported(driver)) { throw new ParameterException( @@ -2241,31 +2106,31 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Set the persistence policies for a topic") + @Command(description = "Set the persistence policies for a topic", hidden = true) private class SetPersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-e", + @Option(names = { "-e", "--bookkeeper-ensemble" }, description = "Number of bookies to use for a topic") private int bookkeeperEnsemble = 2; - @Parameter(names = { "-w", + @Option(names = { "-w", "--bookkeeper-write-quorum" }, description = "How many writes to make of each entry") private int bookkeeperWriteQuorum = 2; - @Parameter(names = { "-a", + @Option(names = { "-a", "--bookkeeper-ack-quorum" }, description = "Number of acks (guaranteed copies) to wait for each entry") private int bookkeeperAckQuorum = 2; - @Parameter(names = { "-r", + @Option(names = { "-r", "--ml-mark-delete-max-rate" }, description = "Throttling rate of mark-delete operation " + "(0 means no throttle)") private double managedLedgerMaxMarkDeleteRate = 0; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (bookkeeperEnsemble <= 0 || bookkeeperWriteQuorum <= 0 || bookkeeperAckQuorum <= 0) { throw new ParameterException("[--bookkeeper-ensemble], [--bookkeeper-write-quorum] " + "and [--bookkeeper-ack-quorum] must greater than 0."); @@ -2278,59 +2143,59 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove the persistence policy for a topic") + @Command(description = "Remove the persistence policy for a topic", hidden = true) private class RemovePersistence extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removePersistence(persistentTopic); } } - @Parameters(commandDescription = "Get message dispatch rate for a topic") + @Command(description = "Get message dispatch rate for a topic", hidden = true) private class GetDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getDispatchRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set message dispatch rate for a topic") + @Command(description = "Set message dispatch rate for a topic", hidden = true) private class SetDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate (default -1 will be overwrite if not passed)") private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)") private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)", required = false) private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))", required = false) private boolean relativeToPublishRate = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setDispatchRate(persistentTopic, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -2341,115 +2206,115 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove message dispatch rate for a topic") + @Command(description = "Remove message dispatch rate for a topic", hidden = true) private class RemoveDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeDispatchRate(persistentTopic); } } - @Parameters(commandDescription = "Get max unacked messages policy on consumer for a topic") + @Command(description = "Get max unacked messages policy on consumer for a topic", hidden = true) private class GetMaxUnackedMessagesOnConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxUnackedMessagesOnConsumer(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove max unacked messages policy on consumer for a topic") + @Command(description = "Remove max unacked messages policy on consumer for a topic", hidden = true) private class RemoveMaxUnackedMessagesOnConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxUnackedMessagesOnConsumer(persistentTopic); } } - @Parameters(commandDescription = "Set max unacked messages policy on consumer for a topic") + @Command(description = "Set max unacked messages policy on consumer for a topic", hidden = true) private class SetMaxUnackedMessagesOnConsumer extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-m", "--maxNum"}, description = "max unacked messages num on consumer", required = true) + @Option(names = {"-m", "--maxNum"}, description = "max unacked messages num on consumer", required = true) private int maxNum; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxUnackedMessagesOnConsumer(persistentTopic, maxNum); } } - @Parameters(commandDescription = "Get max unacked messages policy on subscription for a topic") + @Command(description = "Get max unacked messages policy on subscription for a topic", hidden = true) private class GetMaxUnackedMessagesOnSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxUnackedMessagesOnSubscription(persistentTopic, applied)); } } - @Parameters(commandDescription = "Remove max unacked messages policy on subscription for a topic") + @Command(description = "Remove max unacked messages policy on subscription for a topic", hidden = true) private class RemoveMaxUnackedMessagesOnSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxUnackedMessagesOnSubscription(persistentTopic); } } - @Parameters(commandDescription = "Set max unacked messages policy on subscription for a topic") + @Command(description = "Set max unacked messages policy on subscription for a topic", hidden = true) private class SetMaxUnackedMessagesOnSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-m", "--maxNum"}, + @Option(names = {"-m", "--maxNum"}, description = "max unacked messages num on subscription", required = true) private int maxNum; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxUnackedMessagesOnSubscription(persistentTopic, maxNum); } } - @Parameters(commandDescription = "Set subscription types enabled for a topic") + @Command(description = "Set subscription types enabled for a topic", hidden = true) private class SetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." - + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true) + @Option(names = {"--types", "-t"}, description = "Subscription types enabled list (comma separated values)." + + " Possible values: (Exclusive, Shared, Failover, Key_Shared).", required = true, split = ",") private List subTypes; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); Set types = new HashSet<>(); subTypes.forEach(s -> { SubscriptionType subType; @@ -2465,51 +2330,51 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get subscription types enabled for a topic") + @Command(description = "Get subscription types enabled for a topic", hidden = true) private class GetSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getSubscriptionTypesEnabled(persistentTopic)); } } - @Parameters(commandDescription = "Remove subscription types enabled for a topic") + @Command(description = "Remove subscription types enabled for a topic", hidden = true) private class RemoveSubscriptionTypesEnabled extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeSubscriptionTypesEnabled(persistentTopic); } } - @Parameters(commandDescription = "Get compaction threshold for a topic") + @Command(description = "Get compaction threshold for a topic", hidden = true) private class GetCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getCompactionThreshold(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set compaction threshold for a topic") + @Command(description = "Set compaction threshold for a topic", hidden = true) private class SetCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--threshold", "-t" }, + @Option(names = { "--threshold", "-t" }, description = "Maximum number of bytes in a topic backlog before compaction is triggered " + "(eg: 10M, 16G, 3T). 0 disables automatic compaction", required = true, @@ -2518,109 +2383,109 @@ private class SetCompactionThreshold extends CliCommand { @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setCompactionThreshold(persistentTopic, threshold); } } - @Parameters(commandDescription = "Remove compaction threshold for a topic") + @Command(description = "Remove compaction threshold for a topic", hidden = true) private class RemoveCompactionThreshold extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeCompactionThreshold(persistentTopic); } } - @Parameters(commandDescription = "Get publish rate for a topic") + @Command(description = "Get publish rate for a topic", hidden = true) private class GetPublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getPublishRate(persistentTopic)); } } - @Parameters(commandDescription = "Set publish rate for a topic") + @Command(description = "Set publish rate for a topic", hidden = true) private class SetPublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-publish-rate", + @Option(names = { "--msg-publish-rate", "-m" }, description = "message-publish-rate (default -1 will be overwrite if not passed)", required = false) private int msgPublishRate = -1; - @Parameter(names = { "--byte-publish-rate", + @Option(names = { "--byte-publish-rate", "-b" }, description = "byte-publish-rate (default -1 will be overwrite if not passed)", required = false) private long bytePublishRate = -1; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setPublishRate(persistentTopic, new PublishRate(msgPublishRate, bytePublishRate)); } } - @Parameters(commandDescription = "Remove publish rate for a topic") + @Command(description = "Remove publish rate for a topic", hidden = true) private class RemovePublishRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removePublishRate(persistentTopic); } } - @Parameters(commandDescription = "Get subscription message-dispatch-rate for a topic") + @Command(description = "Get subscription message-dispatch-rate for a topic", hidden = true) private class GetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getSubscriptionDispatchRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set subscription message-dispatch-rate for a topic") + @Command(description = "Set subscription message-dispatch-rate for a topic", hidden = true) private class SetSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate (default -1 will be overwrite if not passed)") private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type" + " (default 1 second will be overwrite if not passed)") private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))") private boolean relativeToPublishRate = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setSubscriptionDispatchRate(persistentTopic, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -2631,59 +2496,59 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove subscription message-dispatch-rate for a topic") + @Command(description = "Remove subscription message-dispatch-rate for a topic", hidden = true) private class RemoveSubscriptionDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeSubscriptionDispatchRate(persistentTopic); } } - @Parameters(commandDescription = "Get replicator message-dispatch-rate for a topic") + @Command(description = "Get replicator message-dispatch-rate for a topic", hidden = true) private class GetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") + @Option(names = {"-ap", "--applied"}, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String topic = validatePersistentTopic(params); + String topic = validatePersistentTopic(topicName); print(getTopics().getReplicatorDispatchRate(topic, applied)); } } - @Parameters(commandDescription = "Set replicator message-dispatch-rate for a topic") + @Command(description = "Set replicator message-dispatch-rate for a topic", hidden = true) private class SetReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--msg-dispatch-rate", + @Option(names = { "--msg-dispatch-rate", "-md" }, description = "message-dispatch-rate (default -1 will be overwrite if not passed)") private int msgDispatchRate = -1; - @Parameter(names = { "--byte-dispatch-rate", + @Option(names = { "--byte-dispatch-rate", "-bd" }, description = "byte-dispatch-rate (default -1 will be overwrite if not passed)", required = false) private long byteDispatchRate = -1; - @Parameter(names = { "--dispatch-rate-period", + @Option(names = { "--dispatch-rate-period", "-dt" }, description = "dispatch-rate-period in second type " + "(default 1 second will be overwrite if not passed)") private int dispatchRatePeriodSec = 1; - @Parameter(names = { "--relative-to-publish-rate", + @Option(names = { "--relative-to-publish-rate", "-rp" }, description = "dispatch rate relative to publish-rate (if publish-relative flag is enabled " + "then broker will apply throttling value to (publish-rate + dispatch rate))") private boolean relativeToPublishRate = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setReplicatorDispatchRate(persistentTopic, DispatchRate.builder() .dispatchThrottlingRateInMsg(msgDispatchRate) @@ -2694,220 +2559,220 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove replicator message-dispatch-rate for a topic") + @Command(description = "Remove replicator message-dispatch-rate for a topic", hidden = true) private class RemoveReplicatorDispatchRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeReplicatorDispatchRate(persistentTopic); } } - @Parameters(commandDescription = "Get max number of producers for a topic") + @Command(description = "Get max number of producers for a topic", hidden = true) private class GetMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxProducers(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set max number of producers for a topic") + @Command(description = "Set max number of producers for a topic", hidden = true) private class SetMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-producers", "-p"}, description = "Max producers for a topic", required = true) + @Option(names = {"--max-producers", "-p"}, description = "Max producers for a topic", required = true) private int maxProducers; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxProducers(persistentTopic, maxProducers); } } - @Parameters(commandDescription = "Remove max number of producers for a topic") + @Command(description = "Remove max number of producers for a topic", hidden = true) private class RemoveMaxProducers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxProducers(persistentTopic); } } - @Parameters(commandDescription = "Get max number of subscriptions for a topic") + @Command(description = "Get max number of subscriptions for a topic", hidden = true) private class GetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxSubscriptionsPerTopic(persistentTopic)); } } - @Parameters(commandDescription = "Set max number of subscriptions for a topic") + @Command(description = "Set max number of subscriptions for a topic", hidden = true) private class SetMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-subscriptions-per-topic", "-m"}, + @Option(names = {"--max-subscriptions-per-topic", "-m"}, description = "Maximum subscription limit for a topic", required = true) private int maxSubscriptionsPerTopic; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxSubscriptionsPerTopic(persistentTopic, maxSubscriptionsPerTopic); } } - @Parameters(commandDescription = "Remove max number of subscriptions for a topic") + @Command(description = "Remove max number of subscriptions for a topic", hidden = true) private class RemoveMaxSubscriptionsPerTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxSubscriptionsPerTopic(persistentTopic); } } - @Parameters(commandDescription = "Get max message size for a topic") + @Command(description = "Get max message size for a topic", hidden = true) private class GetMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxMessageSize(persistentTopic)); } } - @Parameters(commandDescription = "Set max message size for a topic") + @Command(description = "Set max message size for a topic", hidden = true) private class SetMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"--max-message-size", "-m"}, description = "Max message size for a topic", required = true) + @Option(names = {"--max-message-size", "-m"}, description = "Max message size for a topic", required = true) private int maxMessageSize; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxMessageSize(persistentTopic, maxMessageSize); } } - @Parameters(commandDescription = "Remove max message size for a topic") + @Command(description = "Remove max message size for a topic", hidden = true) private class RemoveMaxMessageSize extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxMessageSize(persistentTopic); } } - @Parameters(commandDescription = "Get max consumers per subscription for a topic") + @Command(description = "Get max consumers per subscription for a topic", hidden = true) private class GetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxConsumersPerSubscription(persistentTopic)); } } - @Parameters(commandDescription = "Set max consumers per subscription for a topic") + @Command(description = "Set max consumers per subscription for a topic", hidden = true) private class SetMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--max-consumers-per-subscription", "-c" }, + @Option(names = { "--max-consumers-per-subscription", "-c" }, description = "maxConsumersPerSubscription for a namespace", required = true) private int maxConsumersPerSubscription; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxConsumersPerSubscription(persistentTopic, maxConsumersPerSubscription); } } - @Parameters(commandDescription = "Remove max consumers per subscription for a topic") + @Command(description = "Remove max consumers per subscription for a topic", hidden = true) private class RemoveMaxConsumersPerSubscription extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxConsumersPerSubscription(persistentTopic); } } - @Parameters(commandDescription = "Get the inactive topic policies on a topic") + @Command(description = "Get the inactive topic policies on a topic", hidden = true) private class GetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getInactiveTopicPolicies(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set the inactive topic policies on a topic") + @Command(description = "Set the inactive topic policies on a topic", hidden = true) private class SetInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") + @Option(names = { "--enable-delete-while-inactive", "-e" }, description = "Enable delete while inactive") private boolean enableDeleteWhileInactive = false; - @Parameter(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") + @Option(names = { "--disable-delete-while-inactive", "-d" }, description = "Disable delete while inactive") private boolean disableDeleteWhileInactive = false; - @Parameter(names = {"--max-inactive-duration", "-t"}, description = "Max duration of topic inactivity " + @Option(names = {"--max-inactive-duration", "-t"}, description = "Max duration of topic inactivity " + "in seconds, topics that are inactive for longer than this value will be deleted " + "(eg: 1s, 10s, 1m, 5h, 3d)", required = true, converter = TimeUnitToSecondsConverter.class) private Long maxInactiveDurationInSeconds; - @Parameter(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + @Option(names = { "--delete-mode", "-m" }, description = "Mode of delete inactive topic, Valid options are: " + "[delete_when_no_subscriptions, delete_when_subscriptions_caught_up]", required = true) private String inactiveTopicDeleteMode; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (enableDeleteWhileInactive == disableDeleteWhileInactive) { - throw new ParameterException("Need to specify either enable-delete-while-inactive " + throw new IllegalArgumentException("Need to specify either enable-delete-while-inactive " + "or disable-delete-while-inactive"); } InactiveTopicDeleteMode deleteMode = null; @@ -2922,146 +2787,146 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Remove inactive topic policies from a topic") + @Command(description = "Remove inactive topic policies from a topic", hidden = true) private class RemoveInactiveTopicPolicies extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeInactiveTopicPolicies(persistentTopic); } } - @Parameters(commandDescription = "Get max number of consumers for a topic") + @Command(description = "Get max number of consumers for a topic", hidden = true) private class GetMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getMaxConsumers(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set max number of consumers for a topic") + @Command(description = "Set max number of consumers for a topic", hidden = true) private class SetMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--max-consumers", "-c" }, description = "Max consumers for a topic", required = true) + @Option(names = { "--max-consumers", "-c" }, description = "Max consumers for a topic", required = true) private int maxConsumers; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setMaxConsumers(persistentTopic, maxConsumers); } } - @Parameters(commandDescription = "Remove max number of consumers for a topic") + @Command(description = "Remove max number of consumers for a topic", hidden = true) private class RemoveMaxConsumers extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeMaxConsumers(persistentTopic); } } - @Parameters(commandDescription = "Get consumer subscribe rate for a topic") + @Command(description = "Get consumer subscribe rate for a topic", hidden = true) private class GetSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getSubscribeRate(persistentTopic, applied)); } } - @Parameters(commandDescription = "Set consumer subscribe rate for a topic") + @Command(description = "Set consumer subscribe rate for a topic", hidden = true) private class SetSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--subscribe-rate", + @Option(names = { "--subscribe-rate", "-sr" }, description = "subscribe-rate (default -1 will be overwrite if not passed)", required = false) private int subscribeRate = -1; - @Parameter(names = { "--subscribe-rate-period", + @Option(names = { "--subscribe-rate-period", "-st" }, description = "subscribe-rate-period in second type " + "(default 30 second will be overwrite if not passed)") private int subscribeRatePeriodSec = 30; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().setSubscribeRate(persistentTopic, new SubscribeRate(subscribeRate, subscribeRatePeriodSec)); } } - @Parameters(commandDescription = "Remove consumer subscribe rate for a topic") + @Command(description = "Remove consumer subscribe rate for a topic", hidden = true) private class RemoveSubscribeRate extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); getTopics().removeSubscribeRate(persistentTopic); } } - @Parameters(commandDescription = "Enable or disable a replicated subscription on a topic") + @Command(description = "Enable or disable a replicated subscription on a topic") private class SetReplicatedSubscriptionStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", + @Option(names = { "-s", "--subscription" }, description = "Subscription name to enable or disable replication", required = true) private String subName; - @Parameter(names = { "--enable", "-e" }, description = "Enable replication") + @Option(names = { "--enable", "-e" }, description = "Enable replication") private boolean enable = false; - @Parameter(names = { "--disable", "-d" }, description = "Disable replication") + @Option(names = { "--disable", "-d" }, description = "Disable replication") private boolean disable = false; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); if (enable == disable) { - throw new ParameterException("Need to specify either --enable or --disable"); + throw new IllegalArgumentException("Need to specify either --enable or --disable"); } getTopics().setReplicatedSubscriptionStatus(persistentTopic, subName, enable); } } - @Parameters(commandDescription = "Get replicated subscription status on a topic") + @Command(description = "Get replicated subscription status on a topic") private class GetReplicatedSubscriptionStatus extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = {"-s", + @Option(names = {"-s", "--subscription"}, description = "Subscription name", required = true) private String subName; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); print(getTopics().getReplicatedSubscriptionStatus(persistentTopic, subName)); } } @@ -3070,18 +2935,18 @@ private Topics getTopics() { return getAdmin().topics(); } - @Parameters(commandDescription = "Calculate backlog size by a message ID (in bytes).") + @Command(description = "Calculate backlog size by a message ID (in bytes).") private class GetBacklogSizeByMessageId extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--messageId", + @Option(names = { "--messageId", "-m" }, description = "messageId used to calculate backlog size. It can be (ledgerId:entryId).") private String messagePosition = "-1:-1"; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); MessageId messageId; if ("-1:-1".equals(messagePosition)) { messageId = MessageId.earliest; @@ -3094,21 +2959,21 @@ void run() throws PulsarAdminException { } - @Parameters(commandDescription = "Analyze the backlog of a subscription.") + @Command(description = "Analyze the backlog of a subscription.") private class AnalyzeBacklog extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be analyzed", required = true) + @Option(names = { "-s", "--subscription" }, description = "Subscription to be analyzed", required = true) private String subName; - @Parameter(names = { "--position", + @Option(names = { "--position", "-p" }, description = "message position to start the scan from (ledgerId:entryId)", required = false) private String messagePosition; @Override void run() throws PulsarAdminException { - String persistentTopic = validatePersistentTopic(params); + String persistentTopic = validatePersistentTopic(topicName); Optional startPosition = Optional.empty(); if (isNotBlank(messagePosition)) { int partitionIndex = TopicName.get(persistentTopic).getPartitionIndex(); @@ -3120,43 +2985,44 @@ void run() throws PulsarAdminException { } } - @Parameters(commandDescription = "Get the schema validation enforced") + @Command(description = "Get the schema validation enforced") private class GetSchemaValidationEnforced extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") + @Option(names = { "-ap", "--applied" }, description = "Get the applied policy of the topic") private boolean applied = false; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); System.out.println(getAdmin().topics().getSchemaValidationEnforced(topic, applied)); } } - @Parameters(commandDescription = "Set the schema whether open schema validation enforced") + @Command(description = "Set the schema whether open schema validation enforced") private class SetSchemaValidationEnforced extends CliCommand { - @Parameter(description = "tenant/namespace", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; - @Parameter(names = { "--enable", "-e" }, description = "Enable schema validation enforced") + @Option(names = { "--enable", "-e" }, description = "Enable schema validation enforced") private boolean enable = false; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getAdmin().topics().setSchemaValidationEnforced(topic, enable); } } - @Parameters(commandDescription = "Trim a topic") + + @Command(description = "Trim a topic") private class TrimTopic extends CliCommand { - @Parameter(description = "persistent://tenant/namespace/topic", required = true) - private java.util.List params; + @Parameters(description = "persistent://tenant/namespace/topic", arity = "1") + private String topicName; @Override void run() throws PulsarAdminException { - String topic = validateTopicName(params); + String topic = validateTopicName(topicName); getAdmin().topics().trimTopic(topic); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTransactions.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTransactions.java index 41aea611ea84d..63c729263cbe6 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTransactions.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTransactions.java @@ -18,22 +18,22 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.apache.pulsar.cli.converters.TimeUnitToMillisConverter; +import org.apache.pulsar.cli.converters.picocli.TimeUnitToMillisConverter; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.api.transaction.TxnID; import org.apache.pulsar.common.policies.data.TransactionCoordinatorInfo; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -@Parameters(commandDescription = "Operations on transactions") +@Command(description = "Operations on transactions") public class CmdTransactions extends CmdBase { - @Parameters(commandDescription = "Get transaction coordinator stats") + @Command(description = "Get transaction coordinator stats") private class GetCoordinatorStats extends CliCommand { - @Parameter(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = false) + @Option(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = false) private Integer coordinatorId; @Override @@ -46,16 +46,16 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction buffer stats") + @Command(description = "Get transaction buffer stats") private class GetTransactionBufferStats extends CliCommand { - @Parameter(names = {"-t", "--topic"}, description = "The topic", required = true) + @Option(names = {"-t", "--topic"}, description = "The topic", required = true) private String topic; - @Parameter(names = {"-l", "--low-water-mark"}, + @Option(names = {"-l", "--low-water-mark"}, description = "Whether to get information about lowWaterMarks stored in transaction buffer.") private boolean lowWaterMark; - @Parameter(names = {"-s", "--segment-stats"}, + @Option(names = {"-s", "--segment-stats"}, description = "Whether to get segment statistics.") private boolean segmentStats = false; @@ -66,15 +66,15 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction pending ack stats") + @Command(description = "Get transaction pending ack stats") private class GetPendingAckStats extends CliCommand { - @Parameter(names = {"-t", "--topic"}, description = "The topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "The topic name", required = true) private String topic; - @Parameter(names = {"-s", "--sub-name"}, description = "The subscription name", required = true) + @Option(names = {"-s", "--sub-name"}, description = "The subscription name", required = true) private String subName; - @Parameter(names = {"-l", "--low-water-mark"}, + @Option(names = {"-l", "--low-water-mark"}, description = "Whether to get information about lowWaterMarks stored in transaction pending ack.") private boolean lowWaterMarks; @@ -84,18 +84,18 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction in pending ack stats") + @Command(description = "Get transaction in pending ack stats") private class GetTransactionInPendingAckStats extends CliCommand { - @Parameter(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) + @Option(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) private int mostSigBits; - @Parameter(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) + @Option(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) private long leastSigBits; - @Parameter(names = {"-t", "--topic"}, description = "The topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "The topic name", required = true) private String topic; - @Parameter(names = {"-s", "--sub-name"}, description = "The subscription name", required = true) + @Option(names = {"-s", "--sub-name"}, description = "The subscription name", required = true) private String subName; @Override @@ -106,15 +106,15 @@ void run() throws Exception { } - @Parameters(commandDescription = "Get transaction in buffer stats") + @Command(description = "Get transaction in buffer stats") private class GetTransactionInBufferStats extends CliCommand { - @Parameter(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) + @Option(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) private int mostSigBits; - @Parameter(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) + @Option(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) private long leastSigBits; - @Parameter(names = {"-t", "--topic"}, description = "The topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "The topic name", required = true) private String topic; @Override @@ -123,12 +123,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction metadata") + @Command(description = "Get transaction metadata") private class GetTransactionMetadata extends CliCommand { - @Parameter(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) + @Option(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) private int mostSigBits; - @Parameter(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) + @Option(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) private long leastSigBits; @Override @@ -137,12 +137,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get slow transactions.") + @Command(description = "Get slow transactions.") private class GetSlowTransactions extends CliCommand { - @Parameter(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = false) + @Option(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = false) private Integer coordinatorId; - @Parameter(names = { "-t", "--time" }, description = "The transaction timeout time. " + @Option(names = { "-t", "--time" }, description = "The transaction timeout time. " + "(eg: 1s, 10s, 1m, 5h, 3d)", required = true, converter = TimeUnitToMillisConverter.class) private Long timeoutInMillis = 1L; @@ -158,12 +158,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction coordinator internal stats") + @Command(description = "Get transaction coordinator internal stats") private class GetCoordinatorInternalStats extends CliCommand { - @Parameter(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = true) + @Option(names = {"-c", "--coordinator-id"}, description = "The coordinator id", required = true) private int coordinatorId; - @Parameter(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") + @Option(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") private boolean metadata = false; @Override void run() throws Exception { @@ -171,15 +171,15 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get pending ack internal stats") + @Command(description = "Get pending ack internal stats") private class GetPendingAckInternalStats extends CliCommand { - @Parameter(names = {"-t", "--topic"}, description = "Topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "Topic name", required = true) private String topic; - @Parameter(names = {"-s", "--subscription-name"}, description = "Subscription name", required = true) + @Option(names = {"-s", "--subscription-name"}, description = "Subscription name", required = true) private String subName; - @Parameter(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") + @Option(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") private boolean metadata = false; @Override void run() throws Exception { @@ -187,12 +187,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get transaction buffer internal stats") + @Command(description = "Get transaction buffer internal stats") private class GetTransactionBufferInternalStats extends CliCommand { - @Parameter(names = {"-t", "--topic"}, description = "Topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "Topic name", required = true) private String topic; - @Parameter(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") + @Option(names = { "-m", "--metadata" }, description = "Flag to include ledger metadata") private boolean metadata = false; @Override @@ -201,9 +201,9 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Update the scale of transaction coordinators") + @Command(description = "Update the scale of transaction coordinators") private class ScaleTransactionCoordinators extends CliCommand { - @Parameter(names = { "-r", "--replicas" }, description = "The scale of the transaction coordinators") + @Option(names = { "-r", "--replicas" }, description = "The scale of the transaction coordinators") private int replicas; @Override void run() throws Exception { @@ -211,21 +211,21 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Get the position stats in transaction pending ack") + @Command(description = "Get the position stats in transaction pending ack") private class GetPositionStatsInPendingAck extends CliCommand { - @Parameter(names = {"-t", "--topic"}, description = "The topic name", required = true) + @Option(names = {"-t", "--topic"}, description = "The topic name", required = true) private String topic; - @Parameter(names = {"-s", "--subscription-name"}, description = "Subscription name", required = true) + @Option(names = {"-s", "--subscription-name"}, description = "Subscription name", required = true) private String subName; - @Parameter(names = {"-l", "--ledger-id"}, description = "Ledger ID of the position", required = true) + @Option(names = {"-l", "--ledger-id"}, description = "Ledger ID of the position", required = true) private Long ledgerId; - @Parameter(names = {"-e", "--entry-id"}, description = "Entry ID of the position", required = true) + @Option(names = {"-e", "--entry-id"}, description = "Entry ID of the position", required = true) private Long entryId; - @Parameter(names = {"-b", "--batch-index"}, description = "Batch index of the position") + @Option(names = {"-b", "--batch-index"}, description = "Batch index of the position") private Integer batchIndex; @Override @@ -234,7 +234,7 @@ void run() throws Exception { } } - @Parameters(commandDescription = "List transaction coordinators") + @Command(description = "List transaction coordinators") private class ListTransactionCoordinators extends CliCommand { @Override void run() throws Exception { @@ -250,12 +250,12 @@ void run() throws Exception { } } - @Parameters(commandDescription = "Abort transaction") + @Command(description = "Abort transaction") private class AbortTransaction extends CliCommand { - @Parameter(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) + @Option(names = {"-m", "--most-sig-bits"}, description = "The most sig bits", required = true) private long mostSigBits; - @Parameter(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) + @Option(names = {"-l", "--least-sig-bits"}, description = "The least sig bits", required = true) private long leastSigBits; @Override @@ -266,20 +266,20 @@ void run() throws Exception { public CmdTransactions(Supplier admin) { super("transactions", admin); - jcommander.addCommand("coordinator-internal-stats", new GetCoordinatorInternalStats()); - jcommander.addCommand("pending-ack-internal-stats", new GetPendingAckInternalStats()); - jcommander.addCommand("buffer-snapshot-internal-stats", new GetTransactionBufferInternalStats()); - jcommander.addCommand("coordinator-stats", new GetCoordinatorStats()); - jcommander.addCommand("transaction-buffer-stats", new GetTransactionBufferStats()); - jcommander.addCommand("pending-ack-stats", new GetPendingAckStats()); - jcommander.addCommand("transaction-in-buffer-stats", new GetTransactionInBufferStats()); - jcommander.addCommand("transaction-in-pending-ack-stats", new GetTransactionInPendingAckStats()); - jcommander.addCommand("transaction-metadata", new GetTransactionMetadata()); - jcommander.addCommand("slow-transactions", new GetSlowTransactions()); - jcommander.addCommand("scale-transactionCoordinators", new ScaleTransactionCoordinators()); - jcommander.addCommand("position-stats-in-pending-ack", new GetPositionStatsInPendingAck()); - jcommander.addCommand("coordinators-list", new ListTransactionCoordinators()); - jcommander.addCommand("abort-transaction", new AbortTransaction()); + addCommand("coordinator-internal-stats", new GetCoordinatorInternalStats()); + addCommand("pending-ack-internal-stats", new GetPendingAckInternalStats()); + addCommand("buffer-snapshot-internal-stats", new GetTransactionBufferInternalStats()); + addCommand("coordinator-stats", new GetCoordinatorStats()); + addCommand("transaction-buffer-stats", new GetTransactionBufferStats()); + addCommand("pending-ack-stats", new GetPendingAckStats()); + addCommand("transaction-in-buffer-stats", new GetTransactionInBufferStats()); + addCommand("transaction-in-pending-ack-stats", new GetTransactionInPendingAckStats()); + addCommand("transaction-metadata", new GetTransactionMetadata()); + addCommand("slow-transactions", new GetSlowTransactions()); + addCommand("scale-transactionCoordinators", new ScaleTransactionCoordinators()); + addCommand("position-stats-in-pending-ack", new GetPositionStatsInPendingAck()); + addCommand("coordinators-list", new ListTransactionCoordinators()); + addCommand("abort-transaction", new AbortTransaction()); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdUsageFormatter.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdUsageFormatter.java deleted file mode 100644 index a1771a07cc96b..0000000000000 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdUsageFormatter.java +++ /dev/null @@ -1,88 +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.admin.cli; - -import com.beust.jcommander.DefaultUsageFormatter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameters; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class CmdUsageFormatter extends DefaultUsageFormatter { - - /** - * The commands in this set are hidden and not shown to users. - */ - private Set deprecatedCommands = new HashSet<>(); - - private final JCommander commander; - - public CmdUsageFormatter(JCommander commander) { - super(commander); - this.commander = commander; - } - - /** - * This method is copied from DefaultUsageFormatter, - * but the ability to skip deprecated commands is added. - * @param out - * @param indentCount - * @param descriptionIndent - * @param indent - */ - @Override - public void appendCommands(StringBuilder out, int indentCount, int descriptionIndent, String indent) { - out.append(indent + " Commands:\n"); - - for (Map.Entry commands : commander.getRawCommands().entrySet()) { - Object arg = commands.getValue().getObjects().get(0); - Parameters p = arg.getClass().getAnnotation(Parameters.class); - - if (p == null || !p.hidden()) { - JCommander.ProgramName progName = commands.getKey(); - String dispName = progName.getDisplayName(); - //skip the deprecated command - if (deprecatedCommands.contains(dispName)) { - continue; - } - String description = indent + s(4) + dispName + s(6) + getCommandDescription(progName.getName()); - wrapDescription(out, indentCount + descriptionIndent, description); - out.append("\n"); - - JCommander jc = commander.findCommandByAlias(progName.getName()); - jc.getUsageFormatter().usage(out, indent + s(6)); - out.append("\n"); - } - } - } - - public void addDeprecatedCommand(String command) { - this.deprecatedCommands.add(command); - } - - public void removeDeprecatedCommand(String command) { - this.deprecatedCommands.remove(command); - } - - public void clearDeprecatedCommand(){ - this.deprecatedCommands.clear(); - } - -} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CustomCommandsUtils.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CustomCommandsUtils.java index e1968d0349ad2..a49a8c450fa2c 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CustomCommandsUtils.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CustomCommandsUtils.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; @@ -37,7 +35,6 @@ import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.ArrayMemberValue; import javassist.bytecode.annotation.BooleanMemberValue; -import javassist.bytecode.annotation.IntegerMemberValue; import javassist.bytecode.annotation.MemberValue; import javassist.bytecode.annotation.StringMemberValue; import lombok.Setter; @@ -47,6 +44,9 @@ import org.apache.pulsar.admin.cli.extensions.ParameterDescriptor; import org.apache.pulsar.admin.cli.extensions.ParameterType; import org.apache.pulsar.client.admin.PulsarAdmin; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; public final class CustomCommandsUtils { private CustomCommandsUtils() { @@ -68,8 +68,12 @@ public static Object generateCliCommand(CustomCommandGroup group, CommandExecuti ConstPool constpool = classFile.getConstPool(); AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag); - Annotation annotation = new Annotation(Parameters.class.getName(), constpool); - annotation.addMemberValue("commandDescription", new StringMemberValue(description, + Annotation annotation = new Annotation(Command.class.getName(), constpool); + ArrayMemberValue descArrayMemberValue = new ArrayMemberValue(classFile.getConstPool()); + descArrayMemberValue.setValue( + new MemberValue[]{new StringMemberValue(description, classFile.getConstPool())}); + annotation.addMemberValue("description", descArrayMemberValue); + annotation.addMemberValue("name", new StringMemberValue(group.name(), classFile.getConstPool())); annotationsAttribute.setAnnotation(annotation); ctClass.getClassFile().addAttribute(annotationsAttribute); @@ -102,7 +106,7 @@ public CmdBaseAdapter(String cmdName, Supplier adminSupplier, DecoratedCommand commandImpl = generateCustomCommand(cmdName, name, command); commandImpl.setCommand(command); commandImpl.setContext(context); - jcommander.addCommand(name, commandImpl); + addCommand(name, commandImpl); } } } @@ -142,9 +146,12 @@ private static DecoratedCommand generateCustomCommand(String group, String name, AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag); - Annotation annotation = new Annotation(Parameters.class.getName(), constpool); - annotation.addMemberValue("commandDescription", - new StringMemberValue(description, classFile.getConstPool())); + Annotation annotation = new Annotation(Command.class.getName(), constpool); + ArrayMemberValue descArrayMemberValue = new ArrayMemberValue(classFile.getConstPool()); + descArrayMemberValue.setValue( + new MemberValue[]{new StringMemberValue(description, classFile.getConstPool())}); + annotation.addMemberValue("description", descArrayMemberValue); + annotation.addMemberValue("name", new StringMemberValue(name, classFile.getConstPool())); annotationsAttribute.setAnnotation(annotation); ctClass.getClassFile().addAttribute(annotationsAttribute); @@ -183,11 +190,9 @@ private static DecoratedCommand generateCustomCommand(String group, String name, AnnotationsAttribute fieldAnnotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag); - Annotation fieldAnnotation = new Annotation(Parameter.class.getName(), constpool); - - // in JCommander if you don't set the "names" property then you want to get all the other - // parameters + Annotation fieldAnnotation; if (!parameterDescriptor.isMainParameter()) { + fieldAnnotation = new Annotation(Option.class.getName(), constpool); MemberValue[] memberValues = new MemberValue[parameterNames.size()]; int i = 0; for (String parameterName : parameterNames) { @@ -196,16 +201,23 @@ private static DecoratedCommand generateCustomCommand(String group, String name, ArrayMemberValue arrayMemberValue = new ArrayMemberValue(classFile.getConstPool()); arrayMemberValue.setValue(memberValues); fieldAnnotation.addMemberValue("names", arrayMemberValue); - } - - fieldAnnotation.addMemberValue("description", - new StringMemberValue(parameterDescriptor.getDescription(), classFile.getConstPool())); - fieldAnnotation.addMemberValue("required", - new BooleanMemberValue(parameterDescriptor.isRequired(), classFile.getConstPool())); - if (parameterDescriptor.getType() == ParameterType.BOOLEAN) { + fieldAnnotation.addMemberValue("required", + new BooleanMemberValue(parameterDescriptor.isRequired(), classFile.getConstPool())); + if (parameterDescriptor.getType() == ParameterType.BOOLEAN) { + fieldAnnotation.addMemberValue("arity", + new StringMemberValue("1", classFile.getConstPool())); + } + } else { + fieldAnnotation = new Annotation(Parameters.class.getName(), constpool); + String arityValue = parameterDescriptor.isRequired() ? "1" : "0..1"; fieldAnnotation.addMemberValue("arity", - new IntegerMemberValue(classFile.getConstPool(), 1)); + new StringMemberValue(arityValue, classFile.getConstPool())); } + ArrayMemberValue optionDescArrayMemberValue = new ArrayMemberValue(classFile.getConstPool()); + optionDescArrayMemberValue.setValue( + new MemberValue[]{ + new StringMemberValue(parameterDescriptor.getDescription(), classFile.getConstPool())}); + fieldAnnotation.addMemberValue("description", optionDescArrayMemberValue); fieldAnnotationsAttribute.setAnnotation(fieldAnnotation); field.getFieldInfo().addAttribute(fieldAnnotationsAttribute); field.setModifiers(Modifier.PUBLIC); diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminPropertiesProvider.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminPropertiesProvider.java new file mode 100644 index 0000000000000..85d350ce99b7d --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminPropertiesProvider.java @@ -0,0 +1,49 @@ +/* + * 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.admin.cli; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.util.Properties; +import picocli.CommandLine.PropertiesDefaultProvider; + +class PulsarAdminPropertiesProvider extends PropertiesDefaultProvider { + private static final String webServiceUrlKey = "webServiceUrl"; + private final Properties properties; + + private PulsarAdminPropertiesProvider(Properties properties) { + super(properties); + this.properties = properties; + } + + static PulsarAdminPropertiesProvider create(Properties properties) { + Properties clone = (Properties) properties.clone(); + if (isBlank(properties.getProperty(webServiceUrlKey))) { + String serviceUrl = properties.getProperty("serviceUrl"); + if (isNotBlank(serviceUrl)) { + properties.put(webServiceUrlKey, serviceUrl); + } + } + return new PulsarAdminPropertiesProvider(clone); + } + + String getAdminUrl() { + return properties.getProperty(webServiceUrlKey); + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminSupplier.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminSupplier.java index 764dc9de5dfdd..3417f2bb2c618 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminSupplier.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminSupplier.java @@ -19,6 +19,7 @@ package org.apache.pulsar.admin.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import com.google.common.annotations.VisibleForTesting; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import lombok.Data; @@ -53,7 +54,7 @@ static RootParamsKey fromRootParams(PulsarAdminTool.RootParams params) { } } - protected final PulsarAdminBuilder adminBuilder; + protected PulsarAdminBuilder adminBuilder; private RootParamsKey currentParamsKey; private PulsarAdmin admin; @@ -108,4 +109,8 @@ private static void applyRootParamsToAdminBuilder(PulsarAdminBuilder adminBuilde } } + @VisibleForTesting + public void setAdminBuilder(PulsarAdminBuilder builder) { + this.adminBuilder = builder; + } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java index e25520dceb9f8..2cc74d2f13bac 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java @@ -19,11 +19,9 @@ package org.apache.pulsar.admin.cli; 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.google.common.annotations.VisibleForTesting; import java.io.FileInputStream; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.HashMap; @@ -32,7 +30,6 @@ import java.util.Properties; import java.util.function.Supplier; import lombok.Getter; -import org.apache.pulsar.PulsarVersion; import org.apache.pulsar.admin.cli.extensions.CommandExecutionContext; import org.apache.pulsar.admin.cli.extensions.CustomCommandFactory; import org.apache.pulsar.admin.cli.extensions.CustomCommandGroup; @@ -41,75 +38,80 @@ import org.apache.pulsar.client.admin.PulsarAdminBuilder; import org.apache.pulsar.client.admin.internal.PulsarAdminImpl; import org.apache.pulsar.common.util.ShutdownUtil; - -public class PulsarAdminTool { +import org.apache.pulsar.internal.CommandHook; +import org.apache.pulsar.internal.CommanderFactory; +import picocli.CommandLine; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; + +@Command(name = "pulsar-admin", + scope = ScopeType.INHERIT, + mixinStandardHelpOptions = true, + showDefaultValues = true, + versionProvider = PulsarVersionProvider.class +) +public class PulsarAdminTool implements CommandHook { protected static boolean allowSystemExit = true; private static int lastExitCode = Integer.MIN_VALUE; - protected final List customCommandFactories; + protected List customCommandFactories; protected Map> commandMap; - protected JCommander jcommander; - protected RootParams rootParams; - private final Properties properties; + protected final CommandLine commander; + @ArgGroup(heading = "Options:%n", exclusive = false) + protected RootParams rootParams = new RootParams(); protected PulsarAdminSupplier pulsarAdminSupplier; + private PulsarAdminPropertiesProvider pulsarAdminPropertiesProvider; @Getter public static class RootParams { - @Parameter(names = { "--admin-url" }, description = "Admin Service URL to which to connect.") + @Option(names = { "--admin-url" }, description = "Admin Service URL to which to connect.", + descriptionKey = "webServiceUrl") String serviceUrl = null; - @Parameter(names = { "--auth-plugin" }, description = "Authentication plugin class name.") + @Option(names = { "--auth-plugin" }, description = "Authentication plugin class name.", + descriptionKey = "authPlugin") String authPluginClassName = null; - @Parameter(names = { "--request-timeout" }, description = "Request time out in seconds for " + @Option(names = { "--request-timeout" }, description = "Request time out in seconds for " + "the pulsar admin client for any request") int requestTimeout = PulsarAdminImpl.DEFAULT_REQUEST_TIMEOUT_SECONDS; - @Parameter( - names = { "--auth-params" }, + @Option(names = { "--auth-params" }, descriptionKey = "authParams", 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\"}\".") String authParams = null; - @Parameter(names = { "--tls-allow-insecure" }, description = "Allow TLS insecure connection") + @Option(names = { "--tls-allow-insecure" }, description = "Allow TLS insecure connection") Boolean tlsAllowInsecureConnection; - @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS trust cert file path") + @Option(names = { "--tls-trust-cert-path" }, description = "Allow TLS trust cert file path") String tlsTrustCertsFilePath; - @Parameter(names = { "--tls-enable-hostname-verification" }, + @Option(names = { "--tls-enable-hostname-verification" }, description = "Enable TLS common name verification") Boolean tlsEnableHostnameVerification; - @Parameter(names = {"--tls-provider"}, description = "Set up TLS provider. " + @Option(names = {"--tls-provider"}, description = "Set up TLS provider. " + "When TLS authentication with CACert is used, the valid value is either OPENSSL or JDK. " + "When TLS authentication with KeyStore is used, available options can be SunJSSE, Conscrypt " - + "and so on.") + + "and so on.", descriptionKey = "webserviceTlsProvider") String tlsProvider; - - @Parameter(names = { "-v", "--version" }, description = "Get version of pulsar admin client") - boolean version; - - @Parameter(names = { "-h", "--help", }, help = true, description = "Show this help.") - boolean help; } public PulsarAdminTool(Properties properties) throws Exception { - this.properties = properties; - customCommandFactories = CustomCommandFactoryProvider.createCustomCommandFactories(properties); - rootParams = new RootParams(); - // fallback to previous-version serviceUrl property to maintain backward-compatibility - initRootParamsFromProperties(properties); - final PulsarAdminBuilder baseAdminBuilder = createAdminBuilderFromProperties(properties); - pulsarAdminSupplier = new PulsarAdminSupplier(baseAdminBuilder, rootParams); - initJCommander(); + // Use -v instead -V + System.setProperty("picocli.version.name.0", "-v"); + commander = CommanderFactory.createRootCommanderWithHook(this, pulsarAdminPropertiesProvider); + pulsarAdminSupplier = new PulsarAdminSupplier(createAdminBuilderFromProperties(properties), rootParams); + initCommander(properties); } - private static PulsarAdminBuilder createAdminBuilderFromProperties(Properties properties) { boolean useKeyStoreTls = Boolean .parseBoolean(properties.getProperty("useKeyStoreTls", "false")); @@ -143,19 +145,11 @@ private static PulsarAdminBuilder createAdminBuilderFromProperties(Properties pr .tlsCertificateFilePath(tlsCertificateFilePath); } - protected void initRootParamsFromProperties(Properties properties) { - rootParams.serviceUrl = isNotBlank(properties.getProperty("webServiceUrl")) - ? properties.getProperty("webServiceUrl") - : properties.getProperty("serviceUrl"); - rootParams.authPluginClassName = properties.getProperty("authPlugin"); - rootParams.authParams = properties.getProperty("authParams"); - rootParams.tlsProvider = properties.getProperty("webserviceTlsProvider"); - } - - public void setupCommands() { + private void setupCommands(Properties properties) { try { for (Map.Entry> c : commandMap.entrySet()) { - addCommand(c, pulsarAdminSupplier); + Object o = c.getValue().getConstructor(Supplier.class).newInstance(pulsarAdminSupplier); + addCommand(c.getKey(), o); } CommandExecutionContext context = new CommandExecutionContext() { @@ -174,8 +168,7 @@ public Properties getConfiguration() { List customCommandGroups = factory.commandGroups(context); for (CustomCommandGroup group : customCommandGroups) { Object generated = CustomCommandsUtils.generateCliCommand(group, context, pulsarAdminSupplier); - jcommander.addCommand(group.name(), generated); - commandMap.put(group.name(), null); + addCommand(group.name(), generated); } } } catch (Exception e) { @@ -190,84 +183,16 @@ public Properties getConfiguration() { } } - private void addCommand(Map.Entry> c, Supplier admin) throws Exception { - // To remain backwards compatibility for "source" and "sink" commands - // TODO eventually remove this - if (c.getKey().equals("sources") || c.getKey().equals("source")) { - jcommander.addCommand("sources", c.getValue().getConstructor(Supplier.class).newInstance(admin), "source"); - } else if (c.getKey().equals("sinks") || c.getKey().equals("sink")) { - jcommander.addCommand("sinks", c.getValue().getConstructor(Supplier.class).newInstance(admin), "sink"); - } else if (c.getKey().equals("functions")) { - jcommander.addCommand(c.getKey(), c.getValue().getConstructor(Supplier.class).newInstance(admin)); + private void addCommand(String name, Object o) throws Exception { + if (o instanceof CmdBase) { + commander.addSubcommand(name, ((CmdBase) o).getCommander()); } else { - // Other mode, all components are initialized. - if (c.getValue() != null) { - jcommander.addCommand(c.getKey(), c.getValue().getConstructor(Supplier.class).newInstance(admin)); - } + commander.addSubcommand(o); } } protected boolean run(String[] args) { - setupCommands(); - - if (args.length == 0) { - jcommander.usage(); - return false; - } - - int cmdPos; - for (cmdPos = 0; cmdPos < args.length; cmdPos++) { - if (commandMap.containsKey(args[cmdPos])) { - break; - } - } - - try { - jcommander.parse(Arrays.copyOfRange(args, 0, Math.min(cmdPos, args.length))); - //rootParams are populated by jcommander.parse - pulsarAdminSupplier.rootParamsUpdated(rootParams); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.err.println(); - jcommander.usage(); - return false; - } - - if (isBlank(rootParams.serviceUrl)) { - System.out.println("Can't find any admin url to use"); - jcommander.usage(); - return false; - } - - if (rootParams.version) { - System.out.println("Current version of pulsar admin client is: " + PulsarVersion.getVersion()); - return true; - } - - if (rootParams.help) { - jcommander.usage(); - return true; - } - - if (cmdPos == args.length) { - jcommander.usage(); - return false; - } else { - String cmd = args[cmdPos]; - - // To remain backwards compatibility for "source" and "sink" commands - // TODO eventually remove this - if (cmd.equals("source")) { - cmd = "sources"; - } else if (cmd.equals("sink")) { - cmd = "sinks"; - } - - JCommander obj = jcommander.getCommands().get(cmd); - CmdBase cmdObj = (CmdBase) obj.getObjects().get(0); - - return cmdObj.run(Arrays.copyOfRange(args, cmdPos + 1, args.length)); - } + return commander.execute(args) == 0; } public static void main(String[] args) throws Exception { @@ -325,11 +250,20 @@ static void resetLastExitCode() { lastExitCode = Integer.MIN_VALUE; } - protected void initJCommander() { - jcommander = new JCommander(); - jcommander.setProgramName("pulsar-admin"); - jcommander.addObject(rootParams); + @Override + public int preRun() { + if (isBlank(rootParams.serviceUrl)) { + commander.getErr().println("Can't find any admin url to use"); + return 1; + } + pulsarAdminSupplier.rootParamsUpdated(rootParams); + return 0; + } + private void initCommander(Properties properties) throws IOException { + customCommandFactories = CustomCommandFactoryProvider.createCustomCommandFactories(properties); + pulsarAdminPropertiesProvider = PulsarAdminPropertiesProvider.create(properties); + commander.setDefaultValueProvider(pulsarAdminPropertiesProvider); commandMap = new HashMap<>(); commandMap.put("clusters", CmdClusters.class); commandMap.put("ns-isolation-policy", CmdNamespaceIsolationPolicy.class); @@ -361,17 +295,10 @@ protected void initJCommander() { // Automatically generate documents for pulsar-admin commandMap.put("documents", CmdGenerateDocument.class); // To remain backwards compatibility for "source" and "sink" commands - // TODO eventually remove this - commandMap.put("source", CmdSources.class); - commandMap.put("sink", CmdSinks.class); - commandMap.put("packages", CmdPackages.class); commandMap.put("transactions", CmdTransactions.class); - } - @VisibleForTesting - public void setPulsarAdminSupplier(PulsarAdminSupplier pulsarAdminSupplier) { - this.pulsarAdminSupplier = pulsarAdminSupplier; + setupCommands(properties); } @VisibleForTesting @@ -379,8 +306,12 @@ public PulsarAdminSupplier getPulsarAdminSupplier() { return pulsarAdminSupplier; } - @VisibleForTesting - public RootParams getRootParams() { - return rootParams; + // The following methods are used for Pulsar shell. + protected void setCommandName(String name) { + commander.setCommandName(name); + } + + protected String getAdminUrl() { + return pulsarAdminPropertiesProvider.getAdminUrl(); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarVersionProvider.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarVersionProvider.java new file mode 100644 index 0000000000000..ff107a0eb1536 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarVersionProvider.java @@ -0,0 +1,29 @@ +/* + * 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.admin.cli; + +import org.apache.pulsar.PulsarVersion; +import picocli.CommandLine.IVersionProvider; + +public class PulsarVersionProvider implements IVersionProvider { + @Override + public String[] getVersion() { + return new String[]{"Current version of pulsar admin client is: " + PulsarVersion.getVersion()}; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/NoSplitter.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmd.java similarity index 65% rename from pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/NoSplitter.java rename to pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmd.java index 0429970366504..10b68648ebbba 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/NoSplitter.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmd.java @@ -18,21 +18,14 @@ */ package org.apache.pulsar.client.cli; -import com.beust.jcommander.converters.IParameterSplitter; -import java.util.LinkedList; -import java.util.List; +import java.util.concurrent.Callable; -public class NoSplitter implements IParameterSplitter { - - /* - * (non-Javadoc) - * - * @see com.beust.jcommander.converters.IParameterSplitter#split(java.lang.String) - */ +public abstract class AbstractCmd implements Callable { + // Picocli entrypoint. @Override - public List split(final String value) { - final List result = new LinkedList<>(); - result.add(value); - return result; + public Integer call() throws Exception { + return run(); } -} \ No newline at end of file + + abstract int run() throws Exception; +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmdConsume.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmdConsume.java index ef0ffbc297340..a7932c732eb81 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmdConsume.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/AbstractCmdConsume.java @@ -55,7 +55,7 @@ * common part of consume command and read command of pulsar-client. * */ -public abstract class AbstractCmdConsume { +public abstract class AbstractCmdConsume extends AbstractCmd { protected static final Logger LOG = LoggerFactory.getLogger(PulsarClientTool.class); protected static final String MESSAGE_BOUNDARY = "----- got message -----"; diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdConsume.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdConsume.java index 0c65604cbe6b8..71c172b633713 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdConsume.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdConsume.java @@ -19,16 +19,11 @@ package org.apache.pulsar.client.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.RateLimiter; import java.io.IOException; import java.net.URI; -import java.util.ArrayList; import java.util.Base64; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -38,7 +33,6 @@ import org.apache.pulsar.client.api.ConsumerBuilder; import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.PulsarClient; -import org.apache.pulsar.client.api.PulsarClientException; import org.apache.pulsar.client.api.Schema; import org.apache.pulsar.client.api.SubscriptionInitialPosition; import org.apache.pulsar.client.api.SubscriptionMode; @@ -47,69 +41,74 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.Spec; /** * pulsar-client consume command implementation. - * */ -@Parameters(commandDescription = "Consume messages from a specified topic") +@Command(description = "Consume messages from a specified topic") public class CmdConsume extends AbstractCmdConsume { - @Parameter(description = "TopicName", required = true) - private List mainOptions = new ArrayList(); + @Parameters(description = "TopicName", arity = "1") + private String topic; - @Parameter(names = { "-t", "--subscription-type" }, description = "Subscription type.") + @Option(names = { "-t", "--subscription-type" }, description = "Subscription type.") private SubscriptionType subscriptionType = SubscriptionType.Exclusive; - @Parameter(names = { "-m", "--subscription-mode" }, description = "Subscription mode.") + @Option(names = { "-m", "--subscription-mode" }, description = "Subscription mode.") private SubscriptionMode subscriptionMode = SubscriptionMode.Durable; - @Parameter(names = { "-p", "--subscription-position" }, description = "Subscription position.") + @Option(names = { "-p", "--subscription-position" }, description = "Subscription position.") private SubscriptionInitialPosition subscriptionInitialPosition = SubscriptionInitialPosition.Latest; - @Parameter(names = { "-s", "--subscription-name" }, required = true, description = "Subscription name.") + @Option(names = { "-s", "--subscription-name" }, required = true, description = "Subscription name.") private String subscriptionName; - @Parameter(names = { "-n", + @Option(names = { "-n", "--num-messages" }, description = "Number of messages to consume, 0 means to consume forever.") private int numMessagesToConsume = 1; - @Parameter(names = { "--hex" }, description = "Display binary messages in hex.") + @Option(names = { "--hex" }, description = "Display binary messages in hex.") private boolean displayHex = false; - @Parameter(names = { "--hide-content" }, description = "Do not write the message to console.") + @Option(names = { "--hide-content" }, description = "Do not write the message to console.") private boolean hideContent = false; - @Parameter(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to consume, " + @Option(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to consume, " + "value 0 means to consume messages as fast as possible.") private double consumeRate = 0; - @Parameter(names = { "--regex" }, description = "Indicate the topic name is a regex pattern") + @Option(names = { "--regex" }, description = "Indicate the topic name is a regex pattern") private boolean isRegex = false; - @Parameter(names = {"-q", "--queue-size"}, description = "Consumer receiver queue size.") + @Option(names = {"-q", "--queue-size"}, description = "Consumer receiver queue size.") private int receiverQueueSize = 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 = { "-ekv", + @Option(names = { "-ekv", "--encryption-key-value" }, description = "The URI of private key to decrypt payload, for example " + "file:///path/to/private.key or data:application/x-pem-file;base64,*****") private String encKeyValue; - @Parameter(names = { "-st", "--schema-type"}, + @Option(names = { "-st", "--schema-type"}, description = "Set a schema type on the consumer, it can be 'bytes' or 'auto_consume'") private String schemaType = "bytes"; - @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 = {"-rs", "--replicated" }, description = "Whether the subscription status should be replicated") + @Option(names = {"-rs", "--replicated" }, description = "Whether the subscription status should be replicated") private boolean replicateSubscriptionState = false; public CmdConsume() { @@ -117,24 +116,23 @@ public CmdConsume() { super(); } + @Spec + private CommandSpec commandSpec; + /** * Run the consume command. * * @return 0 for success, < 0 otherwise */ - public int run() throws PulsarClientException, IOException { - if (mainOptions.size() != 1) { - throw (new ParameterException("Please provide one and only one topic name.")); - } + public int run() throws IOException { if (this.subscriptionName == null || this.subscriptionName.isEmpty()) { - throw (new ParameterException("Subscription name is not provided.")); + throw new CommandLine.ParameterException(commandSpec.commandLine(), "Subscription name is not provided."); } if (this.numMessagesToConsume < 0) { - throw (new ParameterException("Number of messages should be zero or positive.")); + throw new CommandLine.ParameterException(commandSpec.commandLine(), + "Number of messages should be zero or positive."); } - String topic = this.mainOptions.get(0); - if (this.serviceURL.startsWith("ws")) { return consumeFromWebSocket(topic); } else { @@ -146,7 +144,7 @@ private int consume(String topic) { int numMessagesConsumed = 0; int returnCode = 0; - try (PulsarClient client = clientBuilder.build()){ + try (PulsarClient client = clientBuilder.build()) { ConsumerBuilder builder; Schema schema = poolMessages ? Schema.BYTEBUFFER : Schema.BYTES; if ("auto_consume".equals(schemaType)) { diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdGenerateDocumentation.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdGenerateDocumentation.java index cb5c5ef3c5b5a..eb0df56175b9a 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdGenerateDocumentation.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdGenerateDocumentation.java @@ -18,64 +18,81 @@ */ package org.apache.pulsar.client.cli; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterDescription; -import com.beust.jcommander.Parameters; +import static org.apache.pulsar.internal.CommandDescriptionUtil.getArgDescription; +import static org.apache.pulsar.internal.CommandDescriptionUtil.getCommandDescription; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.Properties; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.client.api.PulsarClientException; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Model.ArgSpec; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Model.OptionSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.Spec; @Getter -@Parameters(commandDescription = "Generate documentation automatically.") +@Command(description = "Generate documentation automatically.") @Slf4j -public class CmdGenerateDocumentation { +public class CmdGenerateDocumentation extends AbstractCmd { - @Parameter(names = {"-n", "--command-names"}, description = "List of command names") + @Spec + private CommandSpec pulsarClientCommandSpec; + + @Option(names = {"-n", "--command-names"}, description = "List of command names") private List commandNames = new ArrayList<>(); public int run() throws PulsarClientException { - PulsarClientTool pulsarClientTool = new PulsarClientTool(new Properties()); - JCommander commander = pulsarClientTool.jcommander; - if (commandNames.size() == 0) { - for (Map.Entry cmd : commander.getCommands().entrySet()) { - if (cmd.getKey().equals("generate_documentation")) { - continue; + if (commandNames == null || commandNames.isEmpty()) { + pulsarClientCommandSpec.parent().subcommands().forEach((k, v) -> { + if (k.equals("generate_documentation")) { + return; } - generateDocument(cmd.getKey(), commander); - } + this.generateDocument(k, v); + }); } else { - for (String commandName : commandNames) { - if (commandName.equals("generate_documentation")) { - continue; + commandNames.forEach(module -> { + CommandLine commandLine = pulsarClientCommandSpec.parent().subcommands().get(module); + if (commandLine == null) { + return; } - generateDocument(commandName, commander); - } + if (commandLine.getCommandName().equals("generate_documentation")) { + return; + } + this.generateDocument(module, commandLine); + }); } + return 0; } - protected String generateDocument(String module, JCommander parentCmd) { + protected String generateDocument(String module, CommandLine parentCmd) { StringBuilder sb = new StringBuilder(); - JCommander cmd = parentCmd.getCommands().get(module); sb.append("## ").append(module).append("\n\n"); - sb.append(parentCmd.getUsageFormatter().getCommandDescription(module)).append("\n"); + sb.append(getCommandDescription(parentCmd)).append("\n"); sb.append("\n\n```shell\n") .append("$ pulsar-client ").append(module).append(" [options]") .append("\n```"); sb.append("\n\n"); sb.append("|Flag|Description|Default|\n"); sb.append("|---|---|---|\n"); - List 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 options = parentCmd.getCommandSpec().args(); + options.forEach(ele -> { + if (ele.hidden() || !(ele instanceof OptionSpec)) { + return; + } + + String argDescription = getArgDescription(ele); + String descriptions = argDescription.replace("\n", " "); + sb.append("| `").append(Arrays.toString(((OptionSpec) ele).names())) + .append("` | ").append(descriptions) + .append("|").append(ele.defaultValue()).append("|\n"); + sb.append("|\n"); + }); System.out.println(sb.toString()); return sb.toString(); } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdProduce.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdProduce.java index 77a1612f30e67..b41aea4538c02 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdProduce.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdProduce.java @@ -19,9 +19,7 @@ package org.apache.pulsar.client.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.RateLimiter; @@ -77,13 +75,18 @@ 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.Model.CommandSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.Spec; /** * pulsar-client produce command implementation. - * */ -@Parameters(commandDescription = "Produce messages to a specified topic") -public class CmdProduce { +@Command(description = "Produce messages to a specified topic") +public class CmdProduce extends AbstractCmd { private static final Logger LOG = LoggerFactory.getLogger(PulsarClientTool.class); private static final int MAX_MESSAGES = 1000; @@ -91,72 +94,71 @@ public class CmdProduce { private static final String KEY_VALUE_ENCODING_TYPE_SEPARATED = "separated"; private static final String KEY_VALUE_ENCODING_TYPE_INLINE = "inline"; - @Parameter(description = "TopicName", required = true) - private List mainOptions; + @Parameters(description = "TopicName", arity = "1") + private String topic; - @Parameter(names = { "-m", "--messages" }, - description = "Messages to send, either -m or -f must be specified. Specify -m for each message.", - splitter = NoSplitter.class) + @Option(names = { "-m", "--messages" }, + description = "Messages to send, either -m or -f must be specified. Specify -m for each message.") private List messages = new ArrayList<>(); - @Parameter(names = { "-f", "--files" }, + @Option(names = { "-f", "--files" }, description = "Comma separated file paths to send, either -m or -f must be specified.") private List messageFileNames = new ArrayList<>(); - @Parameter(names = { "-n", "--num-produce" }, + @Option(names = { "-n", "--num-produce" }, description = "Number of times to send message(s), the count of messages/files * num-produce " + "should below than " + MAX_MESSAGES + ".") private int numTimesProduce = 1; - @Parameter(names = { "-r", "--rate" }, + @Option(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to produce," + " value 0 means to produce messages as fast as possible.") private double publishRate = 0; - @Parameter(names = { "-db", "--disable-batching" }, description = "Disable batch sending of messages") + @Option(names = { "-db", "--disable-batching" }, description = "Disable batch sending of messages") private boolean disableBatching = false; - @Parameter(names = { "-c", + @Option(names = { "-c", "--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 = { "-s", "--separator" }, + @Option(names = { "-s", "--separator" }, description = "Character to split messages string on default is comma") private String separator = ","; - @Parameter(names = { "-p", "--properties"}, description = "Properties to add, Comma separated " + @Option(names = { "-p", "--properties"}, description = "Properties to add, Comma separated " + "key=value string, like k1=v1,k2=v2.") private List properties = new ArrayList<>(); - @Parameter(names = { "-k", "--key"}, description = "Partitioning key to add to each message") + @Option(names = { "-k", "--key"}, description = "Partitioning key to add to each message") private String key; - @Parameter(names = { "-kvk", "--key-value-key"}, description = "Value to add as message key in KeyValue schema") + @Option(names = { "-kvk", "--key-value-key"}, description = "Value to add as message key in KeyValue schema") private String keyValueKey; - @Parameter(names = { "-kvkf", "--key-value-key-file"}, + @Option(names = {"-kvkf", "--key-value-key-file"}, description = "Path to file containing the value to add as message key in KeyValue schema. " + "JSON and AVRO files are supported.") private String keyValueKeyFile; - @Parameter(names = { "-vs", "--value-schema"}, description = "Schema type (can be bytes,avro,json,string...)") + @Option(names = { "-vs", "--value-schema"}, description = "Schema type (can be bytes,avro,json,string...)") private String valueSchema = "bytes"; - @Parameter(names = { "-ks", "--key-schema"}, description = "Schema type (can be bytes,avro,json,string...)") + @Option(names = { "-ks", "--key-schema"}, description = "Schema type (can be bytes,avro,json,string...)") private String keySchema = "string"; - @Parameter(names = { "-kvet", "--key-value-encoding-type"}, + @Option(names = { "-kvet", "--key-value-encoding-type"}, description = "Key Value Encoding Type (it can be separated or inline)") private String keyValueEncodingType = null; - @Parameter(names = { "-ekn", "--encryption-key-name" }, description = "The public key name to encrypt payload") + @Option(names = { "-ekn", "--encryption-key-name" }, description = "The public key name to encrypt payload") private String encKeyName = null; - @Parameter(names = { "-ekv", + @Option(names = { "-ekv", "--encryption-key-value" }, description = "The URI of public key to encrypt payload, for example " + "file:///path/to/public.key or data:application/x-pem-file;base64,*****") private String encKeyValue = null; - @Parameter(names = { "-dr", + @Option(names = { "-dr", "--disable-replication" }, description = "Disable geo-replication for messages.") private boolean disableReplication = false; @@ -170,7 +172,6 @@ public CmdProduce() { /** * Set Pulsar client configuration. - * */ public void updateConfig(ClientBuilder newBuilder, Authentication authentication, String serviceURL) { this.clientBuilder = newBuilder; @@ -214,7 +215,7 @@ static List generateMessageBodies(List stringMessages, List reader = new GenericDatumReader<>(avroSchema); JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(avroSchema, m); @@ -237,6 +238,9 @@ private static byte[] jsonToAvro(String m, org.apache.avro.Schema avroSchema){ } } + @Spec + private CommandSpec commandSpec; + /** * Run the producer. * @@ -244,19 +248,18 @@ private static byte[] jsonToAvro(String m, org.apache.avro.Schema avroSchema){ * @throws Exception */ public int run() throws PulsarClientException { - if (mainOptions.size() != 1) { - throw (new ParameterException("Please provide one and only one topic name.")); - } if (this.numTimesProduce <= 0) { - throw (new ParameterException("Number of times need to be positive number.")); + throw new CommandLine.ParameterException(commandSpec.commandLine(), + "Number of times need to be positive number."); } - if (messages.size() > 0){ + if (messages.size() > 0) { messages = messages.stream().map(str -> str.split(separator)).flatMap(Stream::of).toList(); } if (messages.size() == 0 && messageFileNames.size() == 0) { - throw (new ParameterException("Please supply message content with either --messages or --files")); + throw new CommandLine.ParameterException(commandSpec.commandLine(), + "Please supply message content with either --messages or --files"); } if (keyValueEncodingType == null) { @@ -279,8 +282,6 @@ public int run() throws PulsarClientException { throw new ParameterException(msg); } - String topic = this.mainOptions.get(0); - if (this.serviceURL.startsWith("ws")) { return publishToWebSocket(topic); } else { @@ -409,7 +410,7 @@ private static Schema buildComponentSchema(String schema) { Schema base; switch (schema) { case "string": - base = Schema.STRING; + base = Schema.STRING; break; case "bytes": // no need for wrappers diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdRead.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdRead.java index 4ad8a5293f6e1..2e0a3e826aa61 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdRead.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdRead.java @@ -19,16 +19,12 @@ package org.apache.pulsar.client.cli; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.RateLimiter; import java.io.IOException; import java.net.URI; -import java.util.ArrayList; import java.util.Base64; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -47,61 +43,63 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * pulsar-client read command implementation. - * */ -@Parameters(commandDescription = "Read messages from a specified topic") +@Command(description = "Read messages from a specified topic") public class CmdRead extends AbstractCmdConsume { private static final Pattern MSG_ID_PATTERN = Pattern.compile("^(-?[1-9][0-9]*|0):(-?[1-9][0-9]*|0)$"); - @Parameter(description = "TopicName", required = true) - private List mainOptions = new ArrayList(); + @Parameters(description = "TopicName", arity = "1") + private String topic; - @Parameter(names = { "-m", "--start-message-id" }, + @Option(names = { "-m", "--start-message-id" }, description = "Initial reader position, it can be 'latest', 'earliest' or ':'") private String startMessageId = "latest"; - @Parameter(names = { "-i", "--start-message-id-inclusive" }, + @Option(names = { "-i", "--start-message-id-inclusive" }, description = "Whether to include the position specified by -m option.") private boolean startMessageIdInclusive = false; - @Parameter(names = { "-n", + @Option(names = { "-n", "--num-messages" }, description = "Number of messages to read, 0 means to read forever.") private int numMessagesToRead = 1; - @Parameter(names = { "--hex" }, description = "Display binary messages in hex.") + @Option(names = { "--hex" }, description = "Display binary messages in hex.") private boolean displayHex = false; - @Parameter(names = { "--hide-content" }, description = "Do not write the message to console.") + @Option(names = { "--hide-content" }, description = "Do not write the message to console.") private boolean hideContent = false; - @Parameter(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to read, " + @Option(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to read, " + "value 0 means to read messages as fast as possible.") private double readRate = 0; - @Parameter(names = {"-q", "--queue-size"}, description = "Reader receiver queue size.") + @Option(names = { "-q", "--queue-size" }, description = "Reader receiver queue size.") private int receiverQueueSize = 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 = { "-ekv", + @Option(names = { "-ekv", "--encryption-key-value" }, description = "The URI of private key to decrypt payload, for example " - + "file:///path/to/private.key or data:application/x-pem-file;base64,*****") + + "file:///path/to/private.key or data:application/x-pem-file;base64,*****") private String encKeyValue; - @Parameter(names = { "-st", "--schema-type"}, + @Option(names = { "-st", "--schema-type" }, description = "Set a schema type on the reader, it can be 'bytes' or 'auto_consume'") private String schemaType = "bytes"; - @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; public CmdRead() { @@ -115,14 +113,10 @@ public CmdRead() { * @return 0 for success, < 0 otherwise */ public int run() throws PulsarClientException, IOException { - if (mainOptions.size() != 1) { - throw (new ParameterException("Please provide one and only one topic name.")); - } if (this.numMessagesToRead < 0) { throw (new ParameterException("Number of messages should be zero or positive.")); } - String topic = this.mainOptions.get(0); if (this.serviceURL.startsWith("ws")) { return readFromWebSocket(topic); diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/ProxyProtocolConverter.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/ProxyProtocolConverter.java new file mode 100644 index 0000000000000..3aa731db138ba --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/ProxyProtocolConverter.java @@ -0,0 +1,35 @@ +/* + * 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.client.cli; + +import org.apache.commons.lang3.StringUtils; +import org.apache.pulsar.client.api.ProxyProtocol; +import picocli.CommandLine.ITypeConverter; + +public class ProxyProtocolConverter implements ITypeConverter { + + @Override + public ProxyProtocol convert(String value) throws Exception { + String proxyProtocolString = StringUtils.trimToNull(value); + if (proxyProtocolString != null) { + return ProxyProtocol.valueOf(proxyProtocolString.toUpperCase()); + } + return null; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientPropertiesProvider.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientPropertiesProvider.java new file mode 100644 index 0000000000000..accc5df8595d5 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientPropertiesProvider.java @@ -0,0 +1,54 @@ +/* + * 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.client.cli; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.util.Properties; +import picocli.CommandLine.PropertiesDefaultProvider; + +class PulsarClientPropertiesProvider extends PropertiesDefaultProvider { + private static final String brokerServiceUrlKey = "brokerServiceUrl"; + private final Properties properties; + + private PulsarClientPropertiesProvider(Properties properties) { + super(properties); + this.properties = properties; + } + + static PulsarClientPropertiesProvider create(Properties properties) { + Properties clone = (Properties) properties.clone(); + String brokerServiceUrl = clone.getProperty(brokerServiceUrlKey); + if (isBlank(brokerServiceUrl)) { + String serviceUrl = clone.getProperty("webServiceUrl"); + if (isBlank(serviceUrl)) { + // fallback to previous-version serviceUrl property to maintain backward-compatibility + serviceUrl = clone.getProperty("serviceUrl"); + } + if (isNotBlank(serviceUrl)) { + clone.put(brokerServiceUrlKey, serviceUrl); + } + } + return new PulsarClientPropertiesProvider(clone); + } + + String getServiceUrl() { + return properties.getProperty(brokerServiceUrlKey); + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java index 4057bbe9fdfd8..567c8d201e4ed 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java @@ -18,21 +18,14 @@ */ package org.apache.pulsar.client.cli; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.beust.jcommander.DefaultUsageFormatter; -import com.beust.jcommander.IUsageFormatter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; +import com.google.common.annotations.VisibleForTesting; import java.io.FileInputStream; import java.util.Arrays; import java.util.Properties; import lombok.Getter; -import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.PulsarVersion; -import org.apache.pulsar.cli.converters.ByteUnitToLongConverter; +import lombok.SneakyThrows; +import org.apache.pulsar.cli.converters.picocli.ByteUnitToLongConverter; import org.apache.pulsar.client.api.Authentication; import org.apache.pulsar.client.api.AuthenticationFactory; import org.apache.pulsar.client.api.ClientBuilder; @@ -40,49 +33,69 @@ import org.apache.pulsar.client.api.PulsarClient; import org.apache.pulsar.client.api.PulsarClientException.UnsupportedAuthenticationException; import org.apache.pulsar.client.api.SizeUnit; - -public class PulsarClientTool { +import org.apache.pulsar.internal.CommandHook; +import org.apache.pulsar.internal.CommanderFactory; +import picocli.CommandLine; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; + +@Command( + name = "pulsar-client", + mixinStandardHelpOptions = true, + versionProvider = PulsarVersionProvider.class, + scope = ScopeType.INHERIT +) +public class PulsarClientTool implements CommandHook { + + private PulsarClientPropertiesProvider pulsarClientPropertiesProvider; @Getter - @Parameters(commandDescription = "Produce or consume messages on a specified topic") + @Command(description = "Produce or consume messages on a specified topic") public static class RootParams { - @Parameter(names = { "--url" }, description = "Broker URL to which to connect.") + @Option(names = {"--url"}, descriptionKey = "brokerServiceUrl", + description = "Broker URL to which to connect.") String serviceURL = null; - @Parameter(names = { "--proxy-url" }, description = "Proxy-server URL to which to connect.") + @Option(names = {"--proxy-url"}, descriptionKey = "proxyServiceUrl", + 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"}, descriptionKey = "proxyProtocol", + description = "Proxy protocol to select type of routing at proxy.", + converter = ProxyProtocolConverter.class) ProxyProtocol proxyProtocol = null; - @Parameter(names = { "--auth-plugin" }, description = "Authentication plugin class name.") + @Option(names = {"--auth-plugin"}, descriptionKey = "authPlugin", + description = "Authentication plugin class name.") String authPluginClassName = null; - @Parameter(names = { "--listener-name" }, description = "Listener name for the broker.") + @Option(names = {"--listener-name"}, description = "Listener name for the broker.") String listenerName = null; - @Parameter( - 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\"}\".") + @Option( + names = {"--auth-params"}, + descriptionKey = "authParams", + 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\"}\".") String authParams = null; - @Parameter(names = { "-v", "--version" }, description = "Get version of pulsar client") - boolean version; - - @Parameter(names = { "-h", "--help", }, help = true, description = "Show this help.") - boolean help; - - @Parameter(names = { "--tlsTrustCertsFilePath" }, description = "File path to client trust certificates") + @Option(names = {"--tlsTrustCertsFilePath"}, + descriptionKey = "tlsTrustCertsFilePath", + description = "File path to client trust certificates") String tlsTrustCertsFilePath; - @Parameter(names = { "-ml", "--memory-limit", }, description = "Configure the Pulsar client memory limit " - + "(eg: 32M, 64M)", converter = ByteUnitToLongConverter.class) + @Option(names = {"-ml", "--memory-limit"}, description = "Configure the Pulsar client memory limit " + + "(eg: 32M, 64M)", descriptionKey = "memoryLimit", + converter = ByteUnitToLongConverter.class) long memoryLimit = 0L; } - protected RootParams rootParams; + + @ArgGroup(exclusive = false) + protected RootParams rootParams = new RootParams(); boolean tlsAllowInsecureConnection; boolean tlsEnableHostnameVerification; @@ -99,16 +112,31 @@ public static class RootParams { String tlsKeyStorePath; String tlsKeyStorePassword; - protected JCommander jcommander; - IUsageFormatter usageFormatter; + protected final CommandLine commander; protected CmdProduce produceCommand; protected CmdConsume consumeCommand; protected CmdRead readCommand; CmdGenerateDocumentation generateDocumentation; public PulsarClientTool(Properties properties) { - rootParams = new RootParams(); - initRootParamsFromProperties(properties); + // Use -v instead -V + System.setProperty("picocli.version.name.0", "-v"); + commander = CommanderFactory.createRootCommanderWithHook(this, null); + initCommander(properties); + } + + @Override + @SneakyThrows + public int preRun() { + return updateConfig(); + } + + protected void initCommander(Properties properties) { + produceCommand = new CmdProduce(); + consumeCommand = new CmdConsume(); + readCommand = new CmdRead(); + generateDocumentation = new CmdGenerateDocumentation(); + this.tlsAllowInsecureConnection = Boolean .parseBoolean(properties.getProperty("tlsAllowInsecureConnection", "false")); this.tlsEnableHostnameVerification = Boolean @@ -125,54 +153,19 @@ public PulsarClientTool(Properties properties) { this.tlsKeyFilePath = properties.getProperty("tlsKeyFilePath"); this.tlsCertificateFilePath = properties.getProperty("tlsCertificateFilePath"); - initJCommander(); + pulsarClientPropertiesProvider = PulsarClientPropertiesProvider.create(properties); + commander.setDefaultValueProvider(pulsarClientPropertiesProvider); + commander.addSubcommand("produce", produceCommand); + commander.addSubcommand("consume", consumeCommand); + commander.addSubcommand("read", readCommand); + commander.addSubcommand("generate_documentation", generateDocumentation); } - protected void initJCommander() { - produceCommand = new CmdProduce(); - consumeCommand = new CmdConsume(); - readCommand = new CmdRead(); - generateDocumentation = new CmdGenerateDocumentation(); - - this.jcommander = new JCommander(); - this.usageFormatter = new DefaultUsageFormatter(this.jcommander); - jcommander.setProgramName("pulsar-client"); - jcommander.addObject(rootParams); - jcommander.addCommand("produce", produceCommand); - jcommander.addCommand("consume", consumeCommand); - jcommander.addCommand("read", readCommand); - jcommander.addCommand("generate_documentation", generateDocumentation); + protected void addCommand(String name, Object cmd) { + commander.addSubcommand(name, cmd); } - protected void initRootParamsFromProperties(Properties properties) { - this.rootParams.serviceURL = isNotBlank(properties.getProperty("brokerServiceUrl")) - ? properties.getProperty("brokerServiceUrl") : properties.getProperty("webServiceUrl"); - // fallback to previous-version serviceUrl property to maintain backward-compatibility - if (isBlank(this.rootParams.serviceURL)) { - this.rootParams.serviceURL = properties.getProperty("serviceUrl"); - } - this.rootParams.authPluginClassName = properties.getProperty("authPlugin"); - this.rootParams.authParams = properties.getProperty("authParams"); - this.rootParams.tlsTrustCertsFilePath = properties.getProperty("tlsTrustCertsFilePath"); - this.rootParams.proxyServiceURL = StringUtils.trimToNull(properties.getProperty("proxyServiceUrl")); - // setting memory limit - this.rootParams.memoryLimit = StringUtils.isNotEmpty(properties.getProperty("memoryLimit")) - ? new ByteUnitToLongConverter("memoryLimit").convert(properties.getProperty("memoryLimit")) - : this.rootParams.memoryLimit; - - String proxyProtocolString = StringUtils.trimToNull(properties.getProperty("proxyProtocol")); - if (proxyProtocolString != null) { - try { - this.rootParams.proxyProtocol = ProxyProtocol.valueOf(proxyProtocolString.toUpperCase()); - } catch (IllegalArgumentException e) { - System.out.println("Incorrect proxyProtocol name '" + proxyProtocolString + "'"); - e.printStackTrace(); - System.exit(1); - } - } - } - - private void updateConfig() throws UnsupportedAuthenticationException { + private int updateConfig() throws UnsupportedAuthenticationException { ClientBuilder clientBuilder = PulsarClient.builder() .memoryLimit(rootParams.memoryLimit, SizeUnit.BYTES); Authentication authentication = null; @@ -201,71 +194,19 @@ private void updateConfig() throws UnsupportedAuthenticationException { if (isNotBlank(rootParams.proxyServiceURL)) { if (rootParams.proxyProtocol == null) { - System.out.println("proxy-protocol must be provided with proxy-url"); - System.exit(1); + commander.getErr().println("proxy-protocol must be provided with proxy-url"); + return 1; } clientBuilder.proxyServiceUrl(rootParams.proxyServiceURL, rootParams.proxyProtocol); } this.produceCommand.updateConfig(clientBuilder, authentication, this.rootParams.serviceURL); this.consumeCommand.updateConfig(clientBuilder, authentication, this.rootParams.serviceURL); this.readCommand.updateConfig(clientBuilder, authentication, this.rootParams.serviceURL); + return 0; } public int run(String[] args) { - try { - jcommander.parse(args); - - if (isBlank(this.rootParams.serviceURL)) { - jcommander.usage(); - return -1; - } - - if (rootParams.version) { - System.out.println("Current version of pulsar client is: " + PulsarVersion.getVersion()); - return 0; - } - - if (rootParams.help) { - jcommander.usage(); - return 0; - } - - try { - this.updateConfig(); // If the --url, --auth-plugin, or --auth-params parameter are not specified, - // it will default to the values passed in by the constructor - } catch (UnsupportedAuthenticationException exp) { - System.out.println("Failed to load an authentication plugin"); - exp.printStackTrace(); - return -1; - } - - String chosenCommand = jcommander.getParsedCommand(); - if ("produce".equals(chosenCommand)) { - return produceCommand.run(); - } else if ("consume".equals(chosenCommand)) { - return consumeCommand.run(); - } else if ("read".equals(chosenCommand)) { - return readCommand.run(); - } else if ("generate_documentation".equals(chosenCommand)) { - return generateDocumentation.run(); - } else { - jcommander.usage(); - return -1; - } - } catch (Exception e) { - System.out.println(e.getMessage()); - String chosenCommand = jcommander.getParsedCommand(); - if (e instanceof ParameterException) { - try { - usageFormatter.usage(chosenCommand); - } catch (ParameterException noCmd) { - e.printStackTrace(); - } - } else { - e.printStackTrace(); - } - return -1; - } + return commander.execute(args); } public static void main(String[] args) throws Exception { @@ -286,6 +227,28 @@ public static void main(String[] args) throws Exception { int exitCode = clientTool.run(Arrays.copyOfRange(args, 1, args.length)); System.exit(exitCode); + } + + @VisibleForTesting + public void replaceProducerCommand(CmdProduce object) { + this.produceCommand = object; + if (commander.getSubcommands().containsKey("produce")) { + commander.getCommandSpec().removeSubcommand("produce"); + } + commander.addSubcommand("produce", this.produceCommand); + } + + @VisibleForTesting + CommandLine getCommander() { + return commander; + } + + // The following methods are used for Pulsar shell. + protected void setCommandName(String name) { + commander.setCommandName(name); + } + protected String getServiceUrl() { + return pulsarClientPropertiesProvider.getServiceUrl(); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarVersionProvider.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarVersionProvider.java new file mode 100644 index 0000000000000..45996b92ff706 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarVersionProvider.java @@ -0,0 +1,29 @@ +/* + * 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.client.cli; + +import org.apache.pulsar.PulsarVersion; +import picocli.CommandLine.IVersionProvider; + +public class PulsarVersionProvider implements IVersionProvider { + @Override + public String[] getVersion() { + return new String[]{"Current version of pulsar client is: " + PulsarVersion.getVersion()}; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandDescriptionUtil.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandDescriptionUtil.java new file mode 100644 index 0000000000000..ae00aebcb1817 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandDescriptionUtil.java @@ -0,0 +1,40 @@ +/* + * 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.internal; + +import picocli.CommandLine; +import picocli.CommandLine.Model.ArgSpec; + +public class CommandDescriptionUtil { + 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 getArgDescription(ArgSpec argSpec) { + String[] description = argSpec.description(); + if (description != null && description.length != 0) { + return description[0]; + } + return ""; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandHook.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandHook.java new file mode 100644 index 0000000000000..879c82a75816a --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommandHook.java @@ -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.pulsar.internal; + +/** + * CommandHook is used for injecting or configuring parameters during different stages of command execution. + */ +public interface CommandHook { + /** + * The preRun hook is used to execute commands before. + * + * @return If the return value is 0, continue to the next stage. + */ + default int preRun() { + return 0; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommanderFactory.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommanderFactory.java new file mode 100644 index 0000000000000..b9e3fbe90cf22 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/CommanderFactory.java @@ -0,0 +1,91 @@ +/* + * 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.internal; + +import java.io.PrintWriter; +import org.apache.pulsar.client.admin.PulsarAdminException; +import org.apache.pulsar.client.admin.PulsarAdminException.ConnectException; +import picocli.CommandLine; +import picocli.CommandLine.ExecutionException; +import picocli.CommandLine.IDefaultValueProvider; +import picocli.CommandLine.IExecutionStrategy; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ParseResult; + +public class CommanderFactory { + static class CommandExecutionStrategy implements IExecutionStrategy { + private int preRun(ParseResult parseResult) { + Object userObject = parseResult.commandSpec().userObject(); + if (userObject instanceof CommandHook) { + int exitCode = ((CommandHook) userObject).preRun(); + if (exitCode != 0) { + return exitCode; + } + } + + if (parseResult.hasSubcommand()) { + return preRun(parseResult.subcommand()); + } + + return 0; + } + + + @Override + public int execute(ParseResult parseResult) throws ExecutionException, ParameterException { + int preRunCode = preRun(parseResult); + if (preRunCode != 0) { + return preRunCode; + } + + return new CommandLine.RunLast().execute(parseResult); + } + } + + /** + * createRootCommanderWithHook is used for the root command, which supports the hook feature. + * + * @param object Command class or object. + * @param defaultValueProvider Default value provider of command. + * @return Picocli commander. + */ + public static CommandLine createRootCommanderWithHook(Object object, IDefaultValueProvider defaultValueProvider) { + CommandLine commander = new CommandLine(object); + commander.setExecutionStrategy(new CommandExecutionStrategy()); + commander.setExecutionExceptionHandler((ex, commandLine, parseResult) -> { + PrintWriter errPrinter = commandLine.getErr(); + if (ex instanceof ConnectException) { + errPrinter.println(ex.getMessage()); + errPrinter.println(); + errPrinter.println("Error connecting to Pulsar"); + return 1; + } else if (ex instanceof PulsarAdminException) { + errPrinter.println(((PulsarAdminException) ex).getHttpError()); + errPrinter.println(); + errPrinter.println("Reason: " + ex.getMessage()); + return 1; + } + throw ex; + }); + if (defaultValueProvider != null) { + commander.setDefaultValueProvider(defaultValueProvider); + } + return commander; + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/InnerClassFactory.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/InnerClassFactory.java new file mode 100644 index 0000000000000..64c946ba72e3b --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/InnerClassFactory.java @@ -0,0 +1,60 @@ +/* + * 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.internal; + +import java.lang.reflect.Constructor; +import picocli.CommandLine; +import picocli.CommandLine.IFactory; +import picocli.CommandLine.InitializationException; + +// Copied from https://github.com/remkop/picocli/blob/v4.7.5/src/test/java/picocli/InnerClassFactory.java +// The default Picocli factory doesn't support create non-static inner class. +public class InnerClassFactory implements IFactory { + private final Object outer; + private final IFactory defaultFactory = CommandLine.defaultFactory(); + + public InnerClassFactory(Object outer) { + this.outer = outer; + } + + public K create(final Class cls) throws Exception { + try { + return defaultFactory.create(cls); + } catch (Exception ex0) { + try { + Constructor constructor = cls.getDeclaredConstructor(outer.getClass()); + return constructor.newInstance(outer); + } catch (Exception ex) { + try { + @SuppressWarnings("deprecation") // Class.newInstance is deprecated in Java 9 + K result = cls.newInstance(); + return result; + } catch (Exception ex2) { + try { + Constructor constructor = cls.getDeclaredConstructor(); + return constructor.newInstance(); + } catch (Exception ex3) { + throw new InitializationException("Could not instantiate " + cls.getName() + + " either with or without construction parameter " + outer + ": " + ex, ex); + } + } + } + } + } +} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ShellCommandsProvider.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/ShellCommandsProvider.java similarity index 58% rename from pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ShellCommandsProvider.java rename to pulsar-client-tools/src/main/java/org/apache/pulsar/internal/ShellCommandsProvider.java index 069ee01d4dcd8..caae4eea01547 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ShellCommandsProvider.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/ShellCommandsProvider.java @@ -16,10 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.shell; +package org.apache.pulsar.internal; -import com.beust.jcommander.JCommander; -import java.util.Properties; +import picocli.CommandLine; /** * Commands provider for Pulsar shell. @@ -47,32 +46,8 @@ public interface ShellCommandsProvider { String getAdminUrl(); /** - * Init state before a command is executed. - * If the implementing class rely on JCommander, it's suggested to not recycle JCommander - * objects because they are meant to single-shot usage. - * @param properties + * Return commander instance. + * @return Non-null. */ - void setupState(Properties properties); - - /** - * Cleanup state after a command is executed. - * If the implementing class rely on JCommander, it's suggested to not recycle JCommander - * objects because they are meant to single-shot usage. - * @param properties - */ - void cleanupState(Properties properties); - - /** - * Return JCommander instance, if exists. - * @return - */ - JCommander getJCommander(); - - /** - * Run command for the passed args. - * - * @param args arguments for the command. Note that the first word of the user command is omitted. - * @throws Exception if any error occurs. The shell session will not be closed. - */ - boolean runCommand(String[] args) throws Exception; + CommandLine getCommander(); } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/package-info.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/package-info.java new file mode 100644 index 0000000000000..99aa7f7e33675 --- /dev/null +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/internal/package-info.java @@ -0,0 +1,19 @@ +/* + * 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.internal; diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/AdminShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/AdminShell.java index e139697860229..35d676eefabfb 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/AdminShell.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/AdminShell.java @@ -18,19 +18,22 @@ */ package org.apache.pulsar.shell; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameters; +import com.google.common.annotations.VisibleForTesting; import java.util.Properties; import org.apache.pulsar.admin.cli.PulsarAdminTool; +import org.apache.pulsar.internal.ShellCommandsProvider; +import picocli.CommandLine; +import picocli.CommandLine.Command; /** * Pulsar Admin tool extension for Pulsar shell. */ -@Parameters(commandDescription = "Admin console") +@Command(description = "Admin console") public class AdminShell extends PulsarAdminTool implements ShellCommandsProvider { public AdminShell(Properties properties) throws Exception { super(properties); + setCommandName(getName()); } @Override @@ -45,30 +48,16 @@ public String getServiceUrl() { @Override public String getAdminUrl() { - return rootParams.getServiceUrl(); + return super.getAdminUrl(); } @Override - public void setupState(Properties properties) { - getJCommander().setProgramName(getName()); - setupCommands(); + public CommandLine getCommander() { + return commander; } - @Override - public JCommander getJCommander() { - return jcommander; - } - - @Override - public void cleanupState(Properties properties) { - rootParams = new RootParams(); - initRootParamsFromProperties(properties); - initJCommander(); - } - - - @Override - public boolean runCommand(String[] args) throws Exception { + @VisibleForTesting + boolean runCommand(String[] args) { return run(args); } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ClientShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ClientShell.java index 6ba96e046374a..7cc4f825c85a0 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ClientShell.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ClientShell.java @@ -18,19 +18,21 @@ */ package org.apache.pulsar.shell; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameters; import java.util.Properties; import org.apache.pulsar.client.cli.PulsarClientTool; +import org.apache.pulsar.internal.ShellCommandsProvider; +import picocli.CommandLine; +import picocli.CommandLine.Command; /** * Pulsar Client tool extension for Pulsar shell. */ -@Parameters(commandDescription = "Produce or consume messages on a specified topic") +@Command(description = "Produce or consume messages on a specified topic") public class ClientShell extends PulsarClientTool implements ShellCommandsProvider { public ClientShell(Properties properties) { super(properties); + setCommandName(getName()); } @Override @@ -40,7 +42,7 @@ public String getName() { @Override public String getServiceUrl() { - return rootParams.getServiceURL(); + return super.getServiceUrl(); } @Override @@ -49,25 +51,7 @@ public String getAdminUrl() { } @Override - public void setupState(Properties properties) { - getJCommander().setProgramName(getName()); - } - - @Override - public void cleanupState(Properties properties) { - rootParams = new RootParams(); - initRootParamsFromProperties(properties); - initJCommander(); - } - - @Override - public JCommander getJCommander() { - return jcommander; - } - - @Override - public boolean runCommand(String[] args) throws Exception { - final int returnCode = run(args); - return returnCode == 0; + public CommandLine getCommander() { + return commander; } } diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java index b957fce70adec..de785c226f3c0 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java @@ -19,10 +19,6 @@ package org.apache.pulsar.shell; import static org.apache.pulsar.shell.config.ConfigStore.DEFAULT_CONFIG; -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.SerializationFeature; import java.io.ByteArrayOutputStream; @@ -33,22 +29,30 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Base64; -import java.util.HashMap; +import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Properties; +import java.util.concurrent.Callable; import java.util.stream.Collectors; import lombok.Getter; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.pulsar.internal.InnerClassFactory; +import org.apache.pulsar.internal.ShellCommandsProvider; import org.apache.pulsar.shell.config.ConfigStore; +import org.apache.pulsar.shell.config.ConfigStore.ConfigEntry; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * Shell commands to manage shell configurations. */ -@Parameters(commandDescription = "Manage Pulsar shell configurations.") +@Command(description = "Manage Pulsar shell configurations.") public class ConfigShell implements ShellCommandsProvider { private static final String LOCAL_FILES_BASE_DIR = System.getProperty("pulsar.shell.working.dir"); @@ -65,25 +69,43 @@ static File resolveLocalFile(String input, String baseDir) { return file; } + private interface RunnableWithResult extends Callable { + boolean run() throws Exception; - @Getter - @Parameters - public static class Params { + // Picocli entrypoint. + @Override + default Integer call() throws Exception { + if (run()) { + return 0; + } + return 1; + } + } - @Parameter(names = {"-h", "--help"}, help = true, description = "Show this help.") - boolean help; + // Must be a public modifier. + public class ConfigNameCompletionCandidates implements Iterable { + @SneakyThrows + @Override + public Iterator iterator() { + return pulsarShell.getConfigStore().listConfigs().stream().map(ConfigEntry::getName).iterator(); + } } - private interface RunnableWithResult { - boolean run() throws Exception; + static class ConfigFileCompletionCandidates implements Iterable { + @Override + public Iterator iterator() { + String path = ConfigShell.resolveLocalFile(".").toPath().toString(); + ArrayList strings = new ArrayList<>(); + strings.add(path); + return strings.iterator(); + } } - private JCommander jcommander; - private Params params; private final PulsarShell pulsarShell; - private final Map commands = new HashMap<>(); private final ConfigStore configStore; private final ObjectMapper writer = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + private final CommandLine commander = new CommandLine(this, new InnerClassFactory(this)); + @Getter private String currentConfig; @@ -91,6 +113,15 @@ public ConfigShell(PulsarShell pulsarShell, String currentConfig) { this.configStore = pulsarShell.getConfigStore(); this.pulsarShell = pulsarShell; this.currentConfig = currentConfig; + commander.addSubcommand("list", new CmdConfigList()); + commander.addSubcommand("create", new CmdConfigCreate()); + commander.addSubcommand("clone", new CmdConfigClone()); + commander.addSubcommand("update", new CmdConfigUpdate()); + commander.addSubcommand("delete", new CmdConfigDelete()); + commander.addSubcommand("use", new CmdConfigUse()); + commander.addSubcommand("view", new CmdConfigView()); + commander.addSubcommand("set-property", new CmdConfigSetProperty()); + commander.addSubcommand("get-property", new CmdConfigGetProperty()); } @Override @@ -109,68 +140,11 @@ public String getAdminUrl() { } @Override - public void setupState(Properties properties) { - - this.params = new Params(); - this.jcommander = new JCommander(); - jcommander.addObject(params); - - commands.put("list", new CmdConfigList()); - commands.put("create", new CmdConfigCreate()); - commands.put("clone", new CmdConfigClone()); - commands.put("update", new CmdConfigUpdate()); - commands.put("delete", new CmdConfigDelete()); - commands.put("use", new CmdConfigUse()); - commands.put("view", new CmdConfigView()); - commands.put("set-property", new CmdConfigSetProperty()); - commands.put("get-property", new CmdConfigGetProperty()); - commands.forEach((k, v) -> jcommander.addCommand(k, v)); - } - - @Override - public void cleanupState(Properties properties) { - setupState(properties); - } - - @Override - public JCommander getJCommander() { - return jcommander; + public CommandLine getCommander() { + return commander; } - @Override - public boolean runCommand(String[] args) throws Exception { - try { - jcommander.parse(args); - - if (params.help) { - jcommander.usage(); - return true; - } - - String chosenCommand = jcommander.getParsedCommand(); - final RunnableWithResult command = commands.get(chosenCommand); - if (command == null) { - jcommander.usage(); - return false; - } - return command.run(); - } catch (Throwable e) { - jcommander.getConsole().println(e.getMessage()); - String chosenCommand = jcommander.getParsedCommand(); - if (e instanceof ParameterException) { - try { - jcommander.getUsageFormatter().usage(chosenCommand); - } catch (ParameterException noCmd) { - e.printStackTrace(); - } - } else { - e.printStackTrace(); - } - return false; - } - } - - @Parameters(commandDescription = "List configurations") + @Command(description = "List configurations") private class CmdConfigList implements RunnableWithResult { @Override @@ -194,10 +168,10 @@ private String formatEntry(ConfigStore.ConfigEntry entry) { } } - @Parameters(commandDescription = "Use the configuration for next commands") + @Command(description = "Use the configuration for next commands") private class CmdConfigUse implements RunnableWithResult { - @Parameter(description = "Name of the config", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) private String name; @Override @@ -218,10 +192,10 @@ public boolean run() { } } - @Parameters(commandDescription = "View configuration") + @Command(description = "View configuration") private class CmdConfigView implements RunnableWithResult { - @Parameter(description = "Name of the config", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) private String name; @Override @@ -237,10 +211,10 @@ public boolean run() { } } - @Parameters(commandDescription = "Delete a configuration") + @Command(description = "Delete a configuration") private class CmdConfigDelete implements RunnableWithResult { - @Parameter(description = "Name of the config", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) private String name; @Override @@ -259,10 +233,10 @@ public boolean run() { } } - @Parameters(commandDescription = "Create a new configuration.") + @Command(name = "create", description = "Create a new configuration.") private class CmdConfigCreate extends CmdConfigPut { - @Parameter(description = "Configuration name", required = true) + @Parameters(description = "Configuration name", arity = "1") protected String name; @Override @@ -282,11 +256,11 @@ String name() { } } - @Parameters(commandDescription = "Update an existing configuration.") + @Command(description = "Update an existing configuration.") private class CmdConfigUpdate extends CmdConfigPut { - @Parameter(description = "Configuration name", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) protected String name; @Override @@ -312,14 +286,14 @@ String name() { private abstract class CmdConfigPut implements RunnableWithResult { - @Parameter(names = {"--url"}, description = "URL of the config") + @Option(names = {"--url"}, description = "URL of the config") protected String url; - @Parameter(names = {"--file"}, description = "File path of the config") - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.FILES) + @Option(names = {"--file"}, description = "File path of the config", + completionCandidates = ConfigFileCompletionCandidates.class) protected String file; - @Parameter(names = {"--value"}, description = "Inline value of the config") + @Option(names = {"--value"}, description = "Inline value of the config") protected String inlineValue; @Override @@ -372,13 +346,14 @@ public boolean run() { } + @Command private class CmdConfigClone implements RunnableWithResult { - @Parameter(description = "Configuration to clone", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Configuration to clone", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) protected String cloneFrom; - @Parameter(names = {"--name"}, description = "Name of the new config", required = true) + @Option(names = {"--name"}, description = "Name of the new config", required = true) protected String newName; @Override @@ -410,17 +385,17 @@ private void reloadIfCurrent(ConfigStore.ConfigEntry entry) throws Exception { } - @Parameters(commandDescription = "Set a configuration property by name") + @Command(description = "Set a configuration property by name") private class CmdConfigSetProperty implements RunnableWithResult { - @Parameter(description = "Name of the config", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) private String name; - @Parameter(names = {"-p", "--property"}, required = true, description = "Name of the property to update") + @Option(names = {"-p", "--property"}, required = true, description = "Name of the property to update") protected String propertyName; - @Parameter(names = {"-v", "--value"}, description = "New value for the property") + @Option(names = {"-v", "--value"}, description = "New value for the property") protected String propertyValue; @Override @@ -450,14 +425,14 @@ public boolean run() { } } - @Parameters(commandDescription = "Get a configuration property by name") + @Command(description = "Get a configuration property by name") private class CmdConfigGetProperty implements RunnableWithResult { - @Parameter(description = "Name of the config", required = true) - @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS) + @Parameters(description = "Name of the config", arity = "1", + completionCandidates = ConfigNameCompletionCandidates.class) private String name; - @Parameter(names = {"-p", "--property"}, required = true, description = "Name of the property") + @Option(names = {"-p", "--property"}, required = true, description = "Name of the property") protected String propertyName; @Override @@ -482,7 +457,6 @@ public boolean run() { } - void print(List items) { for (T item : items) { print(item); @@ -492,9 +466,9 @@ void print(List items) { void print(T item) { try { if (item instanceof String) { - jcommander.getConsole().println((String) item); + commander.getOut().println((String) item); } else { - jcommander.getConsole().println(writer.writeValueAsString(item)); + commander.getOut().println(writer.writeValueAsString(item)); } } catch (Exception e) { throw new RuntimeException(e); diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/JCommanderCompleter.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/JCommanderCompleter.java deleted file mode 100644 index 937f9cac04562..0000000000000 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/JCommanderCompleter.java +++ /dev/null @@ -1,203 +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.shell; - -import static java.lang.annotation.ElementType.FIELD; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.ParameterDescription; -import com.beust.jcommander.WrappedParameter; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.SneakyThrows; -import org.apache.pulsar.admin.cli.CmdBase; -import org.apache.pulsar.shell.config.ConfigStore; -import org.jline.builtins.Completers; -import org.jline.reader.Candidate; -import org.jline.reader.Completer; -import org.jline.reader.LineReader; -import org.jline.reader.ParsedLine; -import org.jline.reader.impl.completer.NullCompleter; -import org.jline.reader.impl.completer.StringsCompleter; - -/** - * Convert JCommander instance to JLine3 completers. - */ -public class JCommanderCompleter { - - @AllArgsConstructor - @Getter - public static class ShellContext { - private final ConfigStore configStore; - } - - private JCommanderCompleter() { - } - - public static List createCompletersForCommand(String program, - JCommander command, - ShellContext shellContext) { - command.setProgramName(program); - return createCompletersForCommand(Collections.emptyList(), - command, - Arrays.asList(NullCompleter.INSTANCE), - shellContext); - } - - private static List createCompletersForCommand(List preCompleters, - JCommander command, - List postCompleters, - ShellContext shellContext) { - List all = new ArrayList<>(); - addCompletersForCommand(preCompleters, postCompleters, all, command, shellContext); - return all; - } - - private static void addCompletersForCommand(List preCompleters, - List postCompleters, - List result, - JCommander command, - ShellContext shellContext) { - final Collection options; - final Map subCommands; - final ParameterDescription mainParameterValue; - - if (command.getObjects().get(0) instanceof CmdBase) { - CmdBase cmdBase = (CmdBase) command.getObjects().get(0); - subCommands = cmdBase.getJcommander().getCommands(); - mainParameterValue = cmdBase.getJcommander().getMainParameter() == null ? null : - cmdBase.getJcommander().getMainParameterValue(); - options = cmdBase.getJcommander().getParameters() - .stream() - .map(option -> createOptionDescriptors(option, shellContext)) - .collect(Collectors.toList()); - } else { - subCommands = command.getCommands(); - mainParameterValue = command.getMainParameter() == null ? null : command.getMainParameterValue(); - options = command.getParameters() - .stream() - .map(option -> createOptionDescriptors(option, shellContext)) - .collect(Collectors.toList()); - } - - final StringsCompleter cmdStringsCompleter = new StringsCompleter(command.getProgramName()); - - for (int i = 0; i < options.size() + 1; i++) { - List completersChain = new ArrayList<>(); - completersChain.addAll(preCompleters); - completersChain.add(cmdStringsCompleter); - for (int j = 0; j < i; j++) { - completersChain.add(new Completers.OptionCompleter(options, preCompleters.size() + 1 + j)); - } - for (Map.Entry subCommand : subCommands.entrySet()) { - addCompletersForCommand(completersChain, postCompleters, result, subCommand.getValue(), shellContext); - } - if (mainParameterValue != null) { - final Completer customCompleter = getCustomCompleter(mainParameterValue, shellContext); - if (customCompleter != null) { - completersChain.add(customCompleter); - } - } - completersChain.addAll(postCompleters); - result.add(new OptionStrictArgumentCompleter(completersChain)); - } - } - - - @SneakyThrows - private static Completers.OptDesc createOptionDescriptors(ParameterDescription param, ShellContext shellContext) { - Completer valueCompleter = getCompleter(param, shellContext); - final WrappedParameter parameter = param.getParameter(); - String shortOption = null; - String longOption = null; - final String[] parameterNames = parameter.names(); - for (String parameterName : parameterNames) { - if (parameterName.startsWith("--")) { - longOption = parameterName; - } else if (parameterName.startsWith("-")) { - shortOption = parameterName; - } - } - return new Completers.OptDesc(shortOption, longOption, param.getDescription(), valueCompleter); - } - - @SneakyThrows - private static Completer getCompleter(ParameterDescription param, ShellContext shellContext) { - - Completer valueCompleter = null; - boolean isBooleanArg = param.getObject() instanceof Boolean || param.getDefault() instanceof Boolean - || param.getObject().getClass().isAssignableFrom(Boolean.class); - if (!isBooleanArg) { - valueCompleter = getCustomCompleter(param, shellContext); - if (valueCompleter == null) { - valueCompleter = Completers.AnyCompleter.INSTANCE; - } - } - return valueCompleter; - } - - @SneakyThrows - private static Completer getCustomCompleter(ParameterDescription param, ShellContext shellContext) { - Completer valueCompleter = null; - final Field reflField = param.getParameterized().getClass().getDeclaredField("field"); - reflField.setAccessible(true); - final Field field = (Field) reflField.get(param.getParameterized()); - final ParameterCompleter parameterCompleter = field.getAnnotation(ParameterCompleter.class); - if (parameterCompleter != null) { - final ParameterCompleter.Type completer = parameterCompleter.type(); - if (completer == ParameterCompleter.Type.FILES) { - valueCompleter = new Completers.FilesCompleter(ConfigShell.resolveLocalFile(".")); - } else if (completer == ParameterCompleter.Type.CONFIGS) { - valueCompleter = new Completer() { - @Override - @SneakyThrows - public void complete(LineReader reader, ParsedLine line, List candidates) { - new StringsCompleter(shellContext.configStore.listConfigs() - .stream().map(ConfigStore.ConfigEntry::getName).collect(Collectors.toList())) - .complete(reader, line, candidates); - } - }; - } - } - return valueCompleter; - } - - @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) - @Target({ FIELD }) - public @interface ParameterCompleter { - - enum Type { - FILES, - CONFIGS; - } - - Type type(); - - } - -} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java deleted file mode 100644 index 3a088d28757d0..0000000000000 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java +++ /dev/null @@ -1,109 +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.shell; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import org.jline.builtins.Completers; -import org.jline.reader.Candidate; -import org.jline.reader.Completer; -import org.jline.reader.LineReader; -import org.jline.reader.ParsedLine; -import org.jline.reader.impl.completer.ArgumentCompleter; - -/** - * Same as {@link ArgumentCompleter} but with more strict validation for options. - */ -public class OptionStrictArgumentCompleter implements Completer { - - private final List completers = new ArrayList<>(); - /** - * Create a new completer. - * - * @param completers The embedded completers - */ - public OptionStrictArgumentCompleter(final Collection completers) { - Objects.requireNonNull(completers); - this.completers.addAll(completers); - } - - @Override - public void complete(LineReader reader, ParsedLine line, List candidates) { - Objects.requireNonNull(line); - Objects.requireNonNull(candidates); - - if (line.wordIndex() < 0) { - return; - } - - Completer completer; - - // if we are beyond the end of the completers, just use the last one - if (line.wordIndex() >= completers.size()) { - completer = completers.get(completers.size() - 1); - } else { - completer = completers.get(line.wordIndex()); - } - - - // ensure that all the previous completers are successful - // before allowing this completer to pass (only if strict). - for (int i = 0; i < line.wordIndex(); i++) { - int idx = i >= completers.size() ? (completers.size() - 1) : i; - Completer sub = completers.get(idx); - - List args = line.words(); - String arg = (args == null || i >= args.size()) ? "" : args.get(i).toString(); - - List subCandidates = new LinkedList<>(); - /* - * This is the part that differs from the original ArgumentCompleter. - * It matches only if there's an actual option. - * The implementation of OptionCompleter will return the same candidate even if it is - * not part of the options set because options are not required. - */ - if (sub instanceof Completers.OptionCompleter) { - if (arg.startsWith("-")) { - sub.complete(reader, new ArgumentCompleter.ArgumentLine(arg, arg.length()), subCandidates); - } else { - return; - } - } else { - sub.complete(reader, new ArgumentCompleter.ArgumentLine(arg, arg.length()), subCandidates); - } - - - boolean found = false; - for (Candidate cand : subCandidates) { - if (cand.value().equals(arg)) { - found = true; - break; - } - } - if (!found) { - return; - } - } - completer.complete(reader, line, candidates); - } - -} diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java index 257e00fd14242..3cc99126fb8d7 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.shell; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -28,6 +26,7 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; @@ -36,27 +35,35 @@ import java.util.Properties; import java.util.Scanner; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; +import org.apache.pulsar.internal.CommanderFactory; +import org.apache.pulsar.internal.ShellCommandsProvider; import org.apache.pulsar.shell.config.ConfigStore; import org.apache.pulsar.shell.config.FileConfigStore; -import org.jline.reader.Completer; +import org.jline.console.impl.SystemRegistryImpl; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; import org.jline.reader.impl.DefaultParser; -import org.jline.reader.impl.completer.AggregateCompleter; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.InfoCmp; +import picocli.CommandLine; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.shell.jline3.PicocliCommands; /** * Main Pulsar shell class invokable from the pulsar-shell script. */ +@Command public class PulsarShell { private static final String EXIT_MESSAGE = "Goodbye!"; @@ -87,37 +94,34 @@ public class PulsarShell { }; private static final String DEFAULT_PULSAR_SHELL_ROOT_DIRECTORY = computeDefaultPulsarShellRootDirectory(); + private SystemRegistryImpl systemRegistry; + private final DefaultParser parser = new DefaultParser().eofOnUnclosedQuote(true); + private Terminal terminal; interface Substitutor { String replace(String str, Map vars); } - static final class ShellOptions { - - @Parameter(names = {"-h", "--help"}, help = true, description = "Show this help.") - boolean help; - } - static final class MainOptions { - @Parameter(names = {"-c", "--config"}, description = "Client configuration file.") + @Option(names = {"-c", "--config"}, description = "Client configuration file.") String configFile; - @Parameter(names = {"-f", "--filename"}, description = "Input filename with a list of commands to be executed." + @Option(names = {"-f", "--filename"}, description = "Input filename with a list of commands to be executed." + " Each command must be separated by a newline.") String filename; - @Parameter(names = {"--fail-on-error"}, description = "If true, the shell will be interrupted " + @Option(names = {"--fail-on-error"}, description = "If true, the shell will be interrupted " + "if a command throws an exception.") boolean failOnError; - @Parameter(names = {"-"}, description = "Read commands from the standard input.") + @Option(names = {"-"}, description = "Read commands from the standard input.") boolean readFromStdin; - @Parameter(names = {"-e", "--execute-command"}, description = "Execute this command and exit.") + @Option(names = {"-e", "--execute-command"}, description = "Execute this command and exit.") String inlineCommand; - @Parameter(names = {"-np", "--no-progress"}, description = "Display raw output of the commands without the " + @Option(names = {"-np", "--no-progress"}, description = "Display raw output of the commands without the " + "fancy progress visualization.") boolean noProgress; } @@ -130,9 +134,10 @@ enum ExecState { @Getter private final ConfigStore configStore; private final File pulsarShellDir; - private final JCommander mainCommander; - private final MainOptions mainOptions; - private JCommander shellCommander; + private final CommandLine mainCommander; + @ArgGroup(exclusive = false) + private final MainOptions mainOptions = new MainOptions(); + private CommandLine shellCommander; private Function, InteractiveLineReader> readerBuilder; private InteractiveLineReader reader; private final ConfigShell configShell; @@ -143,15 +148,14 @@ public PulsarShell(String args[]) throws IOException { } public PulsarShell(String args[], Properties props) throws IOException { properties = props; - mainCommander = new JCommander(); - mainOptions = new MainOptions(); - mainCommander.addObject(mainOptions); + mainCommander = new CommandLine(this); + mainCommander.setCommandName("pulsar-shell"); try { - mainCommander.parse(args); + mainCommander.parseArgs(args); } catch (Exception e) { System.err.println(e.getMessage()); System.err.println(); - mainCommander.usage(); + mainCommander.usage(System.out); exit(1); throw new IllegalArgumentException(e); } @@ -217,7 +221,7 @@ public static void main(String[] args) throws Exception { public void reload(Properties properties) throws Exception { this.properties = properties; - final Map providersMap = registerProviders(shellCommander, properties); + final Map providersMap = registerProviders(properties); reader = readerBuilder.apply(providersMap); } @@ -235,19 +239,9 @@ public void run() throws Exception { }) .build(); run((providersMap) -> { - List completers = new ArrayList<>(); String serviceUrl = ""; String adminUrl = ""; - final JCommanderCompleter.ShellContext shellContext = new JCommanderCompleter.ShellContext(configStore); for (ShellCommandsProvider provider : providersMap.values()) { - provider.setupState(properties); - final JCommander jCommander = provider.getJCommander(); - if (jCommander != null) { - jCommander.createDescriptions(); - completers.addAll(JCommanderCompleter - .createCompletersForCommand(provider.getName(), jCommander, shellContext)); - } - final String providerServiceUrl = provider.getServiceUrl(); if (providerServiceUrl != null) { serviceUrl = providerServiceUrl; @@ -258,12 +252,10 @@ public void run() throws Exception { } } - Completer completer = new AggregateCompleter(completers); - LineReaderBuilder readerBuilder = LineReaderBuilder.builder() .terminal(terminal) - .parser(new DefaultParser().eofOnUnclosedQuote(true)) - .completer(completer) + .parser(parser) + .completer(systemRegistry.completer()) .variable(LineReader.INDENTATION, 2) .option(LineReader.Option.INSERT_BRACKET, true); @@ -301,7 +293,7 @@ public List parseLine(String line) { return reader.getParser().parse(line, 0).words(); } }; - }, (providerMap) -> terminal); + }, () -> terminal); } private void configureHistory(Properties properties, LineReaderBuilder readerBuilder) { @@ -341,19 +333,12 @@ interface InteractiveLineReader { } public void run(Function, InteractiveLineReader> readerBuilder, - Function, Terminal> terminalBuilder) throws Exception { + Supplier terminalBuilder) throws Exception { this.readerBuilder = readerBuilder; - /** - * Options read from the shell session - */ - shellCommander = new JCommander(); - final ShellOptions shellOptions = new ShellOptions(); - shellCommander.addObject(shellOptions); - - final Map providersMap = registerProviders(shellCommander, properties); - + this.terminal = terminalBuilder.get(); + final Map providersMap = registerProviders(properties); reader = readerBuilder.apply(providersMap); - final Terminal terminal = terminalBuilder.apply(providersMap); + final Map variables = System.getenv(); CommandReader commandReader; @@ -437,21 +422,20 @@ public List readCommand() { exit(0); return; } - if (shellOptions.help) { - shellCommander.usage(); + if (isHelp(line)) { + shellCommander.usage(System.out); continue; } final ShellCommandsProvider pulsarShellCommandsProvider = getProviderFromArgs(shellCommander, words); if (pulsarShellCommandsProvider == null) { - shellCommander.usage(); + shellCommander.usage(System.out); continue; } - String[] argv = extractAndConvertArgs(words); boolean commandOk = false; try { printExecutingCommands(terminal, commandsInfo, false); - commandOk = pulsarShellCommandsProvider.runCommand(argv); + systemRegistry.execute(line); } catch (InterruptShellException t) { // no-op } catch (Throwable t) { @@ -463,8 +447,6 @@ public List readCommand() { commandsInfo.executedCommands.add(new CommandsInfo.ExecutedCommandInfo(line, commandOk)); printExecutingCommands(terminal, commandsInfo, true); } - pulsarShellCommandsProvider.cleanupState(properties); - } if (mainOptions.failOnError && !commandOk) { exit(1); @@ -524,13 +506,13 @@ private void printExecutingCommands(Terminal terminal, } } - private static ShellCommandsProvider getProviderFromArgs(JCommander mainCommander, List words) { + private static ShellCommandsProvider getProviderFromArgs(CommandLine mainCommander, List words) { final String providerCmd = words.get(0); - final JCommander commander = mainCommander.getCommands().get(providerCmd); + final CommandLine commander = mainCommander.getSubcommands().get(providerCmd); if (commander == null) { return null; } - return (ShellCommandsProvider) commander.getObjects().get(0); + return commander.getCommand(); } private static String createPrompt(String hostname) { @@ -567,28 +549,24 @@ private static boolean isQuitCommand(String line) { return line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit"); } - private static String[] extractAndConvertArgs(List words) { - List parsed = new ArrayList<>(); - for (String s : words.subList(1, words.size())) { - if (s.startsWith("-") && s.contains("=")) { - final String[] split = s.split("=", 2); - parsed.add(split[0]); - parsed.add(split[1]); - } else { - parsed.add(s); - } - } - - String[] argv = parsed.toArray(new String[parsed.size()]); - return argv; + private static boolean isHelp(String line) { + return line.equalsIgnoreCase("help"); } - private Map registerProviders(JCommander commander, Properties properties) + private Map registerProviders(Properties properties) throws Exception { + shellCommander = CommanderFactory.createRootCommanderWithHook(this, null); final Map providerMap = new HashMap<>(); - registerProvider(createAdminShell(properties), commander, providerMap); - registerProvider(createClientShell(properties), commander, providerMap); - registerProvider(configShell, commander, providerMap); + registerProvider(createAdminShell(properties), shellCommander, providerMap); + registerProvider(createClientShell(properties), shellCommander, providerMap); + registerProvider(configShell, shellCommander, providerMap); + + Supplier workDir = () -> Paths.get(DEFAULT_PULSAR_SHELL_ROOT_DIRECTORY); + PicocliCommands picocliCommands = new PicocliCommands(shellCommander); + systemRegistry = new SystemRegistryImpl(parser, terminal, workDir, null); + systemRegistry.setCommandRegistries(picocliCommands); + systemRegistry.register("help", picocliCommands); + return providerMap; } @@ -601,11 +579,11 @@ protected ClientShell createClientShell(Properties properties) { } private static void registerProvider(ShellCommandsProvider provider, - JCommander commander, + CommandLine commander, Map providerMap) { final String name = provider.getName(); - commander.addCommand(name, provider); + commander.addSubcommand(name, provider.getCommander()); providerMap.put(name, provider); } diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdClusters.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdClusters.java index f94ae7bb9f747..bdac206cc7f12 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdClusters.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdClusters.java @@ -18,9 +18,6 @@ */ package org.apache.pulsar.admin.cli; -import com.google.common.collect.Lists; -import org.apache.pulsar.client.admin.Brokers; -import org.apache.pulsar.client.api.ProxyProtocol; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -28,16 +25,20 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.ByteArrayOutputStream; +import com.google.common.collect.Lists; import java.io.File; -import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.nio.file.Files; import java.util.List; import java.util.Map; +import lombok.Cleanup; import org.apache.pulsar.admin.cli.utils.CmdUtils; -import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.client.admin.Brokers; import org.apache.pulsar.client.admin.Clusters; import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.api.ProxyProtocol; +import org.apache.pulsar.common.policies.data.ClusterData; import org.apache.pulsar.common.util.ObjectMapperFactory; import org.assertj.core.util.Maps; import org.testng.Assert; @@ -111,17 +112,22 @@ public void testListCmd() throws Exception { CmdClusters cmd = new CmdClusters(() -> admin); - PrintStream defaultSystemOut = System.out; - try (ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(out)) { - System.setOut(ps); - cmd.run("list".split("\\s+")); - Assert.assertEquals(out.toString(), String.join("\n", clusterList) + "\n"); - out.reset(); - cmd.run("list -c".split("\\s+")); - Assert.assertEquals(out.toString(), String.join("\n", clusterResultList) + "\n"); - } finally { - System.setOut(defaultSystemOut); - } + @Cleanup + StringWriter stringWriter = new StringWriter(); + @Cleanup + PrintWriter printWriter = new PrintWriter(stringWriter); + cmd.getCommander().setOut(printWriter); + + cmd.run("list".split("\\s+")); + Assert.assertEquals(stringWriter.toString(), String.join("\n", clusterList) + "\n"); + + @Cleanup + StringWriter stringWriter1 = new StringWriter(); + @Cleanup + PrintWriter printWriter1 = new PrintWriter(stringWriter1); + cmd.getCommander().setOut(printWriter1); + cmd.run("list -c".split("\\s+")); + Assert.assertEquals(stringWriter1.toString(), String.join("\n", clusterResultList) + "\n"); } @Test diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSinks.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSinks.java index 84ababa725099..c68bbd20ab8b0 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSinks.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSinks.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.beust.jcommander.ParameterException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import java.io.Closeable; @@ -61,6 +60,7 @@ public class TestCmdSinks { private static final String CLASS_NAME = "SomeRandomClassName"; private static final String INPUTS = "test-src1,test-src2"; private static final List INPUTS_LIST; + static { INPUTS_LIST = new LinkedList<>(); INPUTS_LIST.add("test-src1"); @@ -282,7 +282,8 @@ public void testMissingProcessingGuarantees() throws Exception { ); } - @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Sink archive not specfied") + @Test(expectedExceptions = CliCommand.ParameterException.class, + expectedExceptionsMessageRegExp = "Sink archive not specfied") public void testMissingArchive() throws Exception { SinkConfig sinkConfig = getSinkConfig(); sinkConfig.setArchive(null); @@ -502,7 +503,7 @@ public void testCmdSinkConfigFileMissingResources() throws Exception { testCmdSinkConfigFile(testSinkConfig, expectedSinkConfig); } - @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Sink archive not specfied") + @Test(expectedExceptions = CliCommand.ParameterException.class, expectedExceptionsMessageRegExp = "Sink archive not specfied") public void testCmdSinkConfigFileMissingJar() throws Exception { SinkConfig testSinkConfig = getSinkConfig(); testSinkConfig.setArchive(null); @@ -522,8 +523,7 @@ public void testCmdSinkConfigFileInvalidJar() throws Exception { testCmdSinkConfigFile(testSinkConfig, expectedSinkConfig); } - @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Invalid sink type 'foo' " + - "-- Available sinks are: \\[\\]") + @Test(expectedExceptions = CliCommand.ParameterException.class, expectedExceptionsMessageRegExp = "Invalid sink type 'foo' -- Available sinks are: \\[\\]") public void testCmdSinkConfigFileInvalidSinkType() throws Exception { SinkConfig testSinkConfig = getSinkConfig(); // sinkType is prior than archive diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSources.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSources.java index c888ae6c6087d..13a632121e03e 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSources.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdSources.java @@ -18,6 +18,15 @@ */ package org.apache.pulsar.admin.cli; +import static org.apache.pulsar.common.naming.TopicName.DEFAULT_NAMESPACE; +import static org.apache.pulsar.common.naming.TopicName.PUBLIC_TENANT; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertTrue; import com.beust.jcommander.ParameterException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; @@ -41,15 +50,6 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import static org.apache.pulsar.common.naming.TopicName.DEFAULT_NAMESPACE; -import static org.apache.pulsar.common.naming.TopicName.PUBLIC_TENANT; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertTrue; public class TestCmdSources { private static final String TENANT = "test-tenant"; @@ -189,7 +189,7 @@ public void testMissingProcessingGuarantees() throws Exception { ); } - @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Source archive not specified") + @Test(expectedExceptions = CliCommand.ParameterException.class, expectedExceptionsMessageRegExp = "Source archive not specified") public void testMissingArchive() throws Exception { SourceConfig sourceConfig = getSourceConfig(); sourceConfig.setArchive(null); @@ -367,7 +367,7 @@ public void testCmdSourceConfigFileMissingResources() throws Exception { testCmdSourceConfigFile(testSourceConfig, expectedSourceConfig); } - @Test(expectedExceptions = ParameterException.class, expectedExceptionsMessageRegExp = "Source archive not specified") + @Test(expectedExceptions = CliCommand.ParameterException.class, expectedExceptionsMessageRegExp = "Source archive not specified") public void testCmdSourceConfigFileMissingJar() throws Exception { SourceConfig testSourceConfig = getSourceConfig(); testSourceConfig.setArchive(null); diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdTopics.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdTopics.java index ced1c989b118b..fc98b14392c3e 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdTopics.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/admin/cli/TestCmdTopics.java @@ -33,14 +33,14 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import com.google.common.collect.Lists; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import lombok.Cleanup; import org.apache.pulsar.client.admin.ListTopicsOptions; import org.apache.pulsar.client.admin.Lookup; import org.apache.pulsar.client.admin.PulsarAdmin; @@ -132,19 +132,19 @@ public void testListCmd() throws Exception { assertEquals(admin.topics().getList("test", TopicDomain.persistent), topicList); CmdTopics cmd = new CmdTopics(() -> admin); + @Cleanup + StringWriter stringWriter = new StringWriter(); + @Cleanup + PrintWriter printWriter = new PrintWriter(stringWriter); + cmd.getCommander().setOut(printWriter); + + cmd.run("list public/default".split("\\s+")); + Assert.assertEquals(stringWriter.toString(), String.join("\n", topicList) + "\n"); + } - PrintStream defaultSystemOut = System.out; - try (ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(out)) { - System.setOut(ps); - cmd.run("list public/default".split("\\s+")); - Assert.assertEquals(out.toString(), String.join("\n", topicList) + "\n"); - } finally { - System.setOut(defaultSystemOut); - } - } @Test public void testPartitionedLookup() throws Exception { - partitionedLookup.params = Arrays.asList("persistent://public/default/my-topic"); + partitionedLookup.topicName = "persistent://public/default/my-topic"; partitionedLookup.run(); StringBuilder topic = new StringBuilder(); topic.append(PERSISTENT_TOPIC_URL); @@ -158,7 +158,7 @@ public void testPartitionedLookup() throws Exception { @Test public void testPartitionedLookupSortByBroker() throws Exception { - partitionedLookup.params = Arrays.asList("persistent://public/default/my-topic"); + partitionedLookup.topicName = "persistent://public/default/my-topic"; partitionedLookup.run(); StringBuilder topic = new StringBuilder(); topic.append(PERSISTENT_TOPIC_URL); @@ -173,7 +173,7 @@ public void testPartitionedLookupSortByBroker() throws Exception { @Test public void testRunDeleteSingleTopic() throws PulsarAdminException, IOException { // Setup: Specify a single topic to delete - deleteCmd.params = List.of("persistent://tenant/namespace/topic"); + deleteCmd.topic = "persistent://tenant/namespace/topic"; // Act: Run the delete command deleteCmd.run(); @@ -185,7 +185,7 @@ public void testRunDeleteSingleTopic() throws PulsarAdminException, IOException @Test public void testRunDeleteMultipleTopics() throws PulsarAdminException, IOException { // Setup: Specify a regex to delete multiple topics - deleteCmd.params = List.of("persistent://tenant/namespace/.*"); + deleteCmd.topic = "persistent://tenant/namespace/.*"; deleteCmd.regex = true; // Mock: Simulate the return of multiple topics that match the regex @@ -212,7 +212,7 @@ public void testRunDeleteTopicsFromFile() throws PulsarAdminException, IOExcepti Files.write(tempFile, topics); // Setup: Specify the temporary file as input for the delete command - deleteCmd.params = List.of(tempFile.toString()); + deleteCmd.topic = tempFile.toString(); deleteCmd.readFromFile = true; // Act: Run the delete command @@ -239,7 +239,7 @@ public void testRunDeleteTopicsFromFileWithException() throws PulsarAdminExcepti Files.write(tempFile, topics); // Setup: Specify the temporary file as input for the delete command - deleteCmd.params = List.of(tempFile.toString()); + deleteCmd.topic = tempFile.toString(); deleteCmd.readFromFile = true; // Act: Run the delete command diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/AdminShellTest.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/AdminShellTest.java index 2f53546d7bb4a..cb4f63ccf75e5 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/AdminShellTest.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/AdminShellTest.java @@ -48,13 +48,15 @@ public void test() throws Exception { final PulsarAdmin admin = mock(PulsarAdmin.class); when(builder.build()).thenReturn(admin); when(admin.topics()).thenReturn(mock(Topics.class)); - adminShell.setPulsarAdminSupplier(new PulsarAdminSupplier(builder, adminShell.getRootParams())); + PulsarAdminSupplier pulsarAdminSupplier = adminShell.getPulsarAdminSupplier(); + pulsarAdminSupplier.setAdminBuilder(builder); assertTrue(run(new String[]{"topics", "list", "public/default"})); - verify(builder).build(); + verify(builder, times(1)).build(); assertTrue(run(new String[]{"topics", "list", "public/default"})); - verify(builder).build(); + verify(builder, times(1)).build(); assertTrue(run(new String[]{"--admin-url", "http://localhost:8081", "topics", "list", "public/default"})); + verify(builder, times(2)).build(); assertTrue(run(new String[]{"topics", "list", "public/default"})); verify(builder, times(3)).build(); assertTrue(run(new String[]{"--admin-url", "http://localhost:8080", @@ -62,11 +64,7 @@ public void test() throws Exception { verify(builder, times(3)).build(); } - private boolean run(String[] args) throws Exception { - try { - return adminShell.runCommand(args); - } finally { - adminShell.cleanupState(props); - } + private boolean run(String[] args) { + return adminShell.runCommand(args); } } \ No newline at end of file diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java index 18542de7d17ec..fcd856f33695d 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java @@ -28,25 +28,24 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import com.beust.jcommander.internal.Console; import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; import org.apache.pulsar.shell.config.ConfigStore; import org.apache.pulsar.shell.config.FileConfigStore; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import picocli.CommandLine; public class ConfigShellTest { private PulsarShell pulsarShell; private ConfigShell configShell; - private List output; + private String output; + private StringWriter stringWriter; @BeforeMethod(alwaysRun = true) public void before() throws Exception { @@ -59,54 +58,42 @@ public void before() throws Exception { new FileConfigStore(tempJson.toFile(), new ConfigStore.ConfigEntry(ConfigStore.DEFAULT_CONFIG, "#comment\ndefault-config=true"))); configShell = new ConfigShell(pulsarShell, ConfigStore.DEFAULT_CONFIG); - configShell.setupState(new Properties()); - output = new ArrayList<>(); setConsole(); } private void setConsole() { - configShell.getJCommander().setConsole(new Console() { - @Override - public void print(String msg) { - System.out.print("got: " + msg); - output.add(msg); - } - - @Override - public void println(String msg) { - System.out.println("got: " + msg); - output.add(msg); - } - - @Override - public char[] readPassword(boolean echoInput) { - return new char[0]; - } - }); + CommandLine commander = configShell.getCommander(); + stringWriter = new StringWriter(); + commander.setOut(new PrintWriter(stringWriter)); + } + + private void cleanOutput() { + setConsole(); + output = ""; } @Test public void testDefault() throws Exception { assertTrue(runCommand(new String[]{"list"})); - assertEquals(output, Arrays.asList("default (*)")); - output.clear(); + assertEquals(output, "default (*)\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"view", "default"})); - assertEquals(output.get(0), "default-config=true\n"); - output.clear(); + assertEquals(output, "default-config=true\n\n"); + cleanOutput(); final Path newClientConf = Files.createTempFile("client", ".conf"); assertFalse(runCommand(new String[]{"create", "default", "--file", newClientConf.toFile().getAbsolutePath()})); - assertEquals(output, Arrays.asList("Config 'default' already exists.")); - output.clear(); + assertEquals(output, "Config 'default' already exists.\n"); + cleanOutput(); assertFalse(runCommand(new String[]{"update", "default", "--file", newClientConf.toFile().getAbsolutePath()})); - assertEquals(output, Arrays.asList("'default' can't be updated.")); - output.clear(); + assertEquals(output, "'default' can't be updated.\n"); + cleanOutput(); assertFalse(runCommand(new String[]{"delete", "default"})); - assertEquals(output, Arrays.asList("'default' can't be deleted.")); + assertEquals(output, "'default' can't be deleted.\n"); } @Test @@ -119,25 +106,25 @@ public void test() throws Exception { assertTrue(runCommand(new String[]{"create", "myclient", "--file", newClientConf.toFile().getAbsolutePath()})); assertTrue(output.isEmpty()); - output.clear(); + cleanOutput(); assertNull(pulsarShell.getConfigStore().getLastUsed()); assertTrue(runCommand(new String[]{"use", "myclient"})); assertTrue(output.isEmpty()); - output.clear(); + cleanOutput(); assertEquals(pulsarShell.getConfigStore().getLastUsed(), pulsarShell.getConfigStore() .getConfig("myclient")); verify(pulsarShell).reload(any()); assertTrue(runCommand(new String[]{"list"})); - assertEquals(output, Arrays.asList("default", "myclient (*)")); - output.clear(); + assertEquals(output, "default\nmyclient (*)\n"); + cleanOutput(); assertFalse(runCommand(new String[]{"delete", "myclient"})); - assertEquals(output, Arrays.asList("'myclient' is currently used and it can't be deleted.")); - output.clear(); + assertEquals(output, "'myclient' is currently used and it can't be deleted.\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"update", "myclient", "--file", newClientConf.toFile().getAbsolutePath()})); @@ -150,9 +137,9 @@ public void test() throws Exception { verify(pulsarShell, times(2)).reload(any()); assertTrue(runCommand(new String[]{"view", "myclient-copied"})); - assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + - "=pulsar://localhost:6651/\n"); - output.clear(); + assertEquals(output, "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + + "=pulsar://localhost:6651/\n\n"); + cleanOutput(); } @Test @@ -165,65 +152,66 @@ public void testSetGetProperty() throws Exception { assertTrue(runCommand(new String[]{"create", "myclient", "--file", newClientConf.toFile().getAbsolutePath()})); assertTrue(output.isEmpty()); - output.clear(); + cleanOutput(); assertTrue(runCommand(new String[]{"use", "myclient"})); assertTrue(output.isEmpty()); - output.clear(); + cleanOutput(); assertTrue(runCommand(new String[]{"get-property", "-p", "webServiceUrl", "myclient"})); - assertEquals(output.get(0), "http://localhost:8081/"); - output.clear(); + assertEquals(output, "http://localhost:8081/\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"set-property", "-p", "newConf", "-v", "myValue", "myclient"})); verify(pulsarShell, times(2)).reload(any()); - output.clear(); + cleanOutput(); assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"})); - assertEquals(output.get(0), "myValue"); - output.clear(); + assertEquals(output, "myValue\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"view", "myclient"})); - assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + - "=pulsar://localhost:6651/\nnewConf=myValue\n"); - output.clear(); + assertEquals(output, "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + + "=pulsar://localhost:6651/\nnewConf=myValue\n\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"set-property", "-p", "newConf", "-v", "myValue2", "myclient"})); verify(pulsarShell, times(3)).reload(any()); - output.clear(); + cleanOutput(); assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"})); - assertEquals(output.get(0), "myValue2"); - output.clear(); + assertEquals(output, "myValue2\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"view", "myclient"})); - assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + - "=pulsar://localhost:6651/\nnewConf=myValue2\n"); - output.clear(); + assertEquals(output, "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + + "=pulsar://localhost:6651/\nnewConf=myValue2\n\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"set-property", "-p", "newConf", "-v", "", "myclient"})); verify(pulsarShell, times(4)).reload(any()); - output.clear(); + cleanOutput(); assertTrue(runCommand(new String[]{"view", "myclient"})); - assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + - "=pulsar://localhost:6651/\nnewConf=\n"); - output.clear(); + assertEquals(output, "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" + + "=pulsar://localhost:6651/\nnewConf=\n\n"); + cleanOutput(); assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"})); assertTrue(output.isEmpty()); - output.clear(); + cleanOutput(); } private boolean runCommand(String[] x) throws Exception { try { - return configShell.runCommand(x); + CommandLine commander = configShell.getCommander(); + return commander.execute(x) == 0; } finally { - configShell.setupState(null); + output = stringWriter.toString(); setConsole(); } } diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/JCommanderCompleterTest.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/JCommanderCompleterTest.java deleted file mode 100644 index 09981b100046e..0000000000000 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/JCommanderCompleterTest.java +++ /dev/null @@ -1,53 +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.shell; - -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.util.List; -import java.util.Properties; -import org.jline.reader.Completer; -import org.testng.annotations.Test; - -public class JCommanderCompleterTest { - - @Test - public void testCompletersAdmin() throws Exception { - final AdminShell shell = new AdminShell(new Properties()); - shell.setupState(new Properties()); - createAndCheckCompleters(shell, "admin"); - } - - @Test - public void testCompletersClient() throws Exception { - final AdminShell shell = new AdminShell(new Properties()); - shell.setupState(new Properties()); - createAndCheckCompleters(shell, "client"); - } - - private void createAndCheckCompleters(AdminShell shell, String mainCommand) { - final List completers = JCommanderCompleter.createCompletersForCommand(mainCommand, - shell.getJCommander(), null); - assertFalse(completers.isEmpty()); - for (Completer completer : completers) { - assertTrue(completer instanceof OptionStrictArgumentCompleter); - } - } -} diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/PulsarShellTest.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/PulsarShellTest.java index 2afb6f35b22c4..165fee923782b 100644 --- a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/PulsarShellTest.java +++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/PulsarShellTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -36,8 +37,8 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicReference; import lombok.SneakyThrows; -import org.apache.pulsar.admin.cli.PulsarAdminSupplier; import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.admin.PulsarAdminBuilder; import org.apache.pulsar.client.admin.Topics; import org.apache.pulsar.client.cli.CmdProduce; import org.jline.reader.EndOfFileException; @@ -99,9 +100,9 @@ public TestPulsarShell(String[] args, Properties props, PulsarAdmin pulsarAdmin) @Override protected AdminShell createAdminShell(Properties properties) throws Exception { final AdminShell adminShell = new AdminShell(properties); - final PulsarAdminSupplier supplier = mock(PulsarAdminSupplier.class); - when(supplier.get()).thenReturn(pulsarAdmin); - adminShell.setPulsarAdminSupplier(supplier); + PulsarAdminBuilder builder = mock(PulsarAdminBuilder.class); + doReturn(pulsarAdmin).when(builder).build(); + adminShell.getPulsarAdminSupplier().setAdminBuilder(builder); return adminShell; } @@ -109,9 +110,9 @@ protected AdminShell createAdminShell(Properties properties) throws Exception { protected ClientShell createClientShell(Properties properties) { final CmdProduce cmdProduce = mock(CmdProduce.class); cmdProduceHolder.set(cmdProduce); - return new ClientShell(properties) {{ - this.produceCommand = cmdProduce; - }}; + ClientShell clientShell = new ClientShell(properties); + clientShell.replaceProducerCommand(cmdProduce); + return clientShell; } @Override @@ -150,9 +151,9 @@ public void testInteractiveMode() throws Exception { linereader.addCmd("client produce -m msg my-topic"); linereader.addCmd("quit"); final TestPulsarShell testPulsarShell = new TestPulsarShell(new String[]{}, props, pulsarAdmin); - testPulsarShell.run((a) -> linereader, (a) -> terminal); + testPulsarShell.run((a) -> linereader, () -> terminal); verify(topics).createNonPartitionedTopic(eq("persistent://public/default/my-topic"), any(Map.class)); - verify(testPulsarShell.cmdProduceHolder.get()).run(); + verify(testPulsarShell.cmdProduceHolder.get()).call(); assertEquals((int) testPulsarShell.exitCode, 0); } @@ -169,9 +170,9 @@ public void testFileMode() throws Exception { final TestPulsarShell testPulsarShell = new TestPulsarShell(new String[]{"-f", shellFile}, props, pulsarAdmin); - testPulsarShell.run((a) -> linereader, (a) -> terminal); + testPulsarShell.run((a) -> linereader, () -> terminal); verify(topics).createNonPartitionedTopic(eq("persistent://public/default/my-topic"), any(Map.class)); - verify(testPulsarShell.cmdProduceHolder.get()).run(); + verify(testPulsarShell.cmdProduceHolder.get()).call(); } @Test @@ -187,7 +188,7 @@ public void testFileModeExitOnError() throws Exception { final TestPulsarShell testPulsarShell = new TestPulsarShell(new String[]{"-f", shellFile, "--fail-on-error"}, props, pulsarAdmin); try { - testPulsarShell.run((a) -> linereader, (a) -> terminal); + testPulsarShell.run((a) -> linereader, () -> terminal); fail(); } catch (SystemExitCalledException ex) { assertEquals(ex.code, 1); diff --git a/tests/integration/src/test/java/org/apache/pulsar/tests/integration/cli/CLITest.java b/tests/integration/src/test/java/org/apache/pulsar/tests/integration/cli/CLITest.java index f13a4dcfbdceb..3be15c7aee7f1 100644 --- a/tests/integration/src/test/java/org/apache/pulsar/tests/integration/cli/CLITest.java +++ b/tests/integration/src/test/java/org/apache/pulsar/tests/integration/cli/CLITest.java @@ -64,7 +64,7 @@ public void testDeprecatedCommands() throws Exception { "--allowed-clusters", pulsarCluster.getClusterName(), "--admin-roles", "admin" ); - assertTrue(result.getStderr().contains("deprecated")); + assertEquals(result.getExitCode(), 0L); result = pulsarCluster.runAdminCommandOnAnyBroker( "properties", "list"); @@ -367,7 +367,8 @@ public void testPropertiesCLI() throws Exception { "-r", ""); fail("Command should have exited with non-zero"); } catch (ContainerExecException e) { - assertEquals(e.getResult().getStderr(), "rack name is invalid, it should not be null, empty or '/'\n\n"); + assertTrue( + e.getResult().getStderr().startsWith("rack name is invalid, it should not be null, empty or '/'")); } try { @@ -377,7 +378,7 @@ public void testPropertiesCLI() throws Exception { "set-schema-autoupdate-strategy", namespace); } catch (ContainerExecException e) { - assertEquals(e.getResult().getStderr(), "Either --compatibility or --disabled must be specified\n\n"); + assertTrue(e.getResult().getStderr().startsWith("Either --compatibility or --disabled must be specified")); } } @@ -445,7 +446,7 @@ public void testSchemaCLI() throws Exception { topicName); fail("Command should have exited with non-zero"); } catch (ContainerExecException e) { - assertEquals(e.getResult().getStderr(), "Invalid schema type xml. Valid options are: avro, json\n\n"); + assertTrue(e.getResult().getStderr().startsWith("Invalid schema type xml. Valid options are: avro, json")); } }