Wednesday, August 9, 2017

Java Command-Line Interfaces (Part 11): CmdLn

This post describes using Ostermiller Java Utilities 1.08.02's CmdLn (Java Command Line Parser) to process command-line arguments from Java-based applications. The Ostermiller Java Utilities include several different types of utilities, but the focus of this post is on the "Command Line Parser" that is described on the components page, "Handle options and arguments to Java command line programs."

The Ostermiller Utilities command line parser does not use annotations, but instead employs the programmatic builder with fluent API concept that some of the other Java-based command-line parsing libraries have also used instead of annotations. The classes com.Ostermiller.util.CmdLn and com.Ostermiller.util.CmdLnOption are used together in the "definition" stage of command-line processing with CmdLn. This is demonstrated in the next code listing.

"Definition" Stage of CmdLn Processing

public static void main(final String[] arguments)
{
   final CmdLn cmdLn
      = new CmdLn(arguments).setDescription("Demonstrates CmdLn")
         .addOptions(new CmdLnOption[]{
            new CmdLnOption("help",'h'),
            new CmdLnOption("file",'f')
               .setRequiredArgument()
               .setArgumentBounds(1, 1)
               .setDescription("Path and name of file"),
            new CmdLnOption("verbose", 'v')
               .setOptionalArgument()
               .setArgumentBounds(0, 0)
               .setDescription("Enable verbosity")
         });

In the just-shown code listing, three option are defined. One option is a "help" option and the other two options (-h/--help and -v/--verbose) are those used in the other posts in this series on command-line parsing in Java. The code demonstrates that a description can be set for each option for usage/help information and it is possible to designate whether each option is required or optional. The setArgumentBounds(int,int) method is used to specify the minimum and number of arguments that are expected for each option. The file option should always have one and only one argument (the file's path and name), so its minimum and maximum are both 1. The verbosity option should have no arguments (its presence means verbosity is enabled), so its minimum and maximum number of arguments are both 0.

The Ostermiller Java Utilities command line parser provides three approaches for "defining" what's to be parsed. The approach shown above is called the "Options by Name" approach on the CmdLn page. That pages also demonstrates defining the command line options to be parsed with the "Option Enum" and "Call Back to Listeners" approaches (not demonstrated in this post).

Normally in my posts on command-line parsing with Java-based libraries, I introduce how to implement the "parsing" stage after introducing how to implement the "definition" stage. However, with Ostermiller Utilities command line parser, the "parsing" stage is implicit and so I'll return to it after first covering the "interrogation" stage.

The "interrogation" stage is implemented by calling one of the overloaded getResult() methods on the CmdLn instance. If the overloaded getResult(-) method returns null, the option was not present (or not found during parsing). If that overloaded method returns a non-null value, that value will be of type CmdLnResult and represents a parsed option. That returned instance of CmdLnResult provides methods for accessing the argument or arguments associated with the option (such as accessing the path and file name of the -f/--file option in my example). This "interrogation" is demonstrated in the next code listing.

"Interrogation" Stage with CmdLn

if(cmdLn.getResult('h') != null)
{
   cmdLn.printHelp();
   System.exit(0);
}

String fileName = null;
if(cmdLn.getResult('f') != null)
{
   fileName = cmdLn.getResult('f').getArgument();
}
else
{
   out.println("Required parameter -f|--file not provided.\n" + cmdLn.getHelp());
   System.exit(-1);
}

boolean verbose = false;
if (cmdLn.getResult('v') != null)
{
   verbose = true;
}
out.println("File path/name is " + fileName + " and verbosity is set to " + verbose);

The CmdLn class also has getResults methods that return a List<CmdLnResult> to access multiple parsed options. The examples above demonstrate checking the results of the getResult() method call for null to determine whether an option is set or not. The full source code of the Java application used for this post is available on GitHub and uses CmdLn.present(char) instead of null checks for determining presence of an option. The differences can be seen here.

The Ostermiller Java Utilities command line parser does not require an explicit "parsing" call. Instead, the "interrogation" methods previously discussed (overloaded versions of getResult and getResults) implicitly parse when called. Each of these methods calls the public method CmdLn.parse(), but parsing actually only occurs on the first one called because the instance parses a boolean value that tells that instance it does not need to parse again on subsequent calls to getResult or getResults methods.

The next three screen snapshots demonstrate use of this simple Java application using Ostermiller Java Utilities command line parser. The first image depicts the output when no arguments (including the required file path/name argument) are specified. The second image depicts the help/usage output generated when the --help or -h option is specified and shows that there is no error message about the missing parameter in this case. The third screen snapshot demonstrates "happy path" execution of the simple application using the short and long forms of the file and verbosity options.

Here are some additional characteristics of Ostermiller Java Utilities CmdLn to consider when selecting a framework or library to help with command-line parsing in Java.

  • The Ostermiller Java Utilities that CmdLn is a part of are open source and the OstermillerUtils License is the GNU General Public License version 2 (GPL). The com.Ostermiller.util License FAQ explains why the GPL was chosen and the desire to avoid having these libraries "be used in closed source applications."
  • CmdLn is part of the greater Ostermiller Java Utilities, which is available as a single JAR. The ostermillerutils-1.08.02.jar is approximately 272 KB in size and no third-party JARs are needed (no other external dependencies).
  • The Ostermiller Java Utilities requires J2SE 5 or later.
  • The Ostermiller Java Utilities have not seen updates in recent years, but the Version History details the long-term availability of these utilities.

The command line parser portion of the Ostermiller Java Utilities is easy enough to use for basic command-line parsing and gets the job done. However, the Ostermiller Java Utilities have a more restrictive license than most of the open source Java-based command-line processing libraries discussed in this series. Also, one needs to include the entire Ostermiller Java Utilities JAR to get command-line processing functionality and that may feel a bit heavy for some simple command-line-driven applications. I believe that the command line parsing utility provided by Ostermiller Java Utilities will be most attractive to developers whose applications already use the Ostermiller Java Utilities for other utilities it provides.

Additional References

No comments: