From b39ab2bd01b9119f529c04d56aeec2d81bf05363 Mon Sep 17 00:00:00 2001 From: Zixuan Liu Date: Fri, 22 Mar 2024 21:38:00 +0800 Subject: [PATCH] [improve][cli] PIP-343: Use picocli instead of jcommander in pulsar-function Signed-off-by: Zixuan Liu --- pulsar-functions/instance/pom.xml | 4 +- pulsar-functions/localrun-shaded/pom.xml | 6 +- .../apache/pulsar/functions/LocalRunner.java | 70 +++++++++--------- pulsar-functions/runtime/pom.xml | 4 +- .../runtime/JavaInstanceStarter.java | 72 +++++++++---------- .../pulsar/io/docs/ConnectorDocGenerator.java | 57 ++++++--------- 6 files changed, 98 insertions(+), 115 deletions(-) diff --git a/pulsar-functions/instance/pom.xml b/pulsar-functions/instance/pom.xml index 0929d5ff2101b..b8d197c0683d3 100644 --- a/pulsar-functions/instance/pom.xml +++ b/pulsar-functions/instance/pom.xml @@ -153,8 +153,8 @@ - com.beust - jcommander + info.picocli + picocli diff --git a/pulsar-functions/localrun-shaded/pom.xml b/pulsar-functions/localrun-shaded/pom.xml index e8ac0f2faf814..ac075f7ee26fb 100644 --- a/pulsar-functions/localrun-shaded/pom.xml +++ b/pulsar-functions/localrun-shaded/pom.xml @@ -133,7 +133,7 @@ org.rocksdb:* org.eclipse.jetty*:* org.apache.avro:avro - com.beust:* + info.picocli:* net.jodah:* io.airlift:* com.yahoo.datasketches:* @@ -385,8 +385,8 @@ org.apache.pulsar.shaded.com.yahoo.sketches - com.beust - org.apache.pulsar.functions.runtime.shaded.com.beust + info.picocli + org.apache.pulsar.functions.runtime.shaded.info.picocli diff --git a/pulsar-functions/localrun/src/main/java/org/apache/pulsar/functions/LocalRunner.java b/pulsar-functions/localrun/src/main/java/org/apache/pulsar/functions/LocalRunner.java index 711fa33edb2a2..3b1c86a68c285 100644 --- a/pulsar-functions/localrun/src/main/java/org/apache/pulsar/functions/LocalRunner.java +++ b/pulsar-functions/localrun/src/main/java/org/apache/pulsar/functions/LocalRunner.java @@ -20,9 +20,6 @@ import static org.apache.commons.lang3.StringUtils.isNotEmpty; import static org.apache.pulsar.common.functions.Utils.inferMissingArguments; -import com.beust.jcommander.IStringConverter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParser; @@ -87,6 +84,10 @@ import org.apache.pulsar.functions.utils.functions.FunctionUtils; import org.apache.pulsar.functions.utils.io.Connector; import org.apache.pulsar.functions.utils.io.ConnectorUtils; +import picocli.CommandLine; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.Option; +import picocli.CommandLine.TypeConversionException; @Slf4j public class LocalRunner implements AutoCloseable { @@ -115,95 +116,95 @@ private static class UserCodeClassLoader { boolean classLoaderCreated; } - public static class FunctionConfigConverter implements IStringConverter { + public static class FunctionConfigConverter implements ITypeConverter { @Override public FunctionConfig convert(String value) { try { return ObjectMapperFactory.getMapper().reader().readValue(value, FunctionConfig.class); } catch (IOException e) { - throw new RuntimeException("Failed to parse function config:", e); + throw new TypeConversionException(e.getMessage()); } } } - public static class SourceConfigConverter implements IStringConverter { + public static class SourceConfigConverter implements ITypeConverter { @Override public SourceConfig convert(String value) { try { return ObjectMapperFactory.getMapper().reader().readValue(value, SourceConfig.class); } catch (IOException e) { - throw new RuntimeException("Failed to parse source config:", e); + throw new TypeConversionException(e.getMessage()); } } } - public static class SinkConfigConverter implements IStringConverter { + public static class SinkConfigConverter implements ITypeConverter { @Override public SinkConfig convert(String value) { try { return ObjectMapperFactory.getMapper().reader().readValue(value, SinkConfig.class); } catch (IOException e) { - throw new RuntimeException("Failed to parse sink config:", e); + throw new TypeConversionException(e.getMessage()); } } } - public static class RuntimeConverter implements IStringConverter { + public static class RuntimeConverter implements ITypeConverter { @Override public RuntimeEnv convert(String value) { return RuntimeEnv.valueOf(value); } } - @Parameter(names = "--functionConfig", description = "The json representation of FunctionConfig", + @Option(names = "--functionConfig", description = "The json representation of FunctionConfig", hidden = true, converter = FunctionConfigConverter.class) protected FunctionConfig functionConfig; - @Parameter(names = "--sourceConfig", description = "The json representation of SourceConfig", + @Option(names = "--sourceConfig", description = "The json representation of SourceConfig", hidden = true, converter = SourceConfigConverter.class) protected SourceConfig sourceConfig; - @Parameter(names = "--sinkConfig", description = "The json representation of SinkConfig", + @Option(names = "--sinkConfig", description = "The json representation of SinkConfig", hidden = true, converter = SinkConfigConverter.class) protected SinkConfig sinkConfig; - @Parameter(names = "--stateStorageImplClass", description = "The implemenatation class " + @Option(names = "--stateStorageImplClass", description = "The implemenatation class " + "state storage service (by default Apache BookKeeper)", hidden = true, required = false) protected String stateStorageImplClass; - @Parameter(names = "--stateStorageServiceUrl", description = "The URL for the state storage service " + @Option(names = "--stateStorageServiceUrl", description = "The URL for the state storage service " + "(by default Apache BookKeeper)", hidden = true) protected String stateStorageServiceUrl; - @Parameter(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) + @Option(names = "--brokerServiceUrl", description = "The URL for the Pulsar broker", hidden = true) protected String brokerServiceUrl; - @Parameter(names = "--webServiceUrl", description = "The URL for the Pulsar web service", hidden = true) + @Option(names = "--webServiceUrl", description = "The URL for the Pulsar web service", hidden = true) protected String webServiceUrl = null; - @Parameter(names = "--clientAuthPlugin", description = "Client authentication plugin using which " + @Option(names = "--clientAuthPlugin", description = "Client authentication plugin using which " + "function-process can connect to broker", hidden = true) protected String clientAuthPlugin; - @Parameter(names = "--clientAuthParams", description = "Client authentication param", hidden = true) + @Option(names = "--clientAuthParams", description = "Client authentication param", hidden = true) protected String clientAuthParams; - @Parameter(names = "--useTls", description = "Use tls connection\n", hidden = true, arity = 1) + @Option(names = "--useTls", description = "Use tls connection\n", hidden = true, arity = "1") protected boolean useTls; - @Parameter(names = "--tlsAllowInsecureConnection", description = "Allow insecure tls connection\n", - hidden = true, arity = 1) + @Option(names = "--tlsAllowInsecureConnection", description = "Allow insecure tls connection\n", + hidden = true, arity = "1") protected boolean tlsAllowInsecureConnection; - @Parameter(names = "--tlsHostNameVerificationEnabled", description = "Enable hostname verification", hidden = true - , arity = 1) + @Option(names = "--tlsHostNameVerificationEnabled", description = "Enable hostname verification", hidden = true + , arity = "1") protected boolean tlsHostNameVerificationEnabled; - @Parameter(names = "--tlsTrustCertFilePath", description = "tls trust cert file path", hidden = true) + @Option(names = "--tlsTrustCertFilePath", description = "tls trust cert file path", hidden = true) protected String tlsTrustCertFilePath; - @Parameter(names = "--instanceIdOffset", description = "Start the instanceIds from this offset", hidden = true) + @Option(names = "--instanceIdOffset", description = "Start the instanceIds from this offset", hidden = true) protected int instanceIdOffset = 0; - @Parameter(names = "--runtime", description = "Function runtime to use (Thread/Process)", hidden = true, + @Option(names = "--runtime", description = "Function runtime to use (Thread/Process)", hidden = true, converter = RuntimeConverter.class) protected RuntimeEnv runtimeEnv; - @Parameter(names = "--secretsProviderClassName", + @Option(names = "--secretsProviderClassName", description = "Whats the classname of secrets provider", hidden = true) protected String secretsProviderClassName; - @Parameter(names = "--secretsProviderConfig", + @Option(names = "--secretsProviderConfig", description = "Whats the config for the secrets provider", hidden = true) protected String secretsProviderConfig; - @Parameter(names = "--metricsPortStart", description = "The starting port range for metrics server. When running " + @Option(names = "--metricsPortStart", description = "The starting port range for metrics server. When running " + "instances as threads, one metrics server is used to host the stats for all instances.", hidden = true) protected Integer metricsPortStart; - @Parameter(names = "--exitOnError", description = "The starting port range for metrics server. When running " + @Option(names = "--exitOnError", description = "The starting port range for metrics server. When running " + "instances as threads, one metrics server is used to host the stats for all instances.", hidden = true) protected boolean exitOnError; @@ -212,11 +213,10 @@ public RuntimeEnv convert(String value) { public static void main(String[] args) throws Exception { LocalRunner localRunner = LocalRunner.builder().build(); - JCommander jcommander = new JCommander(localRunner); - jcommander.setProgramName("LocalRunner"); + CommandLine jcommander = new CommandLine(localRunner); + jcommander.setCommandName("LocalRunner"); - // parse args by JCommander - jcommander.parse(args); + jcommander.parseArgs(args); try { localRunner.start(true); } catch (Exception e) { diff --git a/pulsar-functions/runtime/pom.xml b/pulsar-functions/runtime/pom.xml index 4c14a4302f188..ec35c3169e814 100644 --- a/pulsar-functions/runtime/pom.xml +++ b/pulsar-functions/runtime/pom.xml @@ -46,8 +46,8 @@ - com.beust - jcommander + info.picocli + picocli diff --git a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java index e23838cb34396..06cfca6c41a2a 100644 --- a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java +++ b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java @@ -20,9 +20,6 @@ import static org.apache.pulsar.functions.utils.FunctionCommon.getSinkType; import static org.apache.pulsar.functions.utils.FunctionCommon.getSourceType; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.converters.StringConverter; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.protobuf.Empty; @@ -59,104 +56,104 @@ import org.apache.pulsar.functions.utils.FunctionCommon; import org.apache.pulsar.functions.utils.functioncache.FunctionCacheManager; import org.apache.pulsar.functions.utils.functioncache.FunctionCacheManagerImpl; +import picocli.CommandLine; +import picocli.CommandLine.Option; @Slf4j public class JavaInstanceStarter implements AutoCloseable { - @Parameter(names = "--function_details", description = "Function details json\n", required = true) + @Option(names = "--function_details", description = "Function details json\n", required = true) public String functionDetailsJsonString; - @Parameter( + @Option( names = "--jar", - description = "Path to Jar\n", - listConverter = StringConverter.class) + description = "Path to Jar\n") public String jarFile; - @Parameter( + @Option( names = "--transform_function_jar", - description = "Path to Transform Function Jar\n", - listConverter = StringConverter.class) + description = "Path to Transform Function Jar\n") public String transformFunctionJarFile; - @Parameter(names = "--instance_id", description = "Instance Id\n", required = true) + @Option(names = "--instance_id", description = "Instance Id\n", required = true) public int instanceId; - @Parameter(names = "--function_id", description = "Function Id\n", required = true) + @Option(names = "--function_id", description = "Function Id\n", required = true) public String functionId; - @Parameter(names = "--function_version", description = "Function Version\n", required = true) + @Option(names = "--function_version", description = "Function Version\n", required = true) public String functionVersion; - @Parameter(names = "--pulsar_serviceurl", description = "Pulsar Service Url\n", required = true) + @Option(names = "--pulsar_serviceurl", description = "Pulsar Service Url\n", required = true) public String pulsarServiceUrl; - @Parameter(names = "--transform_function_id", description = "Transform Function Id\n") + @Option(names = "--transform_function_id", description = "Transform Function Id\n") public String transformFunctionId; - @Parameter(names = "--client_auth_plugin", description = "Client auth plugin name\n") + @Option(names = "--client_auth_plugin", description = "Client auth plugin name\n") public String clientAuthenticationPlugin; - @Parameter(names = "--client_auth_params", description = "Client auth param\n") + @Option(names = "--client_auth_params", description = "Client auth param\n") public String clientAuthenticationParameters; - @Parameter(names = "--use_tls", description = "Use tls connection\n") + @Option(names = "--use_tls", description = "Use tls connection\n") public String useTls = Boolean.FALSE.toString(); - @Parameter(names = "--tls_allow_insecure", description = "Allow insecure tls connection\n") + @Option(names = "--tls_allow_insecure", description = "Allow insecure tls connection\n") public String tlsAllowInsecureConnection = Boolean.FALSE.toString(); - @Parameter(names = "--hostname_verification_enabled", description = "Enable hostname verification") + @Option(names = "--hostname_verification_enabled", description = "Enable hostname verification") public String tlsHostNameVerificationEnabled = Boolean.FALSE.toString(); - @Parameter(names = "--tls_trust_cert_path", description = "tls trust cert file path") + @Option(names = "--tls_trust_cert_path", description = "tls trust cert file path") public String tlsTrustCertFilePath; - @Parameter(names = "--state_storage_impl_class", description = "State Storage Service " + @Option(names = "--state_storage_impl_class", description = "State Storage Service " + "Implementation class\n", required = false) public String stateStorageImplClass; - @Parameter(names = "--state_storage_serviceurl", description = "State Storage Service Url\n", required = false) + @Option(names = "--state_storage_serviceurl", description = "State Storage Service Url\n", required = false) public String stateStorageServiceUrl; - @Parameter(names = "--port", description = "Port to listen on\n", required = true) + @Option(names = "--port", description = "Port to listen on\n", required = true) public int port; - @Parameter(names = "--metrics_port", description = "Port metrics will be exposed on\n", required = true) + @Option(names = "--metrics_port", description = "Port metrics will be exposed on\n", required = true) public int metricsPort; - @Parameter(names = "--max_buffered_tuples", description = "Maximum number of tuples to buffer\n", required = true) + @Option(names = "--max_buffered_tuples", description = "Maximum number of tuples to buffer\n", required = true) public int maxBufferedTuples; - @Parameter(names = "--expected_healthcheck_interval", description = "Expected interval in " + @Option(names = "--expected_healthcheck_interval", description = "Expected interval in " + "seconds between healtchecks", required = true) public int expectedHealthCheckInterval; - @Parameter(names = "--secrets_provider", description = "The classname of the secrets provider", required = false) + @Option(names = "--secrets_provider", description = "The classname of the secrets provider", required = false) public String secretsProviderClassName; - @Parameter(names = "--secrets_provider_config", description = "The config that needs to be " + @Option(names = "--secrets_provider_config", description = "The config that needs to be " + "passed to secrets provider", required = false) public String secretsProviderConfig; - @Parameter(names = "--cluster_name", description = "The name of the cluster this " + @Option(names = "--cluster_name", description = "The name of the cluster this " + "instance is running on", required = true) public String clusterName; - @Parameter(names = "--nar_extraction_directory", description = "The directory where " + @Option(names = "--nar_extraction_directory", description = "The directory where " + "extraction of nar packages happen", required = false) public String narExtractionDirectory = NarClassLoader.DEFAULT_NAR_EXTRACTION_DIR; - @Parameter(names = "--pending_async_requests", description = "Max pending async requests per instance", + @Option(names = "--pending_async_requests", description = "Max pending async requests per instance", required = false) public int maxPendingAsyncRequests = 1000; - @Parameter(names = "--web_serviceurl", description = "Pulsar Web Service Url", required = false) + @Option(names = "--web_serviceurl", description = "Pulsar Web Service Url", required = false) public String webServiceUrl = null; - @Parameter(names = "--expose_pulsaradmin", description = "Whether the pulsar admin client " + @Option(names = "--expose_pulsaradmin", description = "Whether the pulsar admin client " + "exposed to function context, default is disabled.", required = false) public Boolean exposePulsarAdminClientEnabled = false; - @Parameter(names = "--ignore_unknown_config_fields", + @Option(names = "--ignore_unknown_config_fields", description = "Whether to ignore unknown properties when deserializing the connector configuration.", required = false) public Boolean ignoreUnknownConfigFields = false; @@ -176,9 +173,8 @@ public void start(String[] args, ClassLoader functionInstanceClassLoader, ClassL throws Exception { Thread.currentThread().setContextClassLoader(functionInstanceClassLoader); - JCommander jcommander = new JCommander(this); - // parse args by JCommander - jcommander.parse(args); + CommandLine jcommander = new CommandLine(this); + jcommander.parseArgs(args); InstanceConfig instanceConfig = new InstanceConfig(); instanceConfig.setFunctionId(functionId); diff --git a/pulsar-io/docs/src/main/java/org/apache/pulsar/io/docs/ConnectorDocGenerator.java b/pulsar-io/docs/src/main/java/org/apache/pulsar/io/docs/ConnectorDocGenerator.java index fec7b12087977..2e9d6a9f27acc 100644 --- a/pulsar-io/docs/src/main/java/org/apache/pulsar/io/docs/ConnectorDocGenerator.java +++ b/pulsar-io/docs/src/main/java/org/apache/pulsar/io/docs/ConnectorDocGenerator.java @@ -18,8 +18,6 @@ */ package org.apache.pulsar.io.docs; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; import com.google.common.base.Strings; import java.io.File; import java.io.FileOutputStream; @@ -34,14 +32,19 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.Callable; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.io.core.annotations.Connector; import org.apache.pulsar.io.core.annotations.FieldDoc; import org.reflections.Reflections; import org.reflections.util.ConfigurationBuilder; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Slf4j -public class ConnectorDocGenerator { +@Command(name = "connector-doc-gen") +public class ConnectorDocGenerator implements Callable { private static final String INDENT = " "; @@ -118,41 +121,25 @@ private void generatorConnectorYamlFiles(String outputDir) throws IOException { } } - /** - * Args for stats generator. - */ - private static class MainArgs { - @Parameter( - names = {"-o", "--output-dir"}, - description = "The output dir to dump connector docs", - required = true) - String outputDir = null; - - @Parameter(names = {"-h", "--help"}, description = "Show this help message") - boolean help = false; - } + @Option( + names = {"-o", "--output-dir"}, + description = "The output dir to dump connector docs", + required = true) + String outputDir = null; - public static void main(String[] args) throws Exception { - MainArgs mainArgs = new MainArgs(); - - JCommander commander = new JCommander(); - try { - commander.setProgramName("connector-doc-gen"); - commander.addObject(mainArgs); - commander.parse(args); - if (mainArgs.help) { - commander.usage(); - Runtime.getRuntime().exit(0); - return; - } - } catch (Exception e) { - commander.usage(); - Runtime.getRuntime().exit(1); - return; - } + @Option(names = {"-h", "--help"}, usageHelp = true, description = "Show this help message") + boolean help = false; + @Override + public Integer call() throws Exception { ConnectorDocGenerator docGen = new ConnectorDocGenerator(); - docGen.generatorConnectorYamlFiles(mainArgs.outputDir); + docGen.generatorConnectorYamlFiles(outputDir); + return 0; + } + + public static void main(String[] args) throws Exception { + CommandLine commander = new CommandLine(new ConnectorDocGenerator()); + Runtime.getRuntime().exit(commander.execute(args)); } }