RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Extend the JDK Classes with Jakarta Commons, Part III

Explore Jakarta Commons components that enable you to parse arguments in a command-line application, connect to various file systems at the same time, allow an application to uniformly access configurations loaded from various sources, and pool any object.

akarta Commons, the set of reusable classes that various Jakarta projects use, are available as separate components that you can use in your own Java projects. This final installment in a three-part series exploring Jakarta Commons presents sample applications to illustrate how to use four other useful components. (If you haven't already, read Part I and Part II.) The examples don't only illustrate the Commons components; they are complete, modular applications that highlight the useful features you can reuse in typical Java projects.

In particular, this installment explores the following components:

  • CLI (Command Line Interface)
  • VFS (Virtual File System)
  • Configuration
  • Pool

The article also includes the complete source code for all the sample applications. You can run it by launching the test cases for each of example with JUnit.

Author Note: A basic knowledge of object-oriented programming (OOP) and the Gang of Four design patterns (Chain and Command) will be very helpful for understanding the Commons components architecture and the examples presented here.

The CLI (Command Line Interface) component is very useful for parsing the arguments in a command-line application, since writing code to parse these arguments is time consuming and cumbersome. Also, using this component in existing CLI applications makes adding new features easy. This can be achieved by refactoring the code. (The documentation at the Jakarta site provides a pretty good higher-level overview of CLI usage.)

Part I of this series demonstrated the Commons Chain component with an example command-line tool that executed a few network commands. This installment uses the same application to demonstrate Commons CLI. You'll find the source code for the application in the package in.co.narayanan.commons.cli of the source code download. You can launch the main class CommandLine to experiment with the application's arguments (see Listing 1, which presents the command options in the sample application).

Listing 1. Syntax of the Sample Application's Commands Options
// java CommandLine -user admin -password manager -ping {host} // java CommandLine -user admin -password manager -ftp {host} -get {path_to_file} // java CommandLine -user admin -password manager -ftp {host} -ls {path_to_file}

The code uses the Commons Chain and Command pattern in the processing layer. For each command-line network command, it has a class to do the processing. These classes are connected to form a chain. When a command is invoked, the first command in the chain is given the arguments. The control flows through the chain until the desired command is identified to do the processing. The goal is to use Commons CLI to parse the arguments and populate the context object, so that the processing classes in the chain can use it.

Listing 2 contains a code snippet from in.co.narayanan.commons.cli.CommandLine.java, which demonstrates configuring Commons CLI to parse the arguments to the ping command.

Listing 2. Configuring Commons CLI for Parsing the Ping Command Arguments
private void createPingCmdOptions() { // ping command // java CommandProcessor -user admin -password manager -ping {host} pingOptions = new Options(); Option user = createUserOption(); pingOptions.addOption(user); Option passwd = createPasswordOption(); pingOptions.addOption(passwd); Option ping = OptionBuilder.withArgName("ping") .hasArg() .isRequired() .withDescription("Ping a remote system") .create("ping"); pingOptions.addOption(ping); }

The code creates instances of the Option class, which describes each of the command-line options, and adds it to the instance of the Options class. The class OptionBuilder facilitates Option class creation with a series of static methods. Listing 2 intends to create a mandatory option named ping, which has an argument. It provides a description for the option as well. The string it passes to the withArgName method will be used to fetch the value of the argument once parsing is complete. (You will see how that is done shortly.) You can directly instantiate the Option class to get more fine-grained control over the options as well.

The methods createUserOption and createPasswordOption return a new instance to represent the user and password arguments in the command line. These methods are used when creating Option class instances for other commands in the example.

Listing 3 presents the code for configuring the Commons CLI for the ftp command.

Listing 3. Configuring Commons CLI for the ftp Command Arguments
Option ftp = OptionBuilder.withArgName("ftp") .hasArg() .isRequired() .withDescription("File transfer protocol") .create("ftp"); ftpOptions.addOption(ftp); // For additional ftp commands like ls, put, and mget, a OptionGroup needs to be created // to indicate the options are mutually exclusive Option get = OptionBuilder.withArgName("get") .hasArg() .withDescription("Get a file from the server") .create("get"); Option ls = OptionBuilder.withArgName("ls") .hasArg() .withDescription("List the folder contents in the server") .create("ls"); OptionGroup ftpArgs = new OptionGroup(); ftpArgs.addOption(get); ftpArgs.addOption(ls); ftpArgs.setRequired(true); ftpOptions.addOptionGroup(ftpArgs);

In this example, the ftp network command contains mutually exclusive get and ls commands. The code uses the OptionGroup class to represent mutually exclusive options. Listing 3 called the setRequired method of the OptionGroup class by passing true to indicate that the mutually exclusive options are mandatory. So the user will be forced to enter either of the mutually exclusive commands.

This entire configuration lets the Commons CLI framework to do the following:

  • Parse the command-line argument string array and populate the instantiated Option classes with the values passed
  • Perform validations (For instance, if the user misses a mandatory argument, an exception is thrown.)
  • Print the usage message if the user didn't pass the right set of arguments

Listing 4 demonstrates invoking the parse method in a parser class.

Listing 4. Initiating the Parser to Perform the Parsing
public void process(String args[]) { // remaining code CommandLineParser parser = new BasicParser(); org.apache.commons.cli.CommandLine line = null; Context chainContext = null; // remaining code case PING : { try { line = parser.parse(pingOptions, args); chainContext = getPingContext(line); } catch (ParseException e) { System.out.println(e.toString()); HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Ping options", pingOptions); } } break; // remaining code } private Context getPingContext(org.apache.commons.cli.CommandLine line) { String user = line.getOptionValue("user"); String passwd = line.getOptionValue("password"); String host = line.getOptionValue("ping"); return new CommandlineContext(user, passwd, new CLICommand("-ping", new String[] {host})); }

The highlighted code is very important for understanding how to use the parser. It begins by instantiating the appropriate parser class, using BasicParser. For Unix-type CLI commands, you need to use PosixParser. For custom requirements, you can extend the org.apache.commons.cli.Parser class or implement the org.apache.commons.cli.CommandlineParser interface to create a complete parser from scratch.

The call to the parse method returns a reference to org.apache.commons.cli.CommandLine, which can be used to fetch the command-line option values. The code then instantiates CommandlineContext and populates it with the values retrieved for passing it to the chain for further processing.

If the Commons CLI encounters a problem while parsing the arguments passed, it throws an exception in the parse method. In this case, it uses the org.apache.commons.cli.HelpFormatter class to print a formatted help message that informs the user of the error.

The Commons CLI framework enables parsing the options to only one command. If a CLI application contains commands of commands with options—like in the example application here, a bit of preprocessing needs to be done to classify the command type (see Listing 5).

Listing 5. Classifying the Commands
private int classifyCommand(String args[]) throws CommandLineException { if(args != null && args.length > 0) { for(String arg : args) { if(arg.equals("-ping")) { return PING; } if(arg.equals("-ftp")) { return FTP; } } } else { throw new CommandLineException("Invalid command options. See usage."); } throw new CommandLineException("Invalid command options. See usage"); }

In the sample application, ping and ftp are distinct commands, which can have different command-line options. Hence, it cannot represent the command-line arguments in one instance of the Options class. It initially classifies the command type and uses the appropriate Options instance for parsing further.

Commons CLI is a neat API that is essential for every Java CLI application. It saves a lot of time and simplifies further enhancements to the application as well. The Jakarta Ant project uses Commons CLI for processing command-line arguments.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date