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

No comments: