You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by GitBox <gi...@apache.org> on 2022/06/27 22:45:18 UTC

[GitHub] [pulsar] nicoloboschi opened a new pull request, #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

nicoloboschi opened a new pull request, #16251:
URL: https://github.com/apache/pulsar/pull/16251

   
   Master Issue: #16250 
   
   ### Modifications
   
   * Introduced new command `bin/pulsar-shell`
     * Implemented all the basic shell features (autocompletion, persisted history, welcome, quit, ctrl+c trap)
     * Implemented wrappers for `pulsar-admin` and `pulsar-client`
   * Added JLine3 to the classpath. 
   * Refactored `pulsar-client` to use `pulsar-admin-commons.sh` since they file content are nearly equal. Now also the new script use that
   
   
   Notes:
   1. JCommander uses space as option value separator (e.g. `--admin-url http://myhost`). JLine only supports options with `=`. In order to make the completion works when using option, the last one is now accepted by the shell. The first one will works correctly but the completion won't work after that option.
   2. ZK Cli uses JLine2 - (still need to verify classpath clashes and compatibility)
   
   Missing implementations: (will follow up later)
   - non-interactive mode
   - other CLI commands
   
   ### Verifying this change
   
   This change added tests and can be verified as follows:
   
   - Build the branch
   - Run ´bin/pulsar-shell´ and try admin and client commands, check history, check autocompletion
   
   ### Documentation
   
   - [x] `doc-required` 


-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi closed pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi closed pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1
URL: https://github.com/apache/pulsar/pull/16251


-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r919153702


##########
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.")

Review Comment:
   This options already exist in `pulsar-admin` so this pull is not the right place to improve them



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917676607


##########
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:
   I copied this class from JLine3 `ArgumentCompleter` and added just few lines. I'd like to keep it as much similar as the original



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917692028


##########
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:
   it's being overriden by AdminShell class which is in the shell package



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] tisonkun commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917691492


##########
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:
   btw in the end I fixed the class



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] yuruguo commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
yuruguo commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r919140503


##########
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.")

Review Comment:
   Can we provide shorthand to make it easier to use these parameters?



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917683473


##########
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:
   not sure. It's a init method, not a reset one because the final state (after the method execution) is ready and not empty (after a reset method I expect a non initialized state)



##########
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:
   not sure. It's a init method, not a reset one because the final state (after the method execution) is ready and not empty (after a reset method I expect a non initialized state)



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi merged pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi merged PR #16251:
URL: https://github.com/apache/pulsar/pull/16251


-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917684860


##########
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:
   not sure. It's a init method, not a reset one because the final state (after the method execution) is ready and not empty (after a reset method I expect a non initialized state)



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] tisonkun commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
tisonkun commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917763256


##########
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:
   Thanks for your explanation. I mistake the package path >_<
   
   P.S. I cannot resolve this conversation but it should be resolved :P



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] tisonkun commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
tisonkun commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917344200


##########
bin/pulsar-shell:
##########
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+BINDIR=$(dirname "$0")
+export PULSAR_HOME=`cd -P $BINDIR/..;pwd`
+. "$PULSAR_HOME/bin/pulsar-admin-common.sh"
+OPTS="-Dorg.jline.terminal.jansi=false $OPTS"
+
+#Change to PULSAR_HOME to support relative paths
+cd "$PULSAR_HOME"

Review Comment:
   Well. It seems we parse other arguments in https://github.com/apache/pulsar/pull/16332/commits/58d23226f61e75682361ea6d746091efd7a606e1. Perhaps we can add these lines there but it's not a requirement.



##########
bin/pulsar-client:
##########
@@ -19,105 +19,9 @@
 #
 
 BINDIR=$(dirname "$0")
