tisonkun commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917403655


##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -43,121 +44,88 @@ public class PulsarAdminTool {
 
     private static int lastExitCode = Integer.MIN_VALUE;
 
-    protected final Map<String, Class<?>> commandMap;
-    private final JCommander jcommander;
+    protected Map<String, Class<?>> commandMap;

Review Comment:
   ```suggestion
       protected final Map<String, Class<?>> commandMap;
   ```
   
   I think this map can be still `final` and we clear and put values in 
`#initJCommander`.



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -43,121 +44,88 @@ public class PulsarAdminTool {
 
     private static int lastExitCode = Integer.MIN_VALUE;
 
-    protected final Map<String, Class<?>> commandMap;
-    private final JCommander jcommander;
+    protected Map<String, Class<?>> commandMap;
+    protected JCommander jcommander;
     protected final PulsarAdminBuilder adminBuilder;
+    protected RootParams rootParams;
 
-    @Parameter(names = { "--admin-url" }, description = "Admin Service URL to 
which to connect.")
-    String serviceUrl = null;
+    @Getter
+    public static class RootParams {
 
-    @Parameter(names = { "--auth-plugin" }, description = "Authentication 
plugin class name.")
-    String authPluginClassName = null;
+        @Parameter(names = { "--admin-url" }, description = "Admin Service URL 
to which to connect.")
+        String serviceUrl = null;
 
-    @Parameter(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-plugin" }, description = "Authentication 
plugin class name.")
+        String authPluginClassName = 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\"}.")
-    String authParams = null;
+        @Parameter(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 = { "--tls-allow-insecure" }, description = "Allow TLS 
insecure connection")
-    Boolean tlsAllowInsecureConnection;
+        @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\"}.")
+        String authParams = null;
 
-    @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS 
trust cert file path")
-    String tlsTrustCertsFilePath;
+        @Parameter(names = { "--tls-allow-insecure" }, description = "Allow 
TLS insecure connection")
+        Boolean tlsAllowInsecureConnection;
 
-    @Parameter(names = { "--tls-enable-hostname-verification" }, description = 
"Enable TLS common name verification")
-    Boolean tlsEnableHostnameVerification;
+        @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow 
TLS trust cert file path")
+        String tlsTrustCertsFilePath;
 
-    @Parameter(names = { "-v", "--version" }, description = "Get version of 
pulsar admin client")
-    boolean version;
+        @Parameter(names = { "--tls-enable-hostname-verification" },
+                description = "Enable TLS common name verification")
+        Boolean tlsEnableHostnameVerification;
 
-    @Parameter(names = { "-h", "--help", }, help = true, description = "Show 
this help.")
-    boolean help;
+        @Parameter(names = { "-v", "--version" }, description = "Get version 
of pulsar admin client")
+        boolean version;
 
-    // for tls with keystore type config
-    boolean useKeyStoreTls;
-    String tlsTrustStoreType;
-    String tlsTrustStorePath;
-    String tlsTrustStorePassword;
+        @Parameter(names = { "-h", "--help", }, help = true, description = 
"Show this help.")
+        boolean help;
+    }
 
-    PulsarAdminTool(Properties properties) throws Exception {
+    public PulsarAdminTool(Properties properties) throws Exception {
+        rootParams = new RootParams();
         // fallback to previous-version serviceUrl property to maintain 
backward-compatibility
-        serviceUrl = isNotBlank(properties.getProperty("webServiceUrl"))
-                ? properties.getProperty("webServiceUrl")
-                : properties.getProperty("serviceUrl");
-        authPluginClassName = properties.getProperty("authPlugin");
-        authParams = properties.getProperty("authParams");
-        boolean tlsAllowInsecureConnection = this.tlsAllowInsecureConnection 
!= null ? this.tlsAllowInsecureConnection
+        initRootParamsFromProperties(properties);
+        adminBuilder = createAdminBuilder(properties);
+        initJCommander();
+    }
+
+    protected PulsarAdminBuilder createAdminBuilder(Properties properties) {
+        boolean useKeyStoreTls = Boolean
+                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
+        String tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
+        String tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
+        String tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
+        boolean tlsAllowInsecureConnection = 
this.rootParams.tlsAllowInsecureConnection != null
+                ? this.rootParams.tlsAllowInsecureConnection
                 : 
Boolean.parseBoolean(properties.getProperty("tlsAllowInsecureConnection", 
"false"));
 
-        boolean tlsEnableHostnameVerification = 
this.tlsEnableHostnameVerification != null
-                ? this.tlsEnableHostnameVerification
+        boolean tlsEnableHostnameVerification = 
this.rootParams.tlsEnableHostnameVerification != null
+                ? this.rootParams.tlsEnableHostnameVerification
                 : 
Boolean.parseBoolean(properties.getProperty("tlsEnableHostnameVerification", 
"false"));
-        final String tlsTrustCertsFilePath = 
isNotBlank(this.tlsTrustCertsFilePath)
-                ? this.tlsTrustCertsFilePath
+        final String tlsTrustCertsFilePath = 
isNotBlank(this.rootParams.tlsTrustCertsFilePath)
+                ? this.rootParams.tlsTrustCertsFilePath
                 : properties.getProperty("tlsTrustCertsFilePath");
 
-        this.useKeyStoreTls = Boolean
-                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
-        this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
-        this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
-        this.tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
-
-        adminBuilder = 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
+        return 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
                 .enableTlsHostnameVerification(tlsEnableHostnameVerification)
                 .tlsTrustCertsFilePath(tlsTrustCertsFilePath)
                 .useKeyStoreTls(useKeyStoreTls)
                 .tlsTrustStoreType(tlsTrustStoreType)
                 .tlsTrustStorePath(tlsTrustStorePath)
                 .tlsTrustStorePassword(tlsTrustStorePassword);
+    }
 
-        jcommander = new JCommander();
-        jcommander.setProgramName("pulsar-admin");
-        jcommander.addObject(this);
-
-        commandMap = new HashMap<>();
-        commandMap.put("clusters", CmdClusters.class);
-        commandMap.put("ns-isolation-policy", 
CmdNamespaceIsolationPolicy.class);
-        commandMap.put("brokers", CmdBrokers.class);
-        commandMap.put("broker-stats", CmdBrokerStats.class);
-        commandMap.put("tenants", CmdTenants.class);
-        commandMap.put("resourcegroups", CmdResourceGroups.class);
-        commandMap.put("properties", CmdTenants.CmdProperties.class); // 
deprecated, doesn't show in usage()
-        commandMap.put("namespaces", CmdNamespaces.class);
-        commandMap.put("topics", CmdTopics.class);
-        commandMap.put("topicPolicies", CmdTopicPolicies.class);
-        commandMap.put("schemas", CmdSchemas.class);
-        commandMap.put("bookies", CmdBookies.class);
-
-        // Hidden deprecated "persistent" and "non-persistent" subcommands
-        commandMap.put("persistent", CmdPersistentTopics.class);
-        commandMap.put("non-persistent", CmdNonPersistentTopics.class);
-
-
-        commandMap.put("resource-quotas", CmdResourceQuotas.class);
-        // pulsar-proxy cli
-        commandMap.put("proxy-stats", CmdProxyStats.class);
-
-        commandMap.put("functions", CmdFunctions.class);
-        commandMap.put("functions-worker", CmdFunctionWorker.class);
-        commandMap.put("sources", CmdSources.class);
-        commandMap.put("sinks", CmdSinks.class);
-
-        // 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);
+    protected void initRootParamsFromProperties(Properties properties) {

Review Comment:
   ```suggestion
       protected void resetRootParamsFromProperties(Properties properties) {
   ```



##########
pulsar-client-tools/src/test/java/org/apache/pulsar/shell/JCommanderCompleterTest.java:
##########
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.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 test() throws Exception {

Review Comment:
   Perhaps we can assert for `ClientShell` also?
   
   ```java
       @Test
       public void testCompletersAreOptionStrictArgumentCompleter() throws 
Exception {
           final ShellCommandsProvider[] providers = {
               new AdminShell(new Properties()),
               new ClientShell(new Properties()),
           };
           for (ShellCommandsProvider provider : providers) {
               provider.setupState(new Properties());
               final List<Completer> completers = 
JCommanderCompleter.createCompletersForCommand(
                   provider.getName(), provider.getJCommander());
               assertFalse(completers.isEmpty());
               for (Completer completer : completers) {
                   assertTrue(completer instanceof 
OptionStrictArgumentCompleter);
               }
           }
       }
   ```



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -180,11 +148,11 @@ public PulsarAdmin get() {
         }
     }
 
-    private void setupCommands(Function<PulsarAdminBuilder, ? extends 
PulsarAdmin> adminFactory) {
+    public void setupCommands(Function<PulsarAdminBuilder, ? extends 
PulsarAdmin> adminFactory) {

Review Comment:
   ```suggestion
       protected void setupCommands(Function<PulsarAdminBuilder, ? extends 
PulsarAdmin> adminFactory) {
   ```



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -367,4 +331,49 @@ static void resetLastExitCode() {
         lastExitCode = Integer.MIN_VALUE;
     }
 
+    protected void initJCommander() {

Review Comment:
   ```suggestion
       protected void resetCommanderState() {
   ```



##########
pulsar-client-tools/src/test/java/org/apache/pulsar/shell/JCommanderCompleterTest.java:
##########
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.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 test() throws Exception {
+        final AdminShell shell = new AdminShell(new Properties());
+        shell.setupState(new Properties());
+        final List<Completer> completers = 
JCommanderCompleter.createCompletersForCommand("admin",
+                shell.getJCommander());
+        assertFalse(completers.isEmpty());
+        for (Completer completer : completers) {
+            assertTrue(completer instanceof OptionStrictArgumentCompleter);
+        }
+    }
+
+}

Review Comment:
   newline?



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java:
##########
@@ -106,67 +106,82 @@ public PulsarClientTool(Properties properties) {
         this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
         this.tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
 
+        initJCommander();
+    }
+
+    protected void initJCommander() {
         produceCommand = new CmdProduce();
         consumeCommand = new CmdConsume();
         generateDocumentation = new CmdGenerateDocumentation();
 
-        this.commandParser = new JCommander();
-        this.usageFormatter = new DefaultUsageFormatter(this.commandParser);
-        commandParser.setProgramName("pulsar-client");
-        commandParser.addObject(this);
-        commandParser.addCommand("produce", produceCommand);
-        commandParser.addCommand("consume", consumeCommand);
-        commandParser.addCommand("generate_documentation", 
generateDocumentation);
+        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("generate_documentation", generateDocumentation);
+    }
+
+    protected void initRootParamsFromProperties(Properties properties) {

Review Comment:
   ```suggestion
       protected void resetRootParamsFromProperties(Properties properties) {
   ```



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -43,121 +44,88 @@ public class PulsarAdminTool {
 
     private static int lastExitCode = Integer.MIN_VALUE;
 
-    protected final Map<String, Class<?>> commandMap;
-    private final JCommander jcommander;
+    protected Map<String, Class<?>> commandMap;
+    protected JCommander jcommander;
     protected final PulsarAdminBuilder adminBuilder;
+    protected RootParams rootParams;
 
-    @Parameter(names = { "--admin-url" }, description = "Admin Service URL to 
which to connect.")
-    String serviceUrl = null;
+    @Getter
+    public static class RootParams {
 
-    @Parameter(names = { "--auth-plugin" }, description = "Authentication 
plugin class name.")
-    String authPluginClassName = null;
+        @Parameter(names = { "--admin-url" }, description = "Admin Service URL 
to which to connect.")
+        String serviceUrl = null;
 
-    @Parameter(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-plugin" }, description = "Authentication 
plugin class name.")
+        String authPluginClassName = 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\"}.")
-    String authParams = null;
+        @Parameter(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 = { "--tls-allow-insecure" }, description = "Allow TLS 
insecure connection")
-    Boolean tlsAllowInsecureConnection;
+        @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\"}.")
+        String authParams = null;
 
-    @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS 
trust cert file path")
-    String tlsTrustCertsFilePath;
+        @Parameter(names = { "--tls-allow-insecure" }, description = "Allow 
TLS insecure connection")
+        Boolean tlsAllowInsecureConnection;
 
-    @Parameter(names = { "--tls-enable-hostname-verification" }, description = 
"Enable TLS common name verification")
-    Boolean tlsEnableHostnameVerification;
+        @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow 
TLS trust cert file path")
+        String tlsTrustCertsFilePath;
 
-    @Parameter(names = { "-v", "--version" }, description = "Get version of 
pulsar admin client")
-    boolean version;
+        @Parameter(names = { "--tls-enable-hostname-verification" },
+                description = "Enable TLS common name verification")
+        Boolean tlsEnableHostnameVerification;
 
-    @Parameter(names = { "-h", "--help", }, help = true, description = "Show 
this help.")
-    boolean help;
+        @Parameter(names = { "-v", "--version" }, description = "Get version 
of pulsar admin client")
+        boolean version;
 
-    // for tls with keystore type config
-    boolean useKeyStoreTls;
-    String tlsTrustStoreType;
-    String tlsTrustStorePath;
-    String tlsTrustStorePassword;
+        @Parameter(names = { "-h", "--help", }, help = true, description = 
"Show this help.")
+        boolean help;
+    }
 
-    PulsarAdminTool(Properties properties) throws Exception {
+    public PulsarAdminTool(Properties properties) throws Exception {

Review Comment:
   ```suggestion
       PulsarAdminTool(Properties properties) throws Exception {
   ```
   
   Why `public`?



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java:
##########
@@ -106,67 +106,82 @@ public PulsarClientTool(Properties properties) {
         this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
         this.tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
 
+        initJCommander();
+    }
+
+    protected void initJCommander() {

Review Comment:
   ```suggestion
       protected void resetCommanderState() {
   ```



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java:
##########
@@ -0,0 +1,127 @@
+/**
+ * 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.Arrays;
+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<Completer> completers = new ArrayList<>();
+
+    private boolean strict = true;
+    private boolean strictCommand = true;

Review Comment:
   These fields are constant?



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -43,121 +44,88 @@ public class PulsarAdminTool {
 
     private static int lastExitCode = Integer.MIN_VALUE;
 
-    protected final Map<String, Class<?>> commandMap;
-    private final JCommander jcommander;
+    protected Map<String, Class<?>> commandMap;
+    protected JCommander jcommander;
     protected final PulsarAdminBuilder adminBuilder;
+    protected RootParams rootParams;
 
-    @Parameter(names = { "--admin-url" }, description = "Admin Service URL to 
which to connect.")
-    String serviceUrl = null;
+    @Getter
+    public static class RootParams {
 
-    @Parameter(names = { "--auth-plugin" }, description = "Authentication 
plugin class name.")
-    String authPluginClassName = null;
+        @Parameter(names = { "--admin-url" }, description = "Admin Service URL 
to which to connect.")
+        String serviceUrl = null;
 
-    @Parameter(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-plugin" }, description = "Authentication 
plugin class name.")
+        String authPluginClassName = 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\"}.")
-    String authParams = null;
+        @Parameter(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 = { "--tls-allow-insecure" }, description = "Allow TLS 
insecure connection")
-    Boolean tlsAllowInsecureConnection;
+        @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\"}.")
+        String authParams = null;
 
-    @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS 
trust cert file path")
-    String tlsTrustCertsFilePath;
+        @Parameter(names = { "--tls-allow-insecure" }, description = "Allow 
TLS insecure connection")
+        Boolean tlsAllowInsecureConnection;
 
-    @Parameter(names = { "--tls-enable-hostname-verification" }, description = 
"Enable TLS common name verification")
-    Boolean tlsEnableHostnameVerification;
+        @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow 
TLS trust cert file path")
+        String tlsTrustCertsFilePath;
 
-    @Parameter(names = { "-v", "--version" }, description = "Get version of 
pulsar admin client")
-    boolean version;
+        @Parameter(names = { "--tls-enable-hostname-verification" },
+                description = "Enable TLS common name verification")
+        Boolean tlsEnableHostnameVerification;
 
-    @Parameter(names = { "-h", "--help", }, help = true, description = "Show 
this help.")
-    boolean help;
+        @Parameter(names = { "-v", "--version" }, description = "Get version 
of pulsar admin client")
+        boolean version;
 
-    // for tls with keystore type config
-    boolean useKeyStoreTls;
-    String tlsTrustStoreType;
-    String tlsTrustStorePath;
-    String tlsTrustStorePassword;
+        @Parameter(names = { "-h", "--help", }, help = true, description = 
"Show this help.")
+        boolean help;
+    }
 
-    PulsarAdminTool(Properties properties) throws Exception {
+    public PulsarAdminTool(Properties properties) throws Exception {
+        rootParams = new RootParams();
         // fallback to previous-version serviceUrl property to maintain 
backward-compatibility
-        serviceUrl = isNotBlank(properties.getProperty("webServiceUrl"))
-                ? properties.getProperty("webServiceUrl")
-                : properties.getProperty("serviceUrl");
-        authPluginClassName = properties.getProperty("authPlugin");
-        authParams = properties.getProperty("authParams");
-        boolean tlsAllowInsecureConnection = this.tlsAllowInsecureConnection 
!= null ? this.tlsAllowInsecureConnection
+        initRootParamsFromProperties(properties);
+        adminBuilder = createAdminBuilder(properties);
+        initJCommander();
+    }
+
+    protected PulsarAdminBuilder createAdminBuilder(Properties properties) {
+        boolean useKeyStoreTls = Boolean
+                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
+        String tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
+        String tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
+        String tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
+        boolean tlsAllowInsecureConnection = 
this.rootParams.tlsAllowInsecureConnection != null
+                ? this.rootParams.tlsAllowInsecureConnection
                 : 
Boolean.parseBoolean(properties.getProperty("tlsAllowInsecureConnection", 
"false"));
 
-        boolean tlsEnableHostnameVerification = 
this.tlsEnableHostnameVerification != null
-                ? this.tlsEnableHostnameVerification
+        boolean tlsEnableHostnameVerification = 
this.rootParams.tlsEnableHostnameVerification != null
+                ? this.rootParams.tlsEnableHostnameVerification
                 : 
Boolean.parseBoolean(properties.getProperty("tlsEnableHostnameVerification", 
"false"));
-        final String tlsTrustCertsFilePath = 
isNotBlank(this.tlsTrustCertsFilePath)
-                ? this.tlsTrustCertsFilePath
+        final String tlsTrustCertsFilePath = 
isNotBlank(this.rootParams.tlsTrustCertsFilePath)
+                ? this.rootParams.tlsTrustCertsFilePath
                 : properties.getProperty("tlsTrustCertsFilePath");
 
-        this.useKeyStoreTls = Boolean
-                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
-        this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
-        this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
-        this.tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
-
-        adminBuilder = 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
+        return 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
                 .enableTlsHostnameVerification(tlsEnableHostnameVerification)
                 .tlsTrustCertsFilePath(tlsTrustCertsFilePath)
                 .useKeyStoreTls(useKeyStoreTls)
                 .tlsTrustStoreType(tlsTrustStoreType)
                 .tlsTrustStorePath(tlsTrustStorePath)
                 .tlsTrustStorePassword(tlsTrustStorePassword);
+    }
 
-        jcommander = new JCommander();
-        jcommander.setProgramName("pulsar-admin");
-        jcommander.addObject(this);
-
-        commandMap = new HashMap<>();
-        commandMap.put("clusters", CmdClusters.class);
-        commandMap.put("ns-isolation-policy", 
CmdNamespaceIsolationPolicy.class);
-        commandMap.put("brokers", CmdBrokers.class);
-        commandMap.put("broker-stats", CmdBrokerStats.class);
-        commandMap.put("tenants", CmdTenants.class);
-        commandMap.put("resourcegroups", CmdResourceGroups.class);
-        commandMap.put("properties", CmdTenants.CmdProperties.class); // 
deprecated, doesn't show in usage()
-        commandMap.put("namespaces", CmdNamespaces.class);
-        commandMap.put("topics", CmdTopics.class);
-        commandMap.put("topicPolicies", CmdTopicPolicies.class);
-        commandMap.put("schemas", CmdSchemas.class);
-        commandMap.put("bookies", CmdBookies.class);
-
-        // Hidden deprecated "persistent" and "non-persistent" subcommands
-        commandMap.put("persistent", CmdPersistentTopics.class);
-        commandMap.put("non-persistent", CmdNonPersistentTopics.class);
-
-
-        commandMap.put("resource-quotas", CmdResourceQuotas.class);
-        // pulsar-proxy cli
-        commandMap.put("proxy-stats", CmdProxyStats.class);
-
-        commandMap.put("functions", CmdFunctions.class);
-        commandMap.put("functions-worker", CmdFunctionWorker.class);
-        commandMap.put("sources", CmdSources.class);
-        commandMap.put("sinks", CmdSinks.class);
-
-        // 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);
+    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");

Review Comment:
   `serviceUrl`, `authPluginClassName `, and `authParams`  can be passed from 
command line but we never use that value?



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java:
##########
@@ -0,0 +1,127 @@
+/**
+ * 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.Arrays;
+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<Completer> completers = new ArrayList<>();
+
+    private boolean strict = true;
+    private boolean strictCommand = true;
+
+    public List<Completer> getCompleters() {

Review Comment:
   Unused.



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java:
##########
@@ -43,121 +44,88 @@ public class PulsarAdminTool {
 
     private static int lastExitCode = Integer.MIN_VALUE;
 
-    protected final Map<String, Class<?>> commandMap;
-    private final JCommander jcommander;
+    protected Map<String, Class<?>> commandMap;
+    protected JCommander jcommander;
     protected final PulsarAdminBuilder adminBuilder;
+    protected RootParams rootParams;
 
-    @Parameter(names = { "--admin-url" }, description = "Admin Service URL to 
which to connect.")
-    String serviceUrl = null;
+    @Getter
+    public static class RootParams {
 
-    @Parameter(names = { "--auth-plugin" }, description = "Authentication 
plugin class name.")
-    String authPluginClassName = null;
+        @Parameter(names = { "--admin-url" }, description = "Admin Service URL 
to which to connect.")
+        String serviceUrl = null;
 
-    @Parameter(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-plugin" }, description = "Authentication 
plugin class name.")
+        String authPluginClassName = 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\"}.")
-    String authParams = null;
+        @Parameter(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 = { "--tls-allow-insecure" }, description = "Allow TLS 
insecure connection")
-    Boolean tlsAllowInsecureConnection;
+        @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\"}.")
+        String authParams = null;
 
-    @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS 
trust cert file path")
-    String tlsTrustCertsFilePath;
+        @Parameter(names = { "--tls-allow-insecure" }, description = "Allow 
TLS insecure connection")
+        Boolean tlsAllowInsecureConnection;
 
-    @Parameter(names = { "--tls-enable-hostname-verification" }, description = 
"Enable TLS common name verification")
-    Boolean tlsEnableHostnameVerification;
+        @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow 
TLS trust cert file path")
+        String tlsTrustCertsFilePath;
 
-    @Parameter(names = { "-v", "--version" }, description = "Get version of 
pulsar admin client")
-    boolean version;
+        @Parameter(names = { "--tls-enable-hostname-verification" },
+                description = "Enable TLS common name verification")
+        Boolean tlsEnableHostnameVerification;
 
-    @Parameter(names = { "-h", "--help", }, help = true, description = "Show 
this help.")
-    boolean help;
+        @Parameter(names = { "-v", "--version" }, description = "Get version 
of pulsar admin client")
+        boolean version;
 
-    // for tls with keystore type config
-    boolean useKeyStoreTls;
-    String tlsTrustStoreType;
-    String tlsTrustStorePath;
-    String tlsTrustStorePassword;
+        @Parameter(names = { "-h", "--help", }, help = true, description = 
"Show this help.")
+        boolean help;
+    }
 
-    PulsarAdminTool(Properties properties) throws Exception {
+    public PulsarAdminTool(Properties properties) throws Exception {
+        rootParams = new RootParams();
         // fallback to previous-version serviceUrl property to maintain 
backward-compatibility
-        serviceUrl = isNotBlank(properties.getProperty("webServiceUrl"))
-                ? properties.getProperty("webServiceUrl")
-                : properties.getProperty("serviceUrl");
-        authPluginClassName = properties.getProperty("authPlugin");
-        authParams = properties.getProperty("authParams");
-        boolean tlsAllowInsecureConnection = this.tlsAllowInsecureConnection 
!= null ? this.tlsAllowInsecureConnection
+        initRootParamsFromProperties(properties);
+        adminBuilder = createAdminBuilder(properties);
+        initJCommander();
+    }
+
+    protected PulsarAdminBuilder createAdminBuilder(Properties properties) {
+        boolean useKeyStoreTls = Boolean
+                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
+        String tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
+        String tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
+        String tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
+        boolean tlsAllowInsecureConnection = 
this.rootParams.tlsAllowInsecureConnection != null
+                ? this.rootParams.tlsAllowInsecureConnection
                 : 
Boolean.parseBoolean(properties.getProperty("tlsAllowInsecureConnection", 
"false"));
 
-        boolean tlsEnableHostnameVerification = 
this.tlsEnableHostnameVerification != null
-                ? this.tlsEnableHostnameVerification
+        boolean tlsEnableHostnameVerification = 
this.rootParams.tlsEnableHostnameVerification != null
+                ? this.rootParams.tlsEnableHostnameVerification
                 : 
Boolean.parseBoolean(properties.getProperty("tlsEnableHostnameVerification", 
"false"));
-        final String tlsTrustCertsFilePath = 
isNotBlank(this.tlsTrustCertsFilePath)
-                ? this.tlsTrustCertsFilePath
+        final String tlsTrustCertsFilePath = 
isNotBlank(this.rootParams.tlsTrustCertsFilePath)
+                ? this.rootParams.tlsTrustCertsFilePath
                 : properties.getProperty("tlsTrustCertsFilePath");
 
-        this.useKeyStoreTls = Boolean
-                .parseBoolean(properties.getProperty("useKeyStoreTls", 
"false"));
-        this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", 
"JKS");
-        this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
-        this.tlsTrustStorePassword = 
properties.getProperty("tlsTrustStorePassword");
-
-        adminBuilder = 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
+        return 
PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
                 .enableTlsHostnameVerification(tlsEnableHostnameVerification)
                 .tlsTrustCertsFilePath(tlsTrustCertsFilePath)
                 .useKeyStoreTls(useKeyStoreTls)
                 .tlsTrustStoreType(tlsTrustStoreType)
                 .tlsTrustStorePath(tlsTrustStorePath)
                 .tlsTrustStorePassword(tlsTrustStorePassword);
+    }
 
-        jcommander = new JCommander();
-        jcommander.setProgramName("pulsar-admin");
-        jcommander.addObject(this);
-
-        commandMap = new HashMap<>();
-        commandMap.put("clusters", CmdClusters.class);
-        commandMap.put("ns-isolation-policy", 
CmdNamespaceIsolationPolicy.class);
-        commandMap.put("brokers", CmdBrokers.class);
-        commandMap.put("broker-stats", CmdBrokerStats.class);
-        commandMap.put("tenants", CmdTenants.class);
-        commandMap.put("resourcegroups", CmdResourceGroups.class);
-        commandMap.put("properties", CmdTenants.CmdProperties.class); // 
deprecated, doesn't show in usage()
-        commandMap.put("namespaces", CmdNamespaces.class);
-        commandMap.put("topics", CmdTopics.class);
-        commandMap.put("topicPolicies", CmdTopicPolicies.class);
-        commandMap.put("schemas", CmdSchemas.class);
-        commandMap.put("bookies", CmdBookies.class);
-
-        // Hidden deprecated "persistent" and "non-persistent" subcommands
-        commandMap.put("persistent", CmdPersistentTopics.class);
-        commandMap.put("non-persistent", CmdNonPersistentTopics.class);
-
-
-        commandMap.put("resource-quotas", CmdResourceQuotas.class);
-        // pulsar-proxy cli
-        commandMap.put("proxy-stats", CmdProxyStats.class);
-
-        commandMap.put("functions", CmdFunctions.class);
-        commandMap.put("functions-worker", CmdFunctionWorker.class);
-        commandMap.put("sources", CmdSources.class);
-        commandMap.put("sinks", CmdSinks.class);
-
-        // 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);
+    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");

Review Comment:
   OK it seems we later add `rootParams` to `jcommand.addObject` so it should 
parse. I'm unsure whether it's the case so keep this comment.



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java:
##########
@@ -0,0 +1,127 @@
+/**
+ * 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.Arrays;
+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<Completer> completers = new ArrayList<>();
+
+    private boolean strict = true;
+    private boolean strictCommand = true;
+
+    public List<Completer> getCompleters() {
+        return completers;
+    }
+
+
+    /**
+     * Create a new completer.
+     *
+     * @param completers    The embedded completers
+     */
+    public OptionStrictArgumentCompleter(final Collection<Completer> 
completers) {
+        Objects.requireNonNull(completers);
+        this.completers.addAll(completers);
+    }
+
+    public OptionStrictArgumentCompleter(final Completer... completers) {
+        this(Arrays.asList(completers));
+    }
+
+
+    @Override
+    public void complete(LineReader reader, ParsedLine line, List<Candidate> 
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 = strictCommand ? 0 : 1; strict && (i < line.wordIndex()); 
i++) {
+            int idx = i >= completers.size() ? (completers.size() - 1) : i;
+            if (idx == 0 && !strictCommand) {
+                continue;
+            }
+            Completer sub = completers.get(idx);
+
+            List<? extends CharSequence> args = line.words();
+            String arg = (args == null || i >= args.size()) ? "" : 
args.get(i).toString();
+
+            List<Candidate> subCandidates = new LinkedList<>();
+            /**

Review Comment:
   ```suggestion
               /*
   ```



##########
pulsar-client-tools/src/main/java/org/apache/pulsar/shell/OptionStrictArgumentCompleter.java:
##########
@@ -0,0 +1,127 @@
+/**
+ * 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.Arrays;
+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<Completer> completers = new ArrayList<>();
+
+    private boolean strict = true;
+    private boolean strictCommand = true;
+
+    public List<Completer> getCompleters() {
+        return completers;
+    }
+
+
+    /**
+     * Create a new completer.
+     *
+     * @param completers    The embedded completers
+     */
+    public OptionStrictArgumentCompleter(final Collection<Completer> 
completers) {
+        Objects.requireNonNull(completers);
+        this.completers.addAll(completers);
+    }
+
+    public OptionStrictArgumentCompleter(final Completer... completers) {

Review Comment:
   Unused?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to