A Command Line Parameter Example for Referencing Configuration Files


In the simplest case of a command line argument, you define a reference to a configuration file. For example, Listing 8.2 could be a prototype command line argument.

Listing 8.2
start example
 java com.devspace.jseng.configuration.Main -l somefile.txt 
end example
 

In Listing 8.2, there are two command line parameters: -l and somefile.txt . The two command line parameters will be passed to the Java program. Listing 8.3 is an implementation of how to process the arguments.

Listing 8.3
start example
 Options options = new Options(); options.addOption("l", "location", true, "location of configuration file"); CommandLineParser parser = new BasicParser(); CommandLine cmd = parser.parse(options, args); System.out.println("location of the file is (" +  cmd.getOptionValue("l") + ")"); 
end example
 

In Listing 8.3, three basic classes are needed so that you can use the cli package: Options , CommandLineParser , and CommandLine . The class Options contains a collection of defined options. In the case of Listing 8.2, one required option would be the option -l . You define an option by using the method addOption . There is a difference between the definition of the option -l in Listing 8.3 and its actual usage in Listing 8.2. The reason for this difference is that the minus sign is not part of the option definition. The command line parser assumes that the minus sign indicates that an option is about to be defined. Hence, in the definition of the option there is no need to explicitly define the minus sign.

The method addOption has four parameters:

  • l : Defines the short form of the option

  • location : Defines the long form of the option

  • true : Specifies if there is an associated value with the option

  • "location of configuration file" : Defines a textual description of the option

In Listing 8.3, the class BasicParser is an implementation of the interface CommandLineParser . You can use several different implementations to parse the command line. (We will give more details about the different parsers in the next section of this chapter.) The interface method CommandLineParser.parse is used to combine the parser with the options and then to parse the two parts . Returned is a CommandLine class instance. If there are any problems, such as an option that has no declaration, an exception is thrown. The class CommandLine contains the parsed parameters. To retrieve a parameter, the method getOptionValue is used.

Explaining Command Line Parser Implementations

There are three different command line parsers: BasicParser , GnuParser , and PosixParser . All three deal differently with how specific versions of command lines are parsed. If you look at the Java Docs of the cli package, you will see the term bursttoken . However, the Java Docs explanations , while correct, are written using a specification-type language. Table 8.3 shows some command line examples, and how they are handled by each of the three parsers ( BasicParser , GnuParser , and PosixParser ).

Table 8.3: Command line option syntax and whether or not it can be parsed by a specific parser ( BasicParser , GnuParser , and PosixParser ).

Example

BasicParser

GnuParser

PosixParser

-l

Works

Works

Works

--location

Works

Works

Works

-l=file.txt

Error

Works, but gives an incorrect value

Works, but gives an incorrect value

--location=file.txt

Error

Error

Works

-lfile.txt

Error

Works

Works

Detailed Option Definitions

A typical command line will appear like Listing 8.2. Listing 8.4 shows a more complicated one.

Listing 8.4
start example
 java Main -l somefile.txt -f -d value1 value2 "left over" 
end example
 

In Listing 8.4, the command line has more command line options and different variations than it did in Listing 8.2. (For the moment, disregard how you could rewrite an individual option as per the examples shown in Table 8.3.) Listing 8.4 is not clear on what the individual options are. All this is clarified in Listing 8.5, where the square brackets are used to separate the individual options.

Listing 8.5
start example
 java Main [-l somefile.txt] [-f] [-d value1 value2] ["left over"] 
end example
 

In Listing 8.5, there are three options and some leftover arguments. The first option is a simple option. The second option has no associated value and is optional. The third option has two associated values. Finally, there are some leftover arguments that have no associated option. To be able to parse the three options, you can't use the method addOption as illustrated in Listing 8.3. Instead, you need to use the class Option . Listing 8.6 can parse the command line in Listing 8.5.

Listing 8.6
start example
 String args[] = generateMultipleArguments(); Options options = new Options(); options.addOption("l", "location", true, "location of configuration file"); Option option = new Option("f", "flag", false,  "this is a flag"); option.setOptionalArg(true); options.addOption(option); option = new Option("d", "define", true,  "definition declaration"); option.setArgs(2); options.addOption(option); CommandLineParser parser = new BasicParser(); CommandLine cmd = parser.parse(options, args); String results[] = cmd.getOptionValues("d"); if(results != null) { for(int c1 = 0; c1 < results.length; c1 ++) { System.out.println("Option value (" + results[ c1] + ")"); } } List list = cmd.getArgList(); if(list != null) { Iterator iter = list.iterator(); while(iter.hasNext()) { System.out.println("Left over: " + iter.next().toString()); } } 
end example
 