-PULSAR_HOME=`cd -P $BINDIR/..;pwd`
-
-DEFAULT_CLIENT_CONF=$PULSAR_HOME/conf/client.conf
-DEFAULT_LOG_CONF=$PULSAR_HOME/conf/log4j2.yaml
-
-if [ -f "$PULSAR_HOME/conf/pulsar_tools_env.sh" ]
-then
-    . "$PULSAR_HOME/conf/pulsar_tools_env.sh"
-fi
-
-# Check for the java to use
-if [[ -z $JAVA_HOME ]]; then
-    JAVA=$(which java)
-    if [ $? != 0 ]; then
-        echo "Error: JAVA_HOME not set, and no java executable found in $PATH." 1>&2
-        exit 1
-    fi
-else
-    JAVA=$JAVA_HOME/bin/java
-fi
-
-# exclude tests jar
-RELEASE_JAR=`ls $PULSAR_HOME/pulsar-*.jar 2> /dev/null | grep -v tests | tail -1`
-if [ $? == 0 ]; then
-    PULSAR_JAR=$RELEASE_JAR
-fi
-
-# exclude tests jar
-BUILT_JAR=`ls $PULSAR_HOME/pulsar-client-tools/target/pulsar-*.jar 2> /dev/null | grep -v tests | tail -1`
-if [ $? != 0 ] && [ ! -e "$PULSAR_JAR" ]; then
-    echo "\nCouldn't find pulsar jar.";
-    echo "Make sure you've run 'mvn package'\n";
-    exit 1;
-elif [ -e "$BUILT_JAR" ]; then
-    PULSAR_JAR=$BUILT_JAR
-fi
-
-add_maven_deps_to_classpath() {
-    MVN="mvn"
-    if [ "$MAVEN_HOME" != "" ]; then
-	MVN=${MAVEN_HOME}/bin/mvn
-    fi
-
-    # Need to generate classpath from maven pom. This is costly so generate it
-    # and cache it. Save the file into our target dir so a mvn clean will get
-    # clean it up and force us create a new one.
-    f="${PULSAR_HOME}/distribution/server/target/classpath.txt"
-    if [ ! -f "${f}" ]
-    then
-    (
-      cd "${PULSAR_HOME}"
-      ${MVN} -pl distribution/server generate-sources &> /dev/null
-    )
-    fi
-    PULSAR_CLASSPATH=${CLASSPATH}:`cat "${f}"`
-}
-
-if [ -d "$PULSAR_HOME/lib" ]; then
-	PULSAR_CLASSPATH="$PULSAR_CLASSPATH:$PULSAR_HOME/lib/*"
-else
-    add_maven_deps_to_classpath
-fi
-
-if [ -z "$PULSAR_CLIENT_CONF" ]; then
-    PULSAR_CLIENT_CONF=$DEFAULT_CLIENT_CONF
-fi
-if [ -z "$PULSAR_LOG_CONF" ]; then
-    PULSAR_LOG_CONF=$DEFAULT_LOG_CONF
-fi
-
-PULSAR_CLASSPATH="$PULSAR_JAR:$PULSAR_CLASSPATH:$PULSAR_EXTRA_CLASSPATH"
-PULSAR_CLASSPATH="`dirname $PULSAR_LOG_CONF`:$PULSAR_CLASSPATH"
-OPTS="$OPTS -Dlog4j.configurationFile=`basename $PULSAR_LOG_CONF`"
-OPTS="$OPTS -Djava.net.preferIPv4Stack=true"
-
-IS_JAVA_8=`$JAVA -version 2>&1 |grep version|grep '"1\.8'`
-# Start --add-opens options
-# '--add-opens' option is not supported in jdk8
-if [[ -z "$IS_JAVA_8" ]]; then
-  OPTS="$OPTS --add-opens java.base/sun.net=ALL-UNNAMED"
-fi
-
-OPTS="-cp $PULSAR_CLASSPATH $OPTS"
-
-OPTS="$OPTS $PULSAR_EXTRA_OPTS"
-
-# log directory & file
-PULSAR_LOG_DIR=${PULSAR_LOG_DIR:-"$PULSAR_HOME/logs"}
-PULSAR_LOG_APPENDER=${PULSAR_LOG_APPENDER:-"Console"}

Review Comment:
   Good to deduplicate shell scripts. I can see that in `pulsar-admin-common.sh` the default appender is `RoutingAppender` with `pulsar.routing.appender.default=Console`. Does it affect user experience?



##########
bin/pulsar-shell:
##########
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+BINDIR=$(dirname "$0")
+export PULSAR_HOME=`cd -P $BINDIR/..;pwd`
+. "$PULSAR_HOME/bin/pulsar-admin-common.sh"
+OPTS="-Dorg.jline.terminal.jansi=false $OPTS"
+
+#Change to PULSAR_HOME to support relative paths
+cd "$PULSAR_HOME"

Review Comment:
   Why do we need this line? It seems the `$PULSAR_CLIENT_CONF` is set as absolute path.



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] momo-jun commented on pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
momo-jun commented on PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#issuecomment-1188502366

   Hi, @nicoloboschi, will you add or update relevant docs in a follow-up PR?


-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917677614


##########
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:
   yes they are added passed to jcommand as object



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917679096


##########
bin/pulsar-shell:
##########
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+BINDIR=$(dirname "$0")
+export PULSAR_HOME=`cd -P $BINDIR/..;pwd`
+. "$PULSAR_HOME/bin/pulsar-admin-common.sh"
+OPTS="-Dorg.jline.terminal.jansi=false $OPTS"
+
+#Change to PULSAR_HOME to support relative paths
+cd "$PULSAR_HOME"

Review Comment:
   yes this will be changed in next pulls



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917681652


##########
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:
   you have to explicitly declare the assignment in the constructor to make it `final`. I wouldn't change the better methods declaration to just keep the final keyword



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917690879


##########
bin/pulsar-client:
##########
@@ -19,105 +19,9 @@
 #
 
 BINDIR=$(dirname "$0")
-PULSAR_HOME=`cd -P $BINDIR/..;pwd`
-
-DEFAULT_CLIENT_CONF=$PULSAR_HOME/conf/client.conf
-DEFAULT_LOG_CONF=$PULSAR_HOME/conf/log4j2.yaml
-
-if [ -f "$PULSAR_HOME/conf/pulsar_tools_env.sh" ]
-then
-    . "$PULSAR_HOME/conf/pulsar_tools_env.sh"
-fi
-
-# Check for the java to use
-if [[ -z $JAVA_HOME ]]; then
-    JAVA=$(which java)
-    if [ $? != 0 ]; then
-        echo "Error: JAVA_HOME not set, and no java executable found in $PATH." 1>&2
-        exit 1
-    fi
-else
-    JAVA=$JAVA_HOME/bin/java
-fi
-
-# exclude tests jar
-RELEASE_JAR=`ls $PULSAR_HOME/pulsar-*.jar 2> /dev/null | grep -v tests | tail -1`
-if [ $? == 0 ]; then
-    PULSAR_JAR=$RELEASE_JAR
-fi
-
-# exclude tests jar
-BUILT_JAR=`ls $PULSAR_HOME/pulsar-client-tools/target/pulsar-*.jar 2> /dev/null | grep -v tests | tail -1`
-if [ $? != 0 ] && [ ! -e "$PULSAR_JAR" ]; then
-    echo "\nCouldn't find pulsar jar.";
-    echo "Make sure you've run 'mvn package'\n";
-    exit 1;
-elif [ -e "$BUILT_JAR" ]; then
-    PULSAR_JAR=$BUILT_JAR
-fi
-
-add_maven_deps_to_classpath() {
-    MVN="mvn"
-    if [ "$MAVEN_HOME" != "" ]; then
-	MVN=${MAVEN_HOME}/bin/mvn
-    fi
-
-    # Need to generate classpath from maven pom. This is costly so generate it
-    # and cache it. Save the file into our target dir so a mvn clean will get
-    # clean it up and force us create a new one.
-    f="${PULSAR_HOME}/distribution/server/target/classpath.txt"
-    if [ ! -f "${f}" ]
-    then
-    (
-      cd "${PULSAR_HOME}"
-      ${MVN} -pl distribution/server generate-sources &> /dev/null
-    )
-    fi
-    PULSAR_CLASSPATH=${CLASSPATH}:`cat "${f}"`
-}
-
-if [ -d "$PULSAR_HOME/lib" ]; then
-	PULSAR_CLASSPATH="$PULSAR_CLASSPATH:$PULSAR_HOME/lib/*"
-else
-    add_maven_deps_to_classpath
-fi
-
-if [ -z "$PULSAR_CLIENT_CONF" ]; then
-    PULSAR_CLIENT_CONF=$DEFAULT_CLIENT_CONF
-fi
-if [ -z "$PULSAR_LOG_CONF" ]; then
-    PULSAR_LOG_CONF=$DEFAULT_LOG_CONF
-fi
-
-PULSAR_CLASSPATH="$PULSAR_JAR:$PULSAR_CLASSPATH:$PULSAR_EXTRA_CLASSPATH"
-PULSAR_CLASSPATH="`dirname $PULSAR_LOG_CONF`:$PULSAR_CLASSPATH"
-OPTS="$OPTS -Dlog4j.configurationFile=`basename $PULSAR_LOG_CONF`"
-OPTS="$OPTS -Djava.net.preferIPv4Stack=true"
-
-IS_JAVA_8=`$JAVA -version 2>&1 |grep version|grep '"1\.8'`
-# Start --add-opens options
-# '--add-opens' option is not supported in jdk8
-if [[ -z "$IS_JAVA_8" ]]; then
-  OPTS="$OPTS --add-opens java.base/sun.net=ALL-UNNAMED"
-fi
-
-OPTS="-cp $PULSAR_CLASSPATH $OPTS"
-
-OPTS="$OPTS $PULSAR_EXTRA_OPTS"
-
-# log directory & file
-PULSAR_LOG_DIR=${PULSAR_LOG_DIR:-"$PULSAR_HOME/logs"}
-PULSAR_LOG_APPENDER=${PULSAR_LOG_APPENDER:-"Console"}

Review Comment:
   it shouldn't. thanks for deeply checking



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] nicoloboschi commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
nicoloboschi commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917691202


##########
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:
   done



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] tisonkun commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
tisonkun commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917764324


##########
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:
   Fair enough. It's a preference about noun. I don't have a strong feeling here - you're the author and you take the name :)



##########
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:
   Fair enough. It's a preference about verb. I don't have a strong feeling here - you're the author and you take the name :)



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [pulsar] tisonkun commented on a diff in pull request #16251: [feature][cli] Pulsar Shell - pulsar-admin and pulsar-client - part 1

Posted by GitBox <gi...@apache.org>.
tisonkun commented on code in PR #16251:
URL: https://github.com/apache/pulsar/pull/16251#discussion_r917344200


##########
bin/pulsar-shell:
##########
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+BINDIR=$(dirname "$0")
+export PULSAR_HOME=`cd -P $BINDIR/..;pwd`
+. "$PULSAR_HOME/bin/pulsar-admin-common.sh"
+OPTS="-Dorg.jline.terminal.jansi=false $OPTS"
+
+#Change to PULSAR_HOME to support relative paths
+cd "$PULSAR_HOME"

Review Comment:
   Well. It seems we parse other arguments in https://github.com/apache/pulsar/pull/16332/commits/58d23226f61e75682361ea6d746091efd7a606e1. Perhaps we can add these lines there but it's OK to keep as is.



-- 
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: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org