Saturday, July 8, 2017

Java Command-Line Interfaces (Part 6): JOpt Simple

The main web page for JOpt Simple calls this Java-based library "a Java library for parsing command line options, such as those you might pass to an invocation of javac," that "attempts to honor the command line option syntaxes of POSIX getopt() and GNU getopt_long()." This is the sixth post of my series of command-line arguments processing in Java and its focus is on JOpt Simple.

Most of the libraries I've reviewed in this series of command-line processing in Java take use annotations in some way. JOpt Simple, like Apache Commons CLI, does not use annotations. JOpt Simple supports "fluent interfaces" instead. This original post's examples (code listings) and output (screen snapshots) are based on compiling and running against JOpt Simple 4.9, but they have worked similarly for me (and without code changes) when compiling them and running them with JOpt Simple 5.0.3.

The next code listing demonstrates the "definition" stage of command-line processing with JOpt Simple and this example is intentionally similar to that used in the previous posts on command-line processing in Java.

Defining Command-line Options in JOpt Simple

final OptionParser optionParser = new OptionParser();
final String[] fileOptions = {"f", "file"};
optionParser.acceptsAll(Arrays.asList(fileOptions), "Path and name of file.").withRequiredArg().required();
final String[] verboseOptions = {"v", "verbose"};
optionParser.acceptsAll(Arrays.asList(verboseOptions), "Verbose logging.");
final String[] helpOptions = {"h", "help"};
optionParser.acceptsAll(Arrays.asList(helpOptions), "Display help/usage information").forHelp();

This code listing demonstrates use of the "fluent API" approach to defining command-line options. An OptionParser is instantiated and then one of its overloaded acceptsAll methods is called for each potential command-line option. The use of acceptsAll allows multiple flag/option names to be associated with a single option. This support for option synonyms allows for use of "-f" and "--file" for the same option.

The code above demonstrates that a command-line option can be specified as required with the .required() method invocation. In this case, a "file" is required. If an argument is expected to be placed on the command line in association with the option/flag, the withRequiredArg() method can be used. The "help" option in the above code listing takes advantage of the forHelp() method to tell JOpt Simple to not throw an exception if a required option is not on the command-line if the option associated with the forHelp() is on the command-line. This works, in my example, to ensure that the operator could run the application with -h or --help and without any other required option and avoid an exception being thrown.

The JOpt Simple Examples of Usage page provides significant details about the many different possibilities available when defining command-line options and uses JUnit-based assertions to demonstrate how these different tactics for defining command-line options configure differently what is parsed. My code listing shown above only shows a minor subset of what's available. Note that the Javadoc comments for the OptionParser class also contain significant details.

The code above can be even more concise if one statically imports the Arrays.asList and passes the potential command-line options' names as strings directly to that asList(String...) method instead of using the approach I used of creating an array of Strings first and then converting them to a list. I used this approach in this introductory post to make it very clear what was happening, but it's likely that the version of the code associated with this post on GitHub will be changed to use the static import approach.

The "parsing" stage of command-line processing with JOpt Simple is, well, simple:

final OptionSet options = optionParser.parse(arguments);

"Parsing" with JOpt Simple entails invocation of the method OptionParser.parse(String ...)

The "interrogation" stage of command-line processing with JOpt Simple is also simple and is demonstrated in the next code listing.

out.println("Will write to file " + options.valueOf("file") + " and verbosity is set to " + options.has("verbose"));

The single line of code demonstrates that interrogation consists of calling convenient methods on the instance of OptionSet returned by the parsing call. In this case, two demonstrated methods called on OptionSet are OptionSet.valueOf(String) and OptionSet.has(String).

JOpt Simple also supports automatic generation of a usage/help statement. The next code listing demonstrates doing this.

optionParser.printHelpOn(out);

The single line of code just shown writes the usage/help information generated by the instance of OptionParser to the output stream provided to it via its printHelpOn(OutputStream) method.

With the most significant code needed for applying JOpt Simple shown, it's time to see how the simple application that uses this code behaves. The following screen snapshots demonstrate the code in action. The first screen snapshot demonstrates the MissingRequiredOptionsException printed when the required "file" command-line option is not provided.

The next screen snapshot demonstrates specifying the "file" and "verbose" options on the command lines.

The automatic usage/help message provided by JOpt Simple is demonstrated in the next screen snapshot.

Here are some additional characteristics of Apache Commons CLI to consider when selecting a framework or library to help with command-line parsing in Java.

  • JOpt Simple is open source and is licensed under the MIT License.
  • As of this writing, the latest versions of JOpt Simple are 5.0.3 and 6.0 Alpha 1; JOpt Simple 4.9 (latest version currently listed in the JOpt Simple change log and version currently shown in Maven dependency example) was used in this post.
  • The jopt-simple-4.9.jar is approximately 65 KB in size and has no compile-time dependencies on any third-party libraries.
  • JOpt Simple has been or is used by several influential libraries and frameworks. These include Spring Framework (optional compile dependency) and JMH (compile dependency).
    • The main page for the JOpt Simple web page quotes Mark Reinhold, "I thought you might be interested to know that we're using your jopt-simple library in the open-source Java Development Kit. Thanks for writing such a nice little library! It's far cleaner than any of the other alternatives out there."
  • JOpt Simple has been available for several years, but appears to still be maintained (latest on Maven Central is December 2016).
  • JOpt Simple does not use annotations and instead relies on fluent API calls.
  • JOpt Simple supports relationships between command-line options such as required dependent options.

It is typically a positive sign of a library's usefulness when other well-received and useful tools and libraries make use of that library. JOpt Simple's selection as the command-line processing library of choice for some such tools and libraries definitely speaks well of JOpt Simple. JOpt Simple provides a useful and powerful alternative to Apache Commons CLI for those who prefer Java command-line processing that does not use annotations. JOpt Simple provides significant more capability than that shown in this post and this capability is best discovered by reading the unit test-based "tour through JOpt Simple's features."

Additional References

1 comment:

@DustinMarx said...

JDK-8203891 ["Upgrade JOpt Simple to 5.0.4"] proposes to upgrade the version of JOpt Simple used by OpenJDK to JOpt Simple 5.0.4. A high-level description of the effect of this upgrade on OpenJDK is provided in the OpenJDK mailing list message "RFR: JDK-8203891: Upgrade JOpt Simple to 5.0.4".