In Listing 8.6, the first three blocks of code are used to define the three different options. The first block defines an option using the addOption method. The second block still uses the addOption method, but instead of being passed four parameters, it's passed only one parameter. The single parameter is an Option class instance. When defining an option using the class Option, you can fine-tune the definition of the option. The second option is optional, and you can define that using the class method Option.setOptionalArg only. When you create the Option class instance, the constructor options are identical in purpose to the arguments of the previous block's addOption method. The third option has two associated arguments, which are assigned in the third block using the method setArgs . And, as with the second block, the newly created option is added to the list of possible options using the method addOption .

Once the options have been added, you can parse the command line arguments using the parse method. This time, instead of retrieving a single option argument, you need to retrieve a list of arguments using the method getOptionValues , with the associated command line argument. The method returns an array of string values. If the option has only one associated value, an array of length one is returned. If the option does not exist, a null is returned and not an array of length zero.

In Listings 8.3 and 8.6, the method getOptionValue(s) had only one argument, which was the specific command line argument. In each of the method calls, the short form of the command line argument was used. You can also use the long form of the command line argument. The class CommandLine will automatically retrieve the associated short form. It's up to the programmer to decide which form to use; however, the long form might be easier to maintain because the long form is explicit and does not leave the programmer guessing what that command line argument does.

In Listing 8.6, we used the method getArgList to retrieve the leftover arguments that have no associated options. Returned is a List class instance. If there are no leftover arguments, then the List is a null reference. To iterate the individual arguments, an Iterator class instance is used.

Another way to create the options defined in Listing 8.6 is to use the class OptionBuilder . Using this class has a catch, however: there is a static class instance definition within the class OptionBuilder . This will cause problems if there are multiple program instances or multiple threads within the same JVM trying to create options. Generally this is not an issue, but remember it just in case. Listing 8.7 is the same code as Listing 8.6 except that the options have been created using the class OptionBuilder .

Listing 8.7
start example
 Options options = new Options(); Option option = OptionBuilder.withLongOpt("location")  .hasArg()  .isRequired()  .withDescription("location of configuration file")  .create('l'); options.addOption(option); option = OptionBuilder.withLongOpt("flag")  .isRequired(false)  .withDescription("this is a flag")  .create('f'); options.addOption(option); option = OptionBuilder.withLongOpt("define")  .isRequired(true)  .hasArgs(2)  .withDescription("definition declaration")  .create('d'); options.addOption(option); 
end example
 

In Listing 8.7, a unique feature of the cli package is used. The methods are strung together using an object reference notation. This is fine because each OptionBuilder class method returns an instance to OptionBuilder . Each method is used to manipulate an option instance setting. When they are strung together, a valid option is created. It is important to call the method create at the end of the strung-together methods. The method create is the method that instantiates a new Option class instance.

The option methods are fairly self-explanatory. What is not self-explanatory is what the defaults are. Consider, for example, the second option block in Listing 8.7, where the method isRequired with a parameter of false is called. This method call is not necessary since the class OptionBuilder defaults to a value of false . We added it because, for maintenance purposes, it is simpler for the developer to know what is going on. It is assumed that the defaults are ignored and that each characteristic of the option is defined explicitly.

Listing 8.7 tends to be cleaner and simpler to understand than Listing 8.6. You can clean up Listing 8.6, but even then it is not as self-explanatory as Listing 8.7. Therefore, most of the time it is preferable to use the class OptionBuilder instead of instantiating the option classes manually.

When Something Fails or Help Is Needed

Command line options are not self-explanatory, so you will sometimes need help making the application do what is desired. Most applications will output a help message to guide the user on what the possible options are. You typically invoke this by using the -h or ”help option. Listing 8.8 shows how you could rewrite Listing 8.7 to be more user-friendly.

