Friday, May 02, 2008

Parse command line arguments with Args4J

Args4J is a useful library to parse command line arguments, instead of doing it by hand. I'm very pleased with its simplicity, something I didn't find in others libraries.

Recently I've been working on some small console applications in Java. Like most of these applications, it's often needed to pass them some arguments at the console. For a while I parsed those arguments by hand. However, as soon as I had to do it for a large enough amount of arguments I started searching for a library to simplify things.
Jakarta Commons has all sort of useful libraries and Commons CLI is one of them. And for a while I used Commons CLI. But although the code became more organized I felt I had to write as much lines of code as before!
So after another search I found Args4J. This library makes use of annotations and has a much smaller API to use. It's not as flexible as Commons CLI, which allows you to parse different styles of arguments, etc. But I don't need that, I just wanted something to simplify the task. And args4j does simplify. Here's an example of how it works:

Imagine a sample application that tests some external system by sending a request to a specified machine. This application would need the following arguments: -h (the host of the external system) and -p (the port of the external system). This would translate to the following command in the console: "tester -h somemachine.com -p 9000".
The main class should look something like this:


public class Tester {

@Option(name="-h", usage="host of the external system", required=true)
private String host;

@Option(name="-p", usage="port of the external system")
private int port = 80;

public void setHost(String h){ this.host = h; }
public void setPort(int p){ this.port = p; }

public static void main(String[] args) {

Tester t = new Tester();
t.run(args);
}


public void run(String[] args) {

CmdLineParser parser = new CmdLineParser(this);
try {
parser.parseArgument(args);

//do whatever ...
System.out.println("Testing host " + host + " on port " + port);

} catch (CmdLineException e) {

System.err.println(e.getMessage());
System.err.println("\nTester [options...] arguments...");
parser.printUsage(System.err);
}
}
}

As you can see from the sample above, the clever usage of annotations in this case allows the definition and description of the parameters in a very simple way.
  • name is the argument name that should be typed in the console
  • usage is the description that is shown in case you don't type a valid set of arguments
  • required tells arg4j that the specified argument is mandatory (see documentation for more options).
Also, the API is quite small and does all the validation for you. In this case, the "host" parameter is required, so args4j will throw an exception if it's not supplied. And port parameter is automatically converted to "int". An error is also thrown if it can't convert it.
If validation succeeds, the values supplied will be bound to the corresponding fields in the class. Also notice that port is not required and has a value defined. This is basically a default value that will be replaced by the supplied argument, if it receives one.
So, with arg4j, all you have to do is annotate the fields, put the line of code that parses the arguments inside a try-catch block and safely use the fields. That's pretty cool.