This is an automated email from the ASF dual-hosted git repository.
zixuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git
The following commit(s) were added to refs/heads/master by this push:
new 567174f4352 [improve][cli] PIP-343: Use picocli instead of jcommander
in pulsar-function (#22331)
567174f4352 is described below
commit 567174f43528c0f7ae917bfb5166213973c62c29
Author: Zixuan Liu <[email protected]>
AuthorDate: Mon Mar 25 15:52:19 2024 +0800
[improve][cli] PIP-343: Use picocli instead of jcommander in
pulsar-function (#22331)
Signed-off-by: Zixuan Liu <[email protected]>
---
pulsar-functions/instance/pom.xml | 4 +-
pulsar-functions/localrun-shaded/pom.xml | 6 +-
.../org/apache/pulsar/functions/LocalRunner.java | 70 ++++++++++-----------
pulsar-functions/runtime/pom.xml | 4 +-
.../functions/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 0929d5ff210..b8d197c0683 100644
--- a/pulsar-functions/instance/pom.xml
+++ b/pulsar-functions/instance/pom.xml
@@ -153,8 +153,8 @@
</dependency>
<dependency>
- <groupId>com.beust</groupId>
- <artifactId>jcommander</artifactId>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli</artifactId>
</dependency>
<dependency>
diff --git a/pulsar-functions/localrun-shaded/pom.xml
b/pulsar-functions/localrun-shaded/pom.xml
index e8ac0f2faf8..ac075f7ee26 100644
--- a/pulsar-functions/localrun-shaded/pom.xml
+++ b/pulsar-functions/localrun-shaded/pom.xml
@@ -133,7 +133,7 @@
<include>org.rocksdb:*</include>
<include>org.eclipse.jetty*:*</include>
<include>org.apache.avro:avro</include>
- <include>com.beust:*</include>
+ <include>info.picocli:*</include>
<include>net.jodah:*</include>
<include>io.airlift:*</include>
<include>com.yahoo.datasketches:*</include>
@@ -385,8 +385,8 @@
<shadedPattern>org.apache.pulsar.shaded.com.yahoo.sketches</shadedPattern>
</relocation>
<relocation>
- <pattern>com.beust</pattern>
-
<shadedPattern>org.apache.pulsar.functions.runtime.shaded.com.beust</shadedPattern>
+ <pattern>info.picocli</pattern>
+
<shadedPattern>org.apache.pulsar.functions.runtime.shaded.info.picocli</shadedPattern>
</relocation>
<!-- Netty cannot be shaded, this is causing
java.lang.NoSuchMethodError -->
<relocation>
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 711fa33edb2..3b1c86a68c2 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 @@ package org.apache.pulsar.functions;
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.FunctionArchive;
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 @@ public class LocalRunner implements AutoCloseable {
boolean classLoaderCreated;
}
- public static class FunctionConfigConverter implements
IStringConverter<FunctionConfig> {
+ public static class FunctionConfigConverter implements
ITypeConverter<FunctionConfig> {
@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<SourceConfig> {
+ public static class SourceConfigConverter implements
ITypeConverter<SourceConfig> {
@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<SinkConfig> {
+ public static class SinkConfigConverter implements
ITypeConverter<SinkConfig> {
@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<RuntimeEnv> {
+ public static class RuntimeConverter implements ITypeConverter<RuntimeEnv>
{
@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 class LocalRunner implements AutoCloseable {
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 4c14a4302f1..ec35c3169e8 100644
--- a/pulsar-functions/runtime/pom.xml
+++ b/pulsar-functions/runtime/pom.xml
@@ -46,8 +46,8 @@
</dependency>
<dependency>
- <groupId>com.beust</groupId>
- <artifactId>jcommander</artifactId>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli</artifactId>
</dependency>
<dependency>
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 e23838cb343..06cfca6c41a 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 @@ package org.apache.pulsar.functions.runtime;
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.secretsprovider.SecretsProvider;
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 class JavaInstanceStarter implements AutoCloseable {
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 fec7b120879..2e9d6a9f27a 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.nio.file.Path;
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<Integer> {
private static final String INDENT = " ";
@@ -118,41 +121,25 @@ public class ConnectorDocGenerator {
}
}
- /**
- * 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));
}
}