Listing 8.8
start example
 public class MainClass { private void printHelpMessage(Options options) { HelpFormatter help = new HelpFormatter(); String cmdLine = "command [OPTIONS] LEFT_OVER"; String descriptor = "Where [OPTIONS] can be one or more of the following"; String moreHelp = "email:you@somecompany.com for more help"; help.printHelp(cmdLine, descriptor, options, moreHelp); } private Options generateOptions() { Options options = new Options(); Option option = OptionBuilder.withLongOpt("location")  .hasArg()  .isRequired(false)  .withDescription("location of configuration file")  .create('l'); options.addOption(option); option = OptionBuilder.withLongOpt("flag")  .isRequired(false)  .withDescription("this is a flag")  .create('f'); options.addOption(option); option = OptionBuilder.withLongOpt("define")  .isRequired(false)  .hasArgs(2)  .withDescription("definition declaration")  .create('d'); options.addOption(option); option = OptionBuilder.withLongOpt("help")  .isRequired(false)  .withDescription("displays this help message")  .create('h'); options.addOption(option); return options; } public static void main(String args[]) { Options options = generateOptions(); try { CommandLineParser parser = new BasicParser(); CommandLine cmd = parser.parse(options, args); if(cmd.hasOption("h") == true) { printHelpMessage(options); return; } // Validate the arguments... } catch(ParseException ex) { System.out.println("Error: " + ex.getMessage()); printHelpMessage(options); } // Let the program run as normal... } } 
end example
 

Listing 8.8 has been broken apart into three blocks of functionality. Each block is represented by a method. The method main is used to start the program. At the start of the method, the options are created using the method generateOptions . Once the options have been created, the command line arguments can be parsed using the method parse . The next line is a decision that calls the method hasOption to test if the -h or ”help option has been called. If help is requested , then the method printHelpMessage is called. Otherwise, the application continues, the arguments are validated , and the program continues.

The command line parsing method call is encapsulated within an exception block. The exception block is necessary because it catches any errors that relate to incorrect command line options. The exception to catch is not the generic Exception class, but the specific ParseException class. This ensures that the only exceptions caught by this block are command line parsing exceptions. If an exception occurs, then the cause of the exception is retrieved using the method getMessage . Then, as a helper the correct arguments are output using the method printHelpMessage .

If you look at the method generateOptions in Listing 8.8, you'll notice one change: all options are optional. This can be problematic because it means that the parameters must be checked for correctness. If you didn't check the validity of the options, there would be parsing errors when only the -h or ”help flag is available.

The method printHelpMessage is a generic help message approach. In fact, we recommend that you use this format. This method makes it simpler to keep track of the various options. To understand the code, look at Listing 8.9, which is an output of the help message.

Listing 8.9
start example
 usage: command [OPTIONS] LEFT_OVER Where [OPTIONS] can be one or more of the following  -d,define <arg> definition declaration  -f,flag this is a flag  -h,help displays this help message  -l,location location of configuration file email:you@somecompany.com for more help 
end example
 

In Listing 8.9, the first line of text represents the variable cmdLine from Listing 8.8. The first line is supposed to be a command line usage of the application. In other examples of this line, many people specify the various options. The problem with this approach is due to maintenance. If a specific option is referenced in the first line, it has to be checked for existence against the method generateOptions . If you use a generic reference [OPTIONS], the class HelpFormatter will generate the actual available options. You don't need to reference correctly the leftover arguments that do not have an available option.

The second line of Listing 8.9 represents the variable descriptor from Listing 8.8. The second line represents a header, which is a filler that ties the command line to the generated options. Typically, the second line will indicate how the generated options are used.

The third, fourth, fifth, and sixth lines in Listing 8.9 represent the output of the available options defined in Listing 8.8. These lines are automatically generated when the method printHelp in Listing 8.8 is called. In Listing 8.8, the method printHelp has four parameters, which represent the command line, header, options, and footer.

The last line in Listing 8.9 represents the variable moreHelp in Listing 8.8. The last line could be any information, but we recommend that it be where the developer can find further help if he has no idea what the options mean.

We recommend that you follow this approach for parsing command line arguments. If you use this approach we have shown, the correct things will happen. For example, if there are incorrect parameters, the error will be dealt with correctly. In addition, in this approach, the individual blocks of functionality are separated, which makes maintenance and debugging simpler than when everything is put together in one block.

Using Specific Data Types

All of the examples shown thus far assume that the option is string value based. You can, however, define specific object types. For example, a command line argument could be a number, and therefore it would be better to return an integer value. Listing 8.10 is example of where an integer command line argument is converted into a number class instance.

Listing 8.10
start example
 Options options = new Options(); Option option = OptionBuilder.withLongOpt("integer") .hasArg() .isRequired(false) .withDescription("integer option type") .withType(PatternOptionBuilder.NUMBER_VALUE) .create('i'); options.addOption(option); CommandLineParser parser = new BasicParser(); CommandLine cmd = parser.parse(options, args); Integer value = (Integer)cmd.getOptionObject("i"); System.out.println("Value is " + value.toString()); 
end example
 

In Listing 8.10, the type of the option is set using the method withType . The method withType has only one parameter, which is the class descriptor of a supported type. The class descriptor has to be one of the types exposed by the class PatternOptionBuilder .

The class PatternOptionBuilder is a class that helps a developer define options that are type specific. However, it's not that useful, as shown in Listing 8.11.

Listing 8.11
start example
 Options options = PatternOptionBuilder.parsePattern("a:b@cde>f+n%t/"); 
end example
 

In Listing 8.11, the method parsePattern parses a string of characters and returns a list of options. However, looking at the string in Listing 8.11, you can see that it's not even close to apparent what the individual option types are. Hence, using the syntax in Listing 8.10 is much more useful and maintainable than that in Listing 8.11.

To return a specific data type, you use the method getOptionObject , which searches for a specific option. If the option is found, the associated argument value is converted into the correct data type. In Listing 8.10, the returned type is cast into an Integer class instance. If the cast were incorrect, a cast exception would be generated.




Applied Software Engineering Using Apache Jakarta Commons
Applied Software Engineering Using Apache Jakarta Commons (Charles River Media Computer Engineering)
ISBN: 1584502460
EAN: 2147483647
Year: 2002
Pages: 109

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net