Thursday, September 21, 2017

JDK 9 Released Today

The big news in Java today is, of course, the release of JDK 9 in General Availability. Mark Reinhold starts his message JDK 9: General Availability with the statement, "I'm pleased -- nay, thrilled! -- to announce that JDK 9 is now Generally Available."

The Reinhold post adds that in addition to Jigsaw, there are "many other excellent additions and improvements." He lists them with links that are reproduced here:

It will be interesting to see what people learn and share from using JDK 9 over the next several months. We've already seen numerous resources based on early releases of JDK 9, but I'd expect usage and consequential lessons learned to pick up with JDK 9 in General Availability. Recent posts that provide examples of this include Java 9, Jigsaw, JPMS, and Modules: A Personal Exploration and JDK 9: XXtra Command Line Options.

Additional Resources

Friday, September 8, 2017

Java Command-Line Interfaces (Part 15): Jargo

Jargo is defined on its main GitHub page as "a tool to ease the handling of program arguments/options." That page provides a Rationale for another command line processing library when so many others already exist and the top of that list is, "Because type-safety, immutability and readability matters."

Jargo's options "definition" stage uses generic typed instances of the Argument class. These instances of Argument are created via static methods on the Arguments class to establish the type and then using builder-style methods to describe the option. This is demonstrated in the next screen snapshot which depicts definition of options for file path/name and verbosity (full code listing is available on GitHub).

"Definition" Stage with Jargo

final Argument<String> filePathAndName
   = stringArgument().description("Path and name of file.")
                     .names("--file", "-f")
                     .required()
                     .build();
// Use optionArgument() instead of booleanArgument() to avoid need
// to specify true or false as arguments to --verbose/-v option
final Argument<Boolean> verbose
   = optionArgument("--verbose", "-v")
                    .description("Enables verbosity.")
                    .names("--verbose", "-v")
                    .defaultValue(false)
                    .build();

The stringArgument() and optionArgument() methods shown above are called on the statically imported (not shown) Arguments class. The optionArgument() method needed to be used for the verbosity flag to avoid being required to explicitly state true or false after the verbosity flag.

The "parsing" stage is implemented with the class CommandLineParser and its fluent API methods as shown in the next code listing.

final ParsedArguments parsedArguments
   = CommandLineParser.withArguments(filePathAndName, verbose)
                      .parse(arguments);

The instance of ParsedArguments provided by the CommandLineParser can be used for the "interrogation" stage. This is accomplished by invoking the "get" method on the ParsedArguments instance and passing it the appropriate Argument instance. The next code listing demonstrates this.

"Interrogation" Stage with Jargo

out.println("File path/name is '" + parsedArguments.get(filePathAndName)
   + "' and verbosity is set to '" + parsedArguments.get(verbose) + "'.");

The following screen snapshots depict use of Jargo. The first screen snapshot demonstrates the exception stack trace that occurs when a required option is not specified and the second screen snapshot demonstrates the long and short option names being used.

The stack trace shown in the first screen snapshot is not the nicest way to notify the user that a required option was not specified. Jargo allows a nicer message to be returned by catching the ArgumentException and calling its getMessageAndUsage() method. The code for this can be seen on GitHub and the results are shown in the next screen snapshot.

The screen snapshot demonstrates that the information provided in the instantiation of the Arguments is displayed. Jargo also allows an exception to be explicitly thrown to provide this information when a "help" argument is specified. This makes use of the static method helpArgument() on the Arguments class and an example of its usage is included in the GitHub code listing.

There are characteristics of Jargo to consider when selecting a framework or library to help with command-line parsing in Java.

  • Jargo is open source and is licensed under the Apache License, Version 2.0.
  • Jargo's jargo-0.4.1.jar is approximately 177 KB in size, but it has a runtime dependency on the much larger Guava library.
    • The Guava dependency is an intentional decision as described in Jargo's Rationale: "Because I love Guava and wanted an argument parsing library well integrated with it (more to come in this department)."
    • This is obviously not an issue for the many applications that use Guava, but could be an issue for those wanting a command line processing Java-based library for a simple application that did not otherwise use Guava.
  • Jargo uses strongly typed API invocations to programmatically configure expected command line options rather than using annotations and/or reflection.
  • In a field with so many Java-based command line processing libraries available, Jargo is most likely to be a significant contender for a developer who desires all of the attributes of a command line processing library that Jargo's Rationale lists in explanation of why another library in this crowded space was developed.

Jargo is an easy to use library for processing command line options in Java and makes use of generic-typed classes and type-specific static methods to enforce type safety of command line options. Jargo requires Guava to run and so is best suited for applications already using Guava. A developer is likely to seriously consider Jargo over other alternate Java-based command line processing libraries if the items in the Jargo Rationale are all important to that developer.

Additional References

Wednesday, September 6, 2017

Java Command-Line Interfaces (Part 14): google-options

The GitHub page for google-options states that google-options is a "command line argument parsing library from the folks at Google (java)." The page goes on to say, "This is the command-line arguments parser from the Bazel Project. The com.google.devtools.common.options package has been split out into a separate jar for general utility." This blog post demonstrates applying google-options to processing command line options from Java code.

The example used in this post to demonstrate google-options is similar to the examples used in the earlier thirteen posts in this series on processing command line options in Java. This example supports two options: a required file path/name option expecting a String argument with that path and name and a verbosity option with no argument (its existence enables verbosity). Only the most relevant portions of the code will be shown in this post, but the full code is available on GitHub.

The "definition" stage of Java-based command-line processing is accomplished in google-options via a custom class that extends google-options's class com.google.devtools.common.options.OptionsBase. The custom class should provide public fields that correspond to the expected command line options and each of these public fields should be annotated with the @Option annotation. The @Option annotation requires two elements (name and defaultValue) and allows for optional elements such as help and abbrev. An example of this "definition" stage is provided in the next code listing for the custom Options class.

"Definition" Stage with google-options: Custom Options Class

/**
 * Represents command-line options to be processed via
 * google-options library. Fields must be annotated with
 * @Option and must be public.
 */
public class Options extends OptionsBase
{
   @Option(name="file",
           abbrev='f',
           help="Path and name of file",
           category="Startup",
           defaultValue="")
   public String filePathAndName;

   @Option(name="verbose",
           abbrev='v',
           help="Enable verbose output",
           category="Startup",
           defaultValue="false")
   public boolean verbose;
}

The required "name" element of the @Option annotation specifies the "long" option name while the optional "abbrev" element specifies the "short" (single character) option name. The optional "help" element of the @Option annotation allows one to provide a "help" string for the option and the required "defaultValue" element specifies the default value for the option to be used when the option is not explicitly specified.

The "parsing" stage is accomplished in google-options by instantiating an instance of OptionsParser associated with the custom class with Option-annotated public fields and then invoking a "parse" method on that instance of OptionsParser. The next code listing demonstrates this with invocation of the OptionsParser.parseAndExitUponError(String[]) method.

"Parsing" Stage with google-options

final OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
optionsParser.parseAndExitUponError(arguments);

The "interrogation" stage with google-options is as simple as accessing the Option-annotated public fields on the custom class as demonstrated in the next code listing.

"Interrogation" Stage with google-options

out.println(
     "Path/file name is '" + options.filePathAndName
   + "' and verbosity is '" + options.verbose + "'.");

The next code listing demonstrates how the OptionsParser method describeOptions can be used for displaying help details.

Using google-options's "Help"

private static void printUsage(OptionsParser parser)
{
   out.println("Usage: java Main <Options>");
   out.println(parser.describeOptions(Collections.emptyMap(),
   OptionsParser.HelpVerbosity.LONG));
}

The following screen snapshots demonstrate the above code listings. The first image depicts the "help" information provided when no arguments are provided and the second image depicts the two main command line options expressed in various long and short forms. One interesting observation that can be made from that second image is that google-options automatically supports a --noversion option for the boolean option explicitly specifying no verbosity.

The screen snapshots also demonstrate that google-options has a dependency on Guava.

Here are characteristics of google-options to consider when selecting a framework or library to help with command-line parsing in Java.

  • google-options is an open source library licensed with the Apache License 2.0.
  • The google-options-1.0.0.jar is approximately 72 KB in size.
  • Applications using google-options also need to include Guava on the runtime classpath because google-options has a dependency on Guava. For applications using Guava anyway, this is not a big deal. However, for small applications not already using Guava, this may be more of a dependency than the application authors would like to have for a simple command-line based application.

Several of the Java-based command line processing libraries covered in this series use annotations to define the expected options and this is the approach used by google-options. Basic processing of command line options with google-options is a relatively straightforward process, but the cost of a new Guava dependency may be too high of a cost for simple applications that don't already depend on Guava.

Additional References

Saturday, August 26, 2017

Java Command-Line Interfaces (Part 13): JArgs

JArgs 1.0 has the distinction of being the subject of the 13th post of my series on parsing command line arguments in Java. JArgs is an open source (BSD license) library that has been primarily supported by different contributors including Steve Purcell and Ewan Mellor. It turns out that this can lead to some confusion when first using JArgs because package names and other details change depending on which version of JArgs you apply.

The JArgs project page describes JArgs as "a convenient, compact, pre-packaged and comprehensively documented suite of command line option parsers for the use of Java programmers." The page asserts that JArgs might be selected over other Java-based command line processing libraries because JArgs is "easy to use, thoroughly tested, well documented and liberally licensed (BSD licence so no GNU messiness)." The page also states, "The package is small and without frills, but is functional, and contains code that has been in production use for quite some time."

JArgs is indeed small. The jargs-1.0.zip file is under 200 KB in size and the extracted core jargs.jar is only about 12 KB. There are no third-party library dependencies. The following screen snapshot demonstrates the contents of jargs.jar downloaded from SourceForge (the jargs-1.0.jar available on the Maven Repository is similar).

The documentation for JArgs consists primarily of its Javadoc-generated API documentation and code examples such as OptionTest and CustomOptionTest.

When using JArgs, the main class one uses is CmdLineParser (jargs.gnu.CmdLineParser or com.sanityinc.jargs.CmdLineParser depending on where you get your distribution of JArgs and which version you get). If using the JArgs JAR available via SourceForge download or via the Maven Repository, the primary class you'll be using is jargs.gnu.CmdLineParser. On the other hand, if you build JArgs from source available on GitHub (purcell/jargs), the main class will be com.sanityinc.jargs.CmdLineParser. In either case, there is just the one CmdLineParser outer class and it uses nested classes for additional support. In this post, I'm demonstrating examples based on the SourceForge/Maven JARs with jargs.gnu.CmdLineParser. The full source code of my examples will be posted on GitHub and that version will likely be edited to take advantage of more explicit imports for greater code conciseness and readability.

JArgs is small and so surprisingly is simple. It doesn't provide a lot of fancy features, but it does do basic command line argument processing with a simple programmatic approach that doesn't make use of annotations or reflection.

To implement the "definition" stage of command-line argument parsing with JArgs, one instantiates an instance of the CmdLineParser class and invokes the addOption(CmdLineParser.Option) method on that instance for each anticipated command line option. Each option is represented by an instance of a class that extends the CmdLineParser.Option class. In the examples in this series, I have been using a String-based file path/name option and a boolean-based verbosity option. So, when using JArgs for these examples, I can use the class CmdLineParser.Option.StringOption for file path and name and the class CmdLineParser.Option.BooleanOption for the verbosity option. The next code listing demonstrates implementation of the "definition" stage with JArgs.

"Definition" Stage with JArgs

