Skip to content

Commit 5e02d3a

Browse files
committed
Switch from Commons CLI to JCommander.
JCommander has a simpler API and better documentation. It is slightly more code (this change adds about 37K to the invoker jar). Also reorganize the Invoker class with a more usual structure. As a consequence of this change, command line options are spelled e.g. --port rather than -port. JCommander would support either (or indeed both) but this is more in line with what people expect. PiperOrigin-RevId: 293053482
1 parent 149b079 commit 5e02d3a

File tree

3 files changed

+75
-55
lines changed

3 files changed

+75
-55
lines changed

invoker/core/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@
9797
<version>9.4.11.v20180605</version>
9898
</dependency>
9999
<dependency>
100-
<groupId>commons-cli</groupId>
101-
<artifactId>commons-cli</artifactId>
102-
<version>1.4</version>
100+
<groupId>com.beust</groupId>
101+
<artifactId>jcommander</artifactId>
102+
<version>1.78</version>
103103
</dependency>
104104

105105
<!-- Test dependencies -->

invoker/core/src/main/java/com/google/cloud/functions/invoker/runner/Invoker.java

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.google.cloud.functions.invoker.runner;
22

3+
import com.beust.jcommander.JCommander;
4+
import com.beust.jcommander.Parameter;
5+
import com.beust.jcommander.ParameterException;
36
import com.google.cloud.functions.invoker.BackgroundCloudFunction;
47
import com.google.cloud.functions.invoker.BackgroundFunctionExecutor;
58
import com.google.cloud.functions.invoker.BackgroundFunctionSignatureMatcher;
@@ -24,12 +27,6 @@
2427
import java.util.logging.LogManager;
2528
import java.util.logging.Logger;
2629
import javax.servlet.http.HttpServlet;
27-
import org.apache.commons.cli.CommandLine;
28-
import org.apache.commons.cli.CommandLineParser;
29-
import org.apache.commons.cli.DefaultParser;
30-
import org.apache.commons.cli.HelpFormatter;
31-
import org.apache.commons.cli.Options;
32-
import org.apache.commons.cli.ParseException;
3330
import org.eclipse.jetty.server.Server;
3431
import org.eclipse.jetty.servlet.ServletContextHandler;
3532
import org.eclipse.jetty.servlet.ServletHolder;
@@ -62,38 +59,69 @@ public class Invoker {
6259
logger = Logger.getLogger(Invoker.class.getName());
6360
}
6461

65-
public Invoker(
66-
Integer port,
67-
String functionTarget,
68-
String functionSignatureType,
69-
Optional<String> functionJarPath) {
70-
this.port = port;
71-
this.functionTarget = functionTarget;
72-
this.functionSignatureType = functionSignatureType;
73-
this.functionJarPath = functionJarPath;
62+
private static class Options {
63+
@Parameter(
64+
description = "Port on which to listen for HTTP requests.",
65+
names = "--port"
66+
)
67+
private String port = System.getenv().getOrDefault("PORT", "8080");
68+
69+
// TODO(emcmanus): the default value here no longer makes sense and should be changed to a
70+
// class name once we have finished retiring the java8 runtime.
71+
@Parameter(
72+
description = "Name of function class to execute when servicing incoming requests.",
73+
names = "--target"
74+
)
75+
private String target =
76+
System.getenv().getOrDefault("FUNCTION_TARGET", "TestFunction.function");
77+
78+
@Parameter(
79+
description = "Name of a jar file that contains the function to execute. This must be"
80+
+ " self-contained: either it must be a \"fat jar\" which bundles the dependencies"
81+
+ " of all of the function code, or it must use the Class-Path attribute in the jar"
82+
+ " manifest to point to those dependencies.",
83+
names = "--jar"
84+
)
85+
private String jar = null;
86+
87+
@Parameter(
88+
names = "--help", help = true
89+
)
90+
private boolean help = false;
7491
}
7592

7693
public static void main(String[] args) throws Exception {
94+
Options options = new Options();
95+
JCommander jCommander = JCommander.newBuilder()
96+
.addObject(options)
97+
.build();
98+
try {
99+
jCommander.parse(args);
100+
} catch (ParameterException e) {
101+
jCommander.usage();
102+
throw e;
103+
}
77104

78-
CommandLine line = parseCommandLineOptions(args);
105+
if (options.help) {
106+
jCommander.usage();
107+
return;
108+
}
79109

80-
int port =
81-
Arrays.asList(line.getOptionValue("port"), System.getenv("PORT")).stream()
82-
.filter(Objects::nonNull)
83-
.findFirst()
84-
.map(Integer::parseInt)
85-
.orElse(8080);
86-
String functionTarget =
87-
Arrays.asList(line.getOptionValue("target"), System.getenv("FUNCTION_TARGET")).stream()
88-
.filter(Objects::nonNull)
89-
.findFirst()
90-
.orElse("TestFunction.function");
110+
int port;
111+
try {
112+
port = Integer.parseInt(options.port);
113+
} catch (NumberFormatException e) {
114+
System.err.println("--port value should be an integer: " + options.port);
115+
jCommander.usage();
116+
throw e;
117+
}
118+
String functionTarget = options.target;
91119
Path standardFunctionJarPath = Paths.get("function/function.jar");
92120
Optional<String> functionJarPath =
93121
Arrays.asList(
94-
line.getOptionValue("jar"),
95-
System.getenv("FUNCTION_JAR"),
96-
Files.exists(standardFunctionJarPath) ? standardFunctionJarPath.toString() : null)
122+
options.jar,
123+
System.getenv("FUNCTION_JAR"),
124+
Files.exists(standardFunctionJarPath) ? standardFunctionJarPath.toString() : null)
97125
.stream()
98126
.filter(Objects::nonNull)
99127
.findFirst();
@@ -111,30 +139,22 @@ private static boolean isLocalRun() {
111139
return System.getenv("K_SERVICE") == null;
112140
}
113141

114-
private static CommandLine parseCommandLineOptions(String[] args) {
115-
CommandLineParser parser = new DefaultParser();
116-
Options options = new Options();
117-
options.addOption("port", true, "the port on which server listens to HTTP requests");
118-
options.addOption("target", true, "fully qualified name of the target method to execute");
119-
options.addOption("jar", true, "path to function jar");
120-
121-
try {
122-
CommandLine line = parser.parse(options, args);
123-
return line;
124-
} catch (ParseException e) {
125-
logger.log(Level.SEVERE, "Failed to parse command line options", e);
126-
HelpFormatter formatter = new HelpFormatter();
127-
formatter.printHelp("Invoker", options);
128-
System.exit(1);
129-
}
130-
return null;
131-
}
132-
133142
private final Integer port;
134143
private final String functionTarget;
135144
private final String functionSignatureType;
136145
private final Optional<String> functionJarPath;
137146

147+
public Invoker(
148+
Integer port,
149+
String functionTarget,
150+
String functionSignatureType,
151+
Optional<String> functionJarPath) {
152+
this.port = port;
153+
this.functionTarget = functionTarget;
154+
this.functionSignatureType = functionSignatureType;
155+
this.functionJarPath = functionJarPath;
156+
}
157+
138158
public void startServer() throws Exception {
139159
Server server = new Server(port);
140160

invoker/core/src/test/java/com/google/cloud/functions/invoker/IntegrationTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,15 +316,15 @@ private String functionJarString() throws IOException {
316316
}
317317

318318
/**
319-
* Tests that if we launch an HTTP function with {@code -jar}, then the function code cannot
319+
* Tests that if we launch an HTTP function with {@code --jar}, then the function code cannot
320320
* see the classes from the runtime. This is allows us to avoid conflicts between versions of
321321
* libraries that we use in the runtime and different versions of the same libraries that the
322322
* function might use.
323323
*/
324324
@Test
325325
public void jarOptionHttp() throws Exception {
326326
testHttpFunction("com.example.functionjar.Foreground",
327-
ImmutableList.of("-jar", functionJarString()),
327+
ImmutableList.of("--jar", functionJarString()),
328328
TestCase.builder()
329329
.setUrl("/?class=" + INTERNAL_CLASS.getName())
330330
.setExpectedResponseText("OK")
@@ -342,7 +342,7 @@ public void jarOptionBackground() throws Exception {
342342
JsonObject jsonData = json.getAsJsonObject("data");
343343
jsonData.addProperty("class", INTERNAL_CLASS.getName());
344344
testBackgroundFunction("com.example.functionjar.Background",
345-
ImmutableList.of("-jar", functionJarString()),
345+
ImmutableList.of("--jar", functionJarString()),
346346
TestCase.builder().setRequestText(json.toString()).build());
347347
}
348348

0 commit comments

Comments
 (0)