public static void main(final String[] arguments)
{
   final CmdLineParser cmdLineParser = new CmdLineParser();
   final CmdLineParser.Option fileOption = new CmdLineParser.Option.StringOption('f', "file");
   cmdLineParser.addOption(fileOption);
   final CmdLineParser.Option verbosityOption = new CmdLineParser.Option.BooleanOption('v', "verbose");
   cmdLineParser.addOption(verbosityOption);

"Parsing" with JArgs requires just one statement, but you do need to catch the two checked exceptions thrown by the CmdLineParser.parse(String[]) method or explicitly state that these are thrown from your code that invokes with method. This is shown in the next code listing.

"Parsing" Stage with JArgs

try
{
   cmdLineParser.parse(arguments);
}
catch (CmdLineParser.IllegalOptionValueException | CmdLineParser.UnknownOptionException exception)
{
   out.println("Unable to parse command line options - " + exception);
   System.exit(-1);
}

When the code just shown executes successfully (without throwing either of the two checked exceptions), the instance of CmdLineParser upon which the parse(String[]) method was invoked now contains the values parsed for the expected command-line options and so we're ready to "interrogate" that instance.

JArgs "interrogation" stage of command line options processing uses methods on the CmdLineParser.Option-extending classes discussed earlier to retrieve the values parsed for each of those options. There are overloaded versions of the CmdLineParser.getOptionValue() methods for performing this interrogation. The method accepting only the instance of Option whose value is desired [getOptionValue(CmdLineParser.Option)] returns null if the option wasn't found or parsed. The method accepting the instance of Option whose value is desired and a second "default" object [getOptionValue(CmdLineParser.Option, Object)] returns the provided default object if that option was not found or parsed. In the code listing below, I use the second form in both cases and thus ensure I don't have to deal with null.

"Interrogation" Stage with JArgs

final String filePathName = cmdLineParser.getOptionValue(fileOption, "null").toString();
if (filePathName.equals("null"))
{
   out.println("ERROR: File path/name must be provided.");
   System.exit(-2);
}
out.println(
     "File path/name is " + filePathName
   + " and verbosity is " + cmdLineParser.getOptionValue(verbosityOption, false)
   + ".");

With these basic code examples in place, running the simple Java application that uses JArgs to command-line processing is demonstrated in the following screen snapshot.

The CmdLineParser class does not come with any built-in help/usage support. However, the jargs-1.0.zip available on SourceForge includes the source code for AutoHelpParser, a class which extends CmdLineParser and illustrates how help/usage could be implemented.

Because it's dated and due to its simplicity, JArgs can probably be used with older versions of Java than some of the other Java-basd command-line parsing libraries discussed in this series. When I run javap on CmdLineParser in the Maven-provided jargs-1.0.jar and the SourceForge-provided jargs.jar, to determine the version of Java it was compiled with, I see that it's major version 45! As I wrote in my post "Programmatically Determining Java Class's JDK Compilation Version", a major version of 45 indicates that the code was compiled with JDK 1.1!

Maven-provided jargs-1.0.jar Version Information from javap

Classfile jar:file:/C:/lib/jargs-1.0/lib/jargs-1.0.jar!/jargs/gnu/CmdLineParser.class
  Last modified Apr 9, 2005; size 6032 bytes
  MD5 checksum b2d61c0ce786f8a661cccf1e61de2a19
  Compiled from "CmdLineParser.java"
public class jargs.gnu.CmdLineParser
  minor version: 3
  major version: 45

SourceForge-provided jargs.jar Version Information from javap

Classfile jar:file:/C:/lib/jargs-1.0/lib/jargs.jar!/jargs/gnu/CmdLineParser.class
  Last modified Apr 9, 2005; size 6032 bytes
  MD5 checksum b2d61c0ce786f8a661cccf1e61de2a19
  Compiled from "CmdLineParser.java"
public class jargs.gnu.CmdLineParser
  minor version: 3
  major version: 45

Here are characteristics of JArgs to consider when selecting a framework or library to help with command-line parsing in Java.

  • JArgs is open source and is licensed with the BSD license.
  • There seems to be some confusion about versions and primary contributors to JArgs with Maven and SourceForge having one version and purcell/jargs on GitHub having another version.
  • JArgs is small and simple: the jargs.jar (or jargs-1.0.jar on Maven) is only about 12 KB in size and there are sno third-party library dependencies.
  • JArgs uses programmatic APIs for defining, parsing, and interrogating options rather than using annotations or reflection.
  • JArgs is a bit dated with many of its main pages having "latest updates" with years such as 2005. However, the GitHub page referenced in this post several times, and which has the different package name for its main class, shows its last updates being 2012.
  • I believe that JArgs could be used with the vast majority of Java applications today as it appears to me to be able to work with Java versions as far back as Java SE 1.1.

The most compelling reasons one might have to use JArgs instead of some of the other more commonly used and/or more recently updated Java-based command-line processing are its simplicity and small size. I sometimes face the decision of what level of command-line options I want to support JArgs provides a library that might be desirable when I want just a bit more than directly parsing the passed-in String[] to the main function, but don't want the power and complexity of some of the other Java-based command line processing libraries. My biggest concerns about using JArgs are probably that it hasn't been updated in a while and the potential confusion that could result for others using my work and having to deal with two different manifestations of JArgs with difference package name for its main class (using Maven for the dependency could help a lot here). JArgs is small and simple and might find its niche for those only desiring the most basic command line processing.

Additional References

Wednesday, August 23, 2017

Java Command-Line Interfaces (Part 12): CLAJR

The featured library for my twelfth post on processing command-line arguments in Java is Command-Line Arguments with Java Reflection (CLAJR). This "library" is a single Java source file (CLAJR-0.9.java) available for download on SourceForge. The main page for CLAJR currently shows a 2006 copyright date and the downloadable source zip file CLAJR-0.9-src.zip is dated 6 December 2008. Although CLAJR appears to be largely unsupported in recent years and although it's unlikely that I'd choose CLAJR over some of the alternative libraries already discussed in this series for processing command line arguments from Java code, I believe CLAJR warrants a post focused on it. There are some fairly unique characteristics of CLAJR that make it interesting, even if one chooses not to use it.

CLAJR is not provided as a JAR. Instead, it's provided as a single zipped file with a single Java source code file contained in that ZIP file. Being available as a single source code file is not unique to CLAJR; Picocli also is provided in a single Java source code file. However, Picocli also makes a JAR available on the Maven repository (which I used when working with Picocli), but I'm not aware of a pre-built JAR with the appropriate .class files for CLAJR. Therefore, because I prefer a JAR over a source code file when using third-party libraries, the first thing I did when experimenting with CLAJR was to build its provided source code into a small JAR file.

Here are the steps I used to build a JAR with CLAJR (I had to make a slight change to the source file and that change is described later in this post):

  1. Download CLAJR-0.9-src.zip from SourceForge.
  2. Unzip CLAJR-0.9-src.zip to extract CLAJR-0.9.java.
  3. Create a directory clajr to represent the Java clajr package that the Java source class should exist within.
  4. Move the CLAJR-0.9.java file to the clajr directory and rename it CLAJR.java to match the name of the class inside that source file.
    • I also had to edit the source code to make a minor change; this is discussed in more detail later in the post.
  5. Use javac to compile the CLAJR.java file into the multiple class files.
  6. Use jar to assembled the compiled .class files into a JAR (I named it CLAJR-0.9.jar).

The above steps are illustrated in the following two screen snapshots.

The Java-based command-line parsing libraries covered so far in this series tend to use annotations or specific programmatic APIs to allow for the definition, parsing, and interrogation of command-line arguments in Java code. CLAJR, as its full name suggests, instead uses Java reflection for defining the expected arguments to be parsed.

The main CLAJR page describes why the author chose reflection on methods' names for defining command-line arguments. CLAJR looks for methods that are named to correspond with the parameter or parameters being processed. A single underscore precedes a method name for a single-hyphen command-line argument while a double underscore precedes a method name for a double-hyphen command-line argument. A single method can be named to align with multiple command-line arguments when the multiple command-line arguments do the same thing.

As with the other posts in this series, my example for this post demonstrates use of the CLAJR library to model --file/-f command-line arguments and --verbose/-v command-line arguments. To "define" these in CLAJR, I need to name my methods _f__file and _v__verbose to correspond to -f/--file and -v/--verbose arguments. This is demonstrated in the partial snippet of a nested class Options in the next code listing.

"Definition" Stage in CLAJR: Reflection

/**
 * Used reflectively by CLAJR to parse and interrogate command line
 * options defined as fields in this class.
 */
public static class Options
{
   private String file;
   private boolean verbose;

   public void _v__verbose()
   {
      verbose = true;
   }

   public void _f__file(String newFilePathAndName)
   {
      file = newFilePathAndName;
   }

Parsing with CLAJR is a matter of a single statement. The next two lines of code demonstrate calling the static CLAJR.parse(String[], Object...) and passing to it the command-line arguments and just-instantiated instance of the Options class partially shown above.

"Parsing" Stage in CLAJR

final Options options = new Options();
CLAJR.parse(arguments, options);

In the single line parsing shown above, there is no return value from the parse method. Instead, the passed-in "options" instance has its fields populated according to which arguments are provided on the command line. CLAJR uses reflection on the "options" instance's methods to find the methods to invoke when corresponding arguments are discovered on the command line. It is this instance of Options that one can "interrogate" to find the values of arguments on the command line. This is demonstrated in the next code listing.

"Interrogation" Stage with CLAJR

out.println("File is '" + options.getFile() + "' and verbosity is set to '"
   + options.isVerbose() + "'.");

CLAJR supports providing of help/usage information on a per-method basis through reflection. In this case, methods are named with a convention similar to those for the command-line arguments themselves, but with help in front of the method name. This is demonstrated for the two combinations of arguments shown earlier in the next code listing.

CLAJR "Help" Methods

public String help_v__verbose()
{
   return "Enables verbosity of output.";
}

public String help_f__file()
{
   return "Path and name of file.";
}

The methods in the code listing just shown provide "help" strings for the -v/--verbose and -f/--file arguments. The static CLAJR method CLAJR.getHelp() method provides a String representing command-line usage that is based on these methods. CLAJR provides different exceptions that can be captured and these are often useful in conjunction with the "help" methods. These are demonstrated in the next code listing which shows the multiple catches that can be performed related to different error cases and other situations that warrant display of error information.

CLAJR's CLAJR.getHelp() and CLAJR Exceptions

catch (CLAJR.EmptyArgumentListException emptyArgsEx)
{
   out.println("Usage: Main -f|--file  [-v|--verbose]");
}
catch (CLAJR.HelpNeededException helpNeededEx)
{
   out.println(CLAJR.getHelp());
}
catch (CLAJR.ParseException parseEx)
{
   out.println(parseEx.getMessage());
   out.println(CLAJR.getHelp());
}
catch (Throwable throwable)  // CLAJR.parse throws Throwable
{
   out.println(throwable.getMessage());
}

CLAJR 0.9's CLAJR.parse(String[], Object...) method throws a Throwable and so the code above catches that Throwable.

The three screen snapshots that follow demonstrate this simple example. The first image shows the usage printed when no argument is provided. The second image depicts a normal flow of defining, parsing, and interrogating command-line arguments. The third image demonstrates display of help information when an unexpected argument is provided on the command line.

To get my examples to work, I had to change the CLAJR.java source file. Specifically, I changed line 491 in the invoke(String, String, boolean, List) method from if (tails == null) to if (tails == null || tails.isEmpty()). This is one of the advantages of open source; one can adjust or fix the code as necessary.

CLAJR allows a method named with a single underscore to be reflectively accessed. This - method corresponds to the "tail" parameter or parameters. I didn't use this in this example, but it's interesting to note that this will not work in JDK 9 because a single underscore is not allowed as an identifier in JDK 9. In fact, JDK 8's javac warns about this now as shown in the next screen snapshot.

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

  • CLAJR is open source and licensed with the GNU Lesser General Public License (version 2.1 or later).
  • CLAJR is available for download as an approximately 11 KB zip file called CLAJR-0.9-src.zip that contains the single file CLAJR-0.9.java.
  • CLAJR 0.9 does not require any third-party libraries to be downloaded.
  • CLAJR has been around for a while, but appears to not have been updated for several years. I had to make a minor change to the source code to get it to work properly for me.

CLAJR is a Java-based command-line processing library that is primarily distinguished from other libraries covered in this series by its use of reflection. Reflection is used to define expected command line arguments as well as the help messages associated with those arguments. CLAJR offers some unique ideas and approaches related to command-line processing in Java, but it's a somewhat dated library.

Additional Resources

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

Monday, August 7, 2017

Java Command-Line Interfaces (Part 10): picocli

The main picocli page describes picocli as "a mighty tiny command line interface" that "is a one-file Java framework for parsing command line arguments and generating polished, easily tailored usage help messages. With colors." This post provides a brief overview of applying Picocli 0.9.7 to process command line arguments in Java code.

Like the other Java-based command line processing libraries covered in this series, picocli is open source. Because all of picocli's implementation exists in a single Java source code file, it's easy to use the source directly if desired. The picocli page emphasizes this, "A distinguishing feature of picocli is how it aims to let users run picocli-based applications without requiring picocli as an external dependency: all the source code lives in a single file, to encourage application authors to include it in source form." If you'd rather use picocli as a library, there is a JAR available on the Maven Repository with the numerous compiled .class files (Picocli features one Java file but with numerous nested classes and annotations).

The easiest approach for getting an idea of Picocli's single-file nature is to look at that single file itself. The Java source code for CommandLine.java is available on the Picocli 'download' page. The next two screen snapshots show some output from javap when executed against the CommandLine class and when executed against one its inner annotations and one of its inner classes.

Whether one compiles CommandLine.java into one's own class/JAR file or one chooses to use a pre-built JAR from Maven, the source code of an application using Picocli is obviously the same. The "definition" stage of parsing with Picocli is accomplished by annotating instance fields that will store values associated with command-line options. This is demonstrated in the code snippet below.

"Definition" Stage with Picocli

/**
 * Demonstrate Java-based command-line processing with picocli.
 */
@Command(
   name="Main",
   description="Demonstrating picocli",
   headerHeading="Demonstration Usage:%n%n")
public class Main
{
   @Option(names={"-v", "--verbose"}, description="Verbose output?")
   private boolean verbose;

   @Option(names={"-f", "--file"}, description="Path and name of file", required=true)
   private String fileName;

   @Option(names={"-h", "--help"}, description="Display help/usage.", help=true)
   boolean help;

The code sample just shown demonstrates that Picocli allows for multiple names of the option flags to be specified (I specified single-hyphen single-character names and double hyphen multi-character names in my example). The example also shows that required=true can be specified for required options and help=true can be specified for "help" options that support special help-specific behaviors such as printing usage details and avoiding errors related to absent required options. Note that Picocli 0.9.8 adds more specific help type support with versionHelp and usageHelp.

The "parsing" stage is accomplished in Picocli with CommandLine.populateCommand(T, String...), where the T is the instance of the class with Picocli-annotated fields and the remaining Strings are the arguments to be parsed. This is demonstrated in the next code snippet.

"Parsing" Stage with Picocli

final Main main = CommandLine.populateCommand(new Main(), arguments);

The "interrogation" stage with Picocli consists simply of accessing the Picocli-annotated fields of the instance passed to the CommandLine.populateCommand(T,String...) method in the "parsing" stage. A simple example of this "interrogation" is depicted in the next code listing.

"Interrogation" Stage with Picocli

out.println(
     "The provided file path and name is " + main.fileName
   + " and verbosity is set to " + main.verbose);

To display help/usage information to the user when -h or --help is provided on the command line, it's as simple as "interrogating" the Picocli-annotated field that was designated help=true to see if that boolean is set or not and, if it is set, calling one of the overloaded CommandLine.usage methods. I happened to use one of the static versions of this method as shown in the next code listing.

Help/Usage with Picocli

if (main.help)
{
   CommandLine.usage(main, out, CommandLine.Help.Ansi.AUTO);
}

The next few screen snapshots demonstrate the simple Picocli-based processing application in action. The first image shows the type of error message and stack trace presented when a required flag is not present. The second image shows how the long and short names I specified in the annotations are respected. The third image shows the help feature in action.

One of Picocli's optional features that many of the other Java-based command-line parsing libraries don't have is color syntax support. The first code listing in this post showed some strings defined in annotations with @| |@ syntax. In the screen snapshot demonstrating "help" usage above, these characters were passed through as-is with no special treatment. However, if I instead run this sample code in Cygwin, I see what those tokens accomplish.

From the above screen snapshot, we see that Picocli applied some color syntax (yellow and white) automatically to the individual options' help and that it also applied the customized bold and underlined bold syntax to help description areas where the @| |@ syntax was applied.

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

  • Picocli is open source and is licensed under the Apache License 2.0.
  • Picocli does not require any third-party libraries or frameworks to be downloaded.
    • Picocli source code is completely contained within a single .java file and that source can be copied and pasted into one's own configuration management system and built with the rest of the application code, meaning that even a Picocli JAR file is not strictly necessary.
  • The CommandLine.java source code file (Picocli 0.9.7) is just under 3700 lines (including white space and comments) and is almost 200 KB in size. The picocli-0.9.7.jar file is approximately 83 KB in size.
  • Picocli enjoys current and frequent support. Its 0.9.8 version was released yesterday (after I had written most of this post).
  • Picocli documentation is detailed and in many ways more modern-feeling than the documentation for several other Java-based command-line processing libraries.
  • Picocli support for color syntax is easy to use and support for color syntax on different platforms is documented under the "Supported Platforms" section of the documentation.
  • Picocli's use of annotations on instance-level fields in similar to some of the other Java-based command-line processing libraries and enjoys the same advantages.
  • Basic Picocli features are highly approachable and easy to learn quickly, but Picocli also supports the ability to significantly customize several aspects of command-line processing with Picocli.

The code listings shown in this post are available in complete form on GitHub.

Picocli is a currently supported and updated library for processing command line arguments from Java. It features several of the newer features and approaches of some of the other available Java-based command-line processing libraries and throws in a couple differentiating features (color syntax and entire library encapsulated in single Java source file). Picocli is easy enough to use and appealing in its own right, but is most likely to separate itself from others in a particular developer's opinion if that developer desires the color syntax support or the ability to drop the source code file into the developer's project without any need for a JAR or compiled .class file.

Additional References