Monday, November 10, 2008

Command-line Parsing with Apache Commons CLI

From time to time, I find myself needing to handle command-line arguments in Java either for Java-based applications or for main() function implementations that provide a simple testing mechanism directly within the class being tested. The Java developer has many choices for command-line parsing. When there is only one, two, or a small number of command-line arguments (especially if the presence or absence of a flag is all that is needed rather than an accompanying value), writing a few lines of code to process these command-line options is not a big deal. When there are more options and/or some options have values, it is nice to access more sophisticated support for command-line parsing.

In this blog entry, I will look at using the Apache Commons CLI library, but there are numerous other choices such as args4j, TE-Code command line parsing, CLAJR (Command-Line Arguments with Java Reflection), JArgs, JSAP (Java Simple Argument Processor), and several others (even more here).

Although Apache Commons CLI library is part of Apache Commons, it is a separate (JAR) download from the JAR download for Apache Commons Modeler and from the JAR download for Apache Commons Lang that I talked about in previous blog entries available here and here. For this blog entry, I am using CLI 1.1 because there is no anticipated release for CLI 2.0 (more details on this at the end of this entry).

I will demonstrate some very simple examples of Apache Common CLI and include some links to other resources on use of this library.

Two important classes in use of Apache Common CLI are the org.apache.commons.cli.Option class and the closely related org.apache.commons.cli.Options (contains multiple instances of the Option class). These classes are used to represent the expected command-line options. The following two code snippets demonstrate setting up of an Options class for Posix-style options and GNU-style options.

Using the Options Class with Multiple Option Instances

/**
 * Construct and provide Posix-compatible Options.
 *
 * @return Options expected from command-line of Posix form.
 */
public static Options constructPosixOptions()
{
   final Options posixOptions = new Options();
   posixOptions.addOption("display", false, "Display the state.");
   return posixOptions;
}

/**
 * Construct and provide GNU-compatible Options.
 *
 * @return Options expected from command-line of GNU form.
 */
public static Options constructGnuOptions()
{
   final Options gnuOptions = new Options();
   gnuOptions.addOption("p", "print", false, "Option for printing")
             .addOption("g", "gui", false, "HMI option")
             .addOption("n", true, "Number of copies");
   return gnuOptions;
}

Note in the examples of setting up Options that there is no difference yet in the handling of Posix-style versus GNU-style options. So far, the options can be treated the same

Before moving onto demonstrating CLI's parsing of command-line arguments based on these anticipated options, it is worth noting CLI's support for usage information and help information via the org.apache.commons.cli.HelpFormatter class. This useful utility class contains methods such as overloaded versions of printHelp, overloaded versions of printUsage, and several other output and related methods.

The following code snippet demonstrates a method that makes use of one of HelpFormatter's printUsage methods and one of that class's printHelp methods.

printUsage() and printHelp()

/**
 * Print usage information to provided OutputStream.
 *
 * @param applicationName Name of application to list in usage.
 * @param options Command-line options to be part of usage.
 * @param out OutputStream to which to write the usage information.
 */
public static void printUsage(
   final String applicationName,
   final Options options,
   final OutputStream out)
{
   final PrintWriter writer = new PrintWriter(out);
   final HelpFormatter usageFormatter = new HelpFormatter();<
   usageFormatter.printUsage(writer, 80, applicationName, options);
   writer.close();
}

/**
 * Write "help" to the provided OutputStream.
 */
public static void printHelp(
   final Options options,
   final int printedRowWidth,
   final String header,
   final String footer,
   final int spacesBeforeOption,
   final int spacesBeforeOptionDescription,
   final boolean displayUsage,
   final OutputStream out)
{
   final String commandLineSyntax = "java -cp ApacheCommonsCLI.jar";
   final PrintWriter writer = new PrintWriter(out);
   final HelpFormatter helpFormatter = new HelpFormatter();
   helpFormatter.printHelp(
      writer,
      printedRowWidth,
      commandLineSyntax,
      header,
      options,
      spacesBeforeOption,
      spacesBeforeOptionDescription,
      footer,
      displayUsage);
   writer.close();
}

The next code snippet shows some calls to the printHelp()and printUsage() methods shown above and is followed by a screen snapshot showing the output from running those.

System.out.println("-- USAGE --");
printUsage(applicationName + " (Posix)",
constructPosixOptions(), System.out);
displayBlankLines(1, System.out);
printUsage(applicationName + " (Gnu)", constructGnuOptions(), System.out);
displayBlankLines(4, System.out);
System.out.println("-- HELP --");
printHelp(
   constructPosixOptions(), 80, "POSIX HELP", "End of POSIX Help",
   3, 5, true, System.out);
displayBlankLines(1, System.out);
printHelp(
   constructGnuOptions(), 80, "GNU HELP", "End of GNU Help",
   5, 3, true, System.out);

The first screen snapshot shows the results when the code above is executed exactly as shown (with true passed to both uses of the printHelp method to indicate that options should be included in the usage portion). The second screen snapshot shows what happens when the second call to printHelp has false passed to it so that the options are not displayed.

printUsage and printHelp

printUsage and printHelp with One printHelp Not Displaying Options

While the usage and help information about the options is, as their names imply, helpful and useful, the real reason for using command-line arguments is usually to control the behavior of the application. The next code listing shows two methods for parsing GNU-style and Posix-style command-line arguments. While the setting up of the Options did not care about the specific style other than specifying the options themselves, the type of option is important now for determining the appropriate parser to use.

usePosixParser() and useGnuParser()

/**
 * Apply Apache Commons CLI PosixParser to command-line arguments.
 *
 * @param commandLineArguments Command-line arguments to be processed with
 *    Posix-style parser.
 */
public static void usePosixParser(final String[] commandLineArguments)
{
   final CommandLineParser cmdLinePosixParser = new PosixParser();
   final Options posixOptions = constructPosixOptions();
   CommandLine commandLine;
   try
   {
      commandLine = cmdLinePosixParser.parse(posixOptions, commandLineArguments);
      if ( commandLine.hasOption("display") )
      {
         System.out.println("You want a display!");
      }
   }
   catch (ParseException parseException)  // checked exception
   {
      System.err.println(
           "Encountered exception while parsing using PosixParser:\n"
         + parseException.getMessage() );
   }
}

/**
 * Apply Apache Commons CLI GnuParser to command-line arguments.
 *
 * @param commandLineArguments Command-line arguments to be processed with
 *    Gnu-style parser.
 */
public static void useGnuParser(final String[] commandLineArguments)
{
   final CommandLineParser cmdLineGnuParser = new GnuParser();
   final Options gnuOptions = constructGnuOptions();
   CommandLine commandLine;
   try
   {
      commandLine = cmdLineGnuParser.parse(gnuOptions, commandLineArguments);
      if ( commandLine.hasOption("p") )
      {
         System.out.println("You want to print (p chosen)!");
      }
      if ( commandLine.hasOption("print") )
      {
         System.out.println("You want to print (print chosen)!");
      }
      if ( commandLine.hasOption('g') )
      {
         System.out.println("You want a GUI!");
      }
      if ( commandLine.hasOption("n") )
      {
         System.out.println(
            "You selected the number " + commandLine.getOptionValue("n"));
      }
   }
   catch (ParseException parseException)  // checked exception
   {
      System.err.println(
           "Encountered exception while parsing using GnuParser:\n"
         + parseException.getMessage() );
   }
}

When the above code is executed, its output looks like that shown in the next two screen snapshots:

PosixParser Results

GNU Parser Results

The Complete Example

The complete code for the example application from which portions were shown above is now listed for convenience.

package dustin.examples.cli;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;

/**
 * Main example demonstrating Apache Commons CLI.  Apache Commons CLI and more
 * details on it are available at http://commons.apache.org/cli/.
 * 
 * @author Dustin
 */
public class MainCliExample
{
   private static Options options = new Options();

   /**
    * Apply Apache Commons CLI PosixParser to command-line arguments.
    * 
    * @param commandLineArguments Command-line arguments to be processed with
    *    Posix-style parser.
    */
   public static void usePosixParser(final String[] commandLineArguments)
   {
      final CommandLineParser cmdLinePosixParser = new PosixParser();
      final Options posixOptions = constructPosixOptions();
      CommandLine commandLine;
      try
      {
         commandLine = cmdLinePosixParser.parse(posixOptions, commandLineArguments);
         if ( commandLine.hasOption("display") )
         {
            System.out.println("You want a display!");
         }
      }
      catch (ParseException parseException)  // checked exception
      {
         System.err.println(
              "Encountered exception while parsing using PosixParser:\n"
            + parseException.getMessage() );
      }
   }

   /**
    * Apply Apache Commons CLI GnuParser to command-line arguments.
    * 
    * @param commandLineArguments Command-line arguments to be processed with
    *    Gnu-style parser.
    */
   public static void useGnuParser(final String[] commandLineArguments)
   {
      final CommandLineParser cmdLineGnuParser = new GnuParser();

      final Options gnuOptions = constructGnuOptions();
      CommandLine commandLine;
      try
      {
         commandLine = cmdLineGnuParser.parse(gnuOptions, commandLineArguments);
         if ( commandLine.hasOption("p") )
         {
            System.out.println("You want to print (p chosen)!");
         }
         if ( commandLine.hasOption("print") )
         {
            System.out.println("You want to print (print chosen)!");
         }
         if ( commandLine.hasOption('g') )
         {
            System.out.println("You want a GUI!");
         }
         if ( commandLine.hasOption("n") )
         {
            System.out.println(
               "You selected the number " + commandLine.getOptionValue("n"));
         }
      }
      catch (ParseException parseException)  // checked exception
      {
         System.err.println(
              "Encountered exception while parsing using GnuParser:\n"
            + parseException.getMessage() );
      }
   }

   /**
    * Construct and provide Posix-compatible Options.
    * 
    * @return Options expected from command-line of Posix form.
    */
   public static Options constructPosixOptions()
   {
      final Options posixOptions = new Options();
      posixOptions.addOption("display", false, "Display the state.");
      return posixOptions;
   }

   /**
    * Construct and provide GNU-compatible Options.
    * 
    * @return Options expected from command-line of GNU form.
    */
   public static Options constructGnuOptions()
   {
      final Options gnuOptions = new Options();
      gnuOptions.addOption("p", "print", false, "Option for printing")
                .addOption("g", "gui", false, "HMI option")
                .addOption("n", true, "Number of copies");
      return gnuOptions;
   }

   /**
    * Display command-line arguments without processing them in any further way.
    * 
    * @param commandLineArguments Command-line arguments to be displayed.
    */
   public static void displayProvidedCommandLineArguments(
      final String[] commandLineArguments,
      final OutputStream out)
   {
      final StringBuffer buffer = new StringBuffer();
      for ( final String argument : commandLineArguments )
      {
         buffer.append(argument).append(" ");
      }
      try
      {
         out.write((buffer.toString() + "\n").getBytes());
      }
      catch (IOException ioEx)
      {
         System.err.println(
            "WARNING: Exception encountered trying to write to OutputStream:\n"
            + ioEx.getMessage() );
         System.out.println(buffer.toString());
      }
   }

   /**
    * Display example application header.
    * 
    * @out OutputStream to which header should be written.
    */
   public static void displayHeader(final OutputStream out)
   {
      final String header =
           "[Apache Commons CLI Example from Dustin's Software Development "
         + "Cogitations and Speculations Blog]\n";
      try
      {
         out.write(header.getBytes());
      }
      catch (IOException ioEx)
      {
         System.out.println(header);
      }
   }

   /**
    * Write the provided number of blank lines to the provided OutputStream.
    * 
    * @param numberBlankLines Number of blank lines to write.
    * @param out OutputStream to which to write the blank lines.
    */
   public static void displayBlankLines(
      final int numberBlankLines,
      final OutputStream out)
   {
      try
      {
         for (int i=0; i<numberBlankLines; ++i)
         {
            out.write("\n".getBytes());
         }
      }
      catch (IOException ioEx)
      {
         for (int i=0; i<numberBlankLines; ++i)
         {
            System.out.println();
         }
      }
   }

   /**
    * Print usage information to provided OutputStream.
    * 
    * @param applicationName Name of application to list in usage.
    * @param options Command-line options to be part of usage.
    * @param out OutputStream to which to write the usage information.
    */
   public static void printUsage(
      final String applicationName,
      final Options options,
      final OutputStream out)
   {
      final PrintWriter writer = new PrintWriter(out);
      final HelpFormatter usageFormatter = new HelpFormatter();
      usageFormatter.printUsage(writer, 80, applicationName, options);
      writer.flush();
   }

   /**
    * Write "help" to the provided OutputStream.
    */
   public static void printHelp(
      final Options options,
      final int printedRowWidth,
      final String header,
      final String footer,
      final int spacesBeforeOption,
      final int spacesBeforeOptionDescription,
      final boolean displayUsage,
      final OutputStream out)
   {
      final String commandLineSyntax = "java -cp ApacheCommonsCLI.jar";
      final PrintWriter writer = new PrintWriter(out);
      final HelpFormatter helpFormatter = new HelpFormatter();
      helpFormatter.printHelp(
         writer,
         printedRowWidth,
         commandLineSyntax,
         header,
         options,
         spacesBeforeOption,
         spacesBeforeOptionDescription,
         footer,
         displayUsage);
      writer.flush();
   }

   /**
    * Main executable method used to demonstrate Apache Commons CLI.
    * 
    * @param commandLineArguments Commmand-line arguments.
    */
   public static void main(final String[] commandLineArguments)
   {
      final String applicationName = "MainCliExample";
      displayBlankLines(1, System.out);
      displayHeader(System.out);
      displayBlankLines(2, System.out);
      if (commandLineArguments.length < 1)
      {
         System.out.println("-- USAGE --");
         printUsage(applicationName + " (Posix)", constructPosixOptions(), System.out);
         displayBlankLines(1, System.out);
         printUsage(applicationName + " (Gnu)", constructGnuOptions(), System.out);

         displayBlankLines(4, System.out);

         System.out.println("-- HELP --");
         printHelp(
            constructPosixOptions(), 80, "POSIX HELP", "End of POSIX Help",
               3, 5, true, System.out);
         displayBlankLines(1, System.out);
         printHelp(
            constructGnuOptions(), 80, "GNU HELP", "End of GNU Help",
               5, 3, true, System.out);
      }
      displayProvidedCommandLineArguments(commandLineArguments, System.out);
      usePosixParser(commandLineArguments);
      //useGnuParser(commandLineArguments);
   }
}

Drawback of CLI: Version Issues

One of the most significant drawbacks of Apache Commons CLI is the CLI version paradox advertised on the CLI's main page. This main CLI page points out that "the 2.x design is generally preferred" while also pointing out that, because there is no planned 2.0 release, "the 1.1 release is recommended to most users." I used CLI 1.1 for the examples in this blog entry.

Conclusion

The Apache Commons CLI is one of many Java-based command-line argument parsing libraries that is available to make writing text-based and command-line based Java applications and tests easier. This example has shown how to use CLI to implement command-line parsing along with output of help and usage information. However, CLI has many more options and uses than shown here. Some of these are demonstrated in the easy-to-read CLI Usage Scenarios document. Other useful introductions to Apache Commons CLI include Parsing Simple Command Line Arguments in Java Using the Commons CLI Library, Process the Command Line with CLI in Java, and Using the Jakarta Commons, Part 1.

Saturday, November 8, 2008

Java Generics and Clearer Communication

I generally appreciate the extra type checking provided by Java Generics. There is no doubt they improve my code's readability and maintainability while also improving my code's runtime robustness. However, one thing that I have observed is that there can still be some room for improvement in code that makes use of generics. For example, while Map<String,String> does specify the mapping of one String to another String, it is not clear to another developer using this Map what the content of the key and of the value for the given Map should be.

There are a few ways to get around this issue and to improve the ability of other developers to use the Map appropriately. These include:

1. Descriptive Naming: The name of the Map can provide significant insight into what is actually being mapped. For example, the map might be declared as Map<String,String> stateToItsCapitalCityMap. This communicates the fact that this is a map with a state mapped to its capital city. This descriptive naming is generally advisable for any code situation, but it can sometimes be difficult to capture the complete essence of a variable's meaning without an unwieldy name.

2. Domain Objects Rather than Simple Objects/Primitives: In the blog entry Never, Never, Never Use String (or at least less often), an argument is made for use of domain objects instead of primitives and String and other similarly general objects. For example, the Map cited above could instead be specified as Map<State,CapitalCity>. This can be combined with the first listed approach to be very descriptive: Map<State,CapitalCity> stateToItsCapitalCityMap. Besides readability, other advantages associated with this approach include more specific type checking and better ability to encapsulate business logic. As some of the strongly worded responses to Never, Never, Never Use String ... demonstrate, there are many people who find this advice to be dogmatic or unrealistic. For my part, I agree with some of the comments that it largely depends on a number of factors including time constraints, application size, the nature of the problem itself (difficult or easy to comprehend), and so on. I think where it gets hard to justify making a domain object is when it is a single value being represented (such as representing the name string as a full-fledged Name object).

3. Effective External Documentation: Naming variables appropriately seems to be largely considered a best practice these days with much less controversy than it once enjoyed. However, even with appropriately named variables and even with Domain-relevant classes used instead of Strings, primitives, and other general types, there is significant value to using Javadoc to effectively document our Map in question. We see an example of this in the Javadoc for Map itself. This Javadoc tells us that Interface Map<K,V> has "Type Parameters" defined as K - the type of keys maintained by this map and V - the type of mapped values. As a colleague of mine (Ross Delaplane) has demonstrated several times in his own code, we can follow this example. For example, I could document my Map in Javadoc as shown next:

/**
 * <p>Map of a Person's Name to that Person.</p>
 *
 * <p>This Map contains key/value pairs {@code <K,V>} where
 * {@code K} is the Name of the Person.
 * {@code V} is the Person associated with the the key Name.</p>
 */
Map<Name,Person> nameToItsPersonMap;

The {@code } tag notation is useful for rendering the text inside it as <code> font (fixed-width font) and for instructing the parser to ignore characters such as < and > that occur inside of there. The {@literal } tag notation is similar to {@code } without the fixed-width, code-oriented font. The ability of both {@code } and {@literal } to allow < and > to occur in the documentation without being parsed in especially useful when documenting generic use.

More General than Generics

While this blog entry has focused on making collections and other data structures using generics even more descriptive to potential users, these considerations are not limited to generics only. In fact, most of these considerations have been previously applied to writing cleaner and more maintainable Java code in general.

As discussed above, the idea of using descriptive variable names and descriptive parameter names has become one of those generally accepted principles of software development.

In the item in Effective Java on designing method signatures carefully, Josh Bloch points out some of the advantages of combining primitive types and Strings into domain objects that encapsulate these values and make the API easier to use. Bloch especially warns against using long lists of the same type in a method signature because that is very likely to have the arguments passed into the method in the wrong order and thus be used incorrectly.

Even though I agree that there is such a thing as too much commenting, I have found that some commenting is essential. This is especially true of Javadoc-generated comments. I have heard some developers state that there is no need for Javadoc because other developers can simply look at the code for the authoritative answers. This argument hinges on the assumption that the code uses descriptive names and is self-documenting. However, in my experience on very large programs, I find that many of the people that perform requirements collection, testing, architecting, and even managing the project prefer Javadoc-generated HTML web pages over looking at the code directly. For these people (and even for me sometimes), the Javadoc comments are important. For example, I relatively rarely look at JDK source code as compared to looking at JDK API documentation. It is generally of very little use to document simple get and set methods, but is often of great use to document more significant methods for these users. In Effective Java, Joshua Block points out the importance of documenting all publicly exposed elements in an API.

Nothing in this blog entry is particularly new, but I have seen that much of it is also not particularly well-followed either. The best developers are those who know what works best for a different situation and use the appropriate approach for that situation rather than trying to stick to rules of thumb that start with "always" or "never." However, in many cases, I think we can do better in communicating the intention of our variables and method parameters, especially when generics are involved. Depending on various factors, we can do this with more descriptive names, with domain objects rather than general objects and primitives, and/or with more descriptive Javadoc comments. In my opinion, descriptive names and descriptive Javadoc comments are almost always (I won't say "always" because I just opined about the dangers of such absolutes) appropriate and using domain objects can often be appropriate.

I began this entry by discussing a few simple ways to better describe the use of collections and data structures that take advantage of generics. I then moved into a more general discussion based on these specific ideas. So, with all the complaints about Java generics, are they even worth using? I personally have found Java generics, despite their sometimes strange wrinkles and warts, to be generally useful and to improve my code. One more reference to Effective Java is appropriate here. The Second Edition of that book states very clearly and plainly: Don't Use Raw Types in New Code.

Generics, like other Java constructs such as methods, parameters, and variables, can be even more powerful when they are named appropriately and descriptively and when their use is appropriately documented. Use of domain-oriented objects rather than general types and primitives can also increase the readability and runtime robustness of these Java constructs.

Colorado Software Summit 2008 CD-ROM

I received my copy of the post-conference CD-ROM for the Colorado Software Summit 2008 yesterday morning (Friday, November 7). This 670MB resource contains the latest versions of speakers' slides that include changes made during the conference in response to participants' feedback. For example, I added several slides to my presentations based on feedback and even included some slides specifically calling out the questions asked with answers. Later in this blog entry, I'll reference some of my previous blog entries that are tightly coupled with my slides and/or code examples on the CD-ROM.

There are several advantages to having each speaker present on each of his or her two topics three times each during the week. Besides the obvious advantages of reduced conflicts between an attendee's most desired sessions and the ability to hear from others which presentations are "must-see," a third advantage is that speakers can and often do include feedback from previous presentations of the same slides into the next presentation. The slides on the post-conference CD are often improved from the original slides provided on the pre-conference USB drive.

Besides the slides, several presenters provided the code examples they demonstrated. I am particularly looking forward to looking at and running the code examples that accompanied Matthew Wakeling's presentation "Performance Engineering from Scratch."

I was surprised at the number of photographs included on the CD-ROM. I submitted a relatively small number of photographs of the conference center and of the Keystone area, but one or more of the other contributors of photographs really went all out. There are over 350 photographs on the CD that include photographs of the sessions, of the attendees, and many of the beautiful scenery in the area.

I tried to include README.txt (or similarly named) files in each directory in the examples I included on the CD-ROM. There is a "jmx" directory under which all of my examples for "Java Management Extensions Circa 2008" presentation reside. All directories other than this "jmx" directory are related to examples for my other presentation (Applying Flash to Java: Flex and OpenLaszlo). These examples have a little more complicated directory structure because they involve Flex files, OpenLaszlo files, and other files related to the generation of a WAR file.

For most of the examples, the libraries or frameworks used need to be downloaded separately. For example, Flex and OpenLaszlo can be downloaded here and here respectively and SWFObject can be downloaded here. For the JMX examples, JMX 2 can be downloaded as described here, the ws-jmx-connector can be downloaded here (Early Access 4 just released), the Spring Framework can be downloaded here, OpenDMK can be downloaded here, Apache Commons Modeler can be downloaded here, and EasyMBean can be downloaded here.

Many of the examples on the CD-ROM have similar counterparts available on my blog. Some of these are highlighted next:


Examples: Applying Flash to Java: Flex and OpenLaszlo

* Flash to Flash Communication (Flex)
* OpenLaszlo ColorPicker
* OpenLaszlo Form Validator
* OpenLaszlo GradientView
* Laszlo RSS Reader
* Laszlo ToolTips

I showed other examples in the presentation, but did not blog on them because of their extreme simplicity and because the code included in the Flex 3 Language Reference and the Flex 3 Component Explorer provide similar examples.


Examples: Java Management Extensions Circa 2008

* Model MBeans Examples:
      + Direct/"Raw" Model MBeans
      + Spring Framework Model MBeans
      + EasyMBean Model MBeans
      + Apache Commons Modeler Model MBeans
* MXBean
* JMX 2 Annotations
* JMX 2 Querying
* JMX Connectors and Adapters
* JMX Web Services Connector
      + Basics of JMX Web Services Connector
      + With WinRM
      + Custom JMX Server with WinRM


I am looking forward to browsing the slides and examples on the Colorado Software Summit 2008 post-conference CD-ROM and continuing my learning of new things from the conference. By the way, Wayne has been posting previous Colorado Software Summit presentations on the conference website in the past. I recommend watching that page in case he continues that practice and posts 2008 presentations. All presentations from Colorado Software Summit 2007 are now available online.

UPDATE (18 November 2008): Chris Richardson has posted the slides from his presentations at Colorado Software Summit 2008 on his blog.

Friday, November 7, 2008

RIA Technology Perceptions in the Java Community

Several polls on java.net have interested me and the results of some of them have even surprised me. While these are not scientific polls and they could certainly be rigged by someone wanting to drive them to a certain suggestion, I don't think there are many developers interested in doing so. The comments can also be interesting. While the results of these polls aren't binding on anyone or any organization and, in fact, are probably ignored by decision makers, I believe that these poll results can provide a better idea of what technologies fellow Java developers are actually using and what they would like to use.

The current poll asks the question "What should be the primary focus of future Swing development?" The voting is still early (slightly fewer than 150 total votes so far), but the overwhelming favorite answer to this poll question (nearly 81% of the votes) is currently "[make] Swing a better stand-alone GUI library." Currently the second-place choice (just over 12% of votes) is for the option to improve JavaFX. Over 5% of the votes were for the option to not develop Swing any more.

UPDATE (14 November 2008): As of 14 November 2008, this poll had a total of 820 votes with 667 (81.3%) of them in favor of "making Swing a better stand-alone Swing GUI" and 103 votes (12.5%) favoring Sun focusing on supporting JavaFX." Sun definitely has an uphill battle to win over Java developers to JavaFX.

These results do not surprise me. For one, I fall in the camp of those who would rather see a once-dominant client-side technology improved and enhanced (such as has been done with Java SE 6 Update 10) rather than having resources and energy divided and diverted to yet another new language. I am not the only one who has been concerned about this. Frank Sommers addresses this in Has JavaFX Hurt Client-Side Java? Of course, Bruce Eckel pointed many Java developers to Flex "way back" in early 2007 with Hybridizing Java.

There have been several interesting blog entries and articles recently that seem to imply additional discontentment with the direction of client-side Java. These include Sharendipity's post We're Moving to Flash [from Java]: Here's Why, Sun Setting Down on the Core Swing, and JavaWorld's Are Applets Making a Comeback?.

Other java.net polls also help paint a picture of the RIA/Java developer landscape. In an earlier poll with the question Are you going to download the JavaFX SDK?, roughly one-third of the 728 responses were either "I already have" or "probably." Another 14% were "maybe" with roughly 38% answering "probably not" or "no." A poll that I think may back up my contention that many of us would have rather seen attention and resources focused on applets (and WebStart) is the results of the poll question What RIA platform do you prefer? Almost 1/4 of the respondents chose Java applets. The only higher response was for Ajax/DHTML (~30%). Flex got 17% of the votes and 10% of the responses were from people who don't like RIA.

I thought that the low point had occurred this week with Kirill Grouchnikov's post Sun Setting Down on the Core Swing. When I read that, my first reaction was, "This is exactly what I've been afraid of since hearing about the JavaFX being the 'big thing' at 2007 JavaOne and again at 2008 JavaOne. Sun has ditched Swing in favor of this new thing that has not even been delivered yet." This was especially frustrating after the release of Java SE 6 Update 10 and the Next Generation Java Plug-in had started to get me excited about the future of Swing and of applets again.

Fortunately, a detailed response from Josh Marinacci alleviated some of my fears and renewed some of my excitement about the direction of Swing and applets. Other encouraging posts include Java RIAs, Languages, and JavaFX and Wouldn't It Be Cool?. Of course, there are still lingering doubts and I'm still not convinced that a totally new JavaFX was necessary, but I do hope that I'll be pleasantly surprised in the near future. One of the big knocks on JavaFX has been the seemingly premature announcement of it at 2007 JavaOne and another edition of JavaOne coming and going before its first SDK release. I think we'll begin to have a better understanding of the potential (good or bad) of JavaFX when the JavaFX SDK 1.0 is released formerly. JavaFX is already behind Flex/Flash and DHTML/Ajax in the race to claim the hearts and minds of Java developers developing RIAs, so I believe that time is of the essence.

I love programming in Java and would like to see a Java-based solution work well for me on the client. However, I have to admit (as I've done many times in this blog) that Flex + Java EE has been a compelling combination and it may already be difficult to pry the rapidly growing number of those of us who do love the Flex/Java EE combination away from what works well for us.

I believe the polls on java.net have given us a fairly representative glimpse of how Java developers feel about Swing, client-side Java in general, and RIA development with Java. It will be interesting to see how future polls and their results document either renewed interest in Java applets and/or JavaFX or continued migration to other client technologies. As for the JavaFX SDK, I will be one of those who will download it and play with it. While I may have felt it wasn't the best decision at the time and was announced way too soon, I can't help but be curious about it. With much time and attention spent on it already, I hope that it will meet or even exceed my expectations.

Monday, November 3, 2008

RIA Accessibility with Flex

The blog entry Accessibility in Adobe Flex was recently featured on DZone and reminded me of another question I was asked by an attendee of one of my three presentations of Applying Flash to Java: Flex and OpenLaszlo at Colorado Software Summit 2008. The conference attendee wanted to know what type of accessibility support Flex offers. In this blog entry, I will reference several good overviews on Flex accessibility (and Flash accessibility) and then demonstrate a couple aspects of Flex accessibility support with concrete code examples.

Nick Boldison's blog entry Accessibility in Adobe Flex provides a brief introduction to and overview of accessibility issues with a Flex perspective. Adobe provides a Flex Accessibility Overview and Flash Accessibility Overview that also provide good high-level descriptions of what Flex and Flash respectively can do for increased accessibility.

The Flex Accessibility Overview explains that Flex comes with 28 components that include built-in accessibility support.

Another useful document related to Flex and accessibility issues is the Accessibility Best Practices for Flex document, which reminds the reader of the importance of testing Flex-based applications and also suggests that the developer of highly accessible Flex applications understand the basics of different types of limited access and how to overcome those types of limited access. In an effort to help with this, this document describes the preferred means of access for individuals with blindness, with vision impairment, with color blindness, with hardness of hearing, and with mobility impairment.

The Accessibility Best Practices for Flex document also explains how to turn Flex's built-in accessibility on (probably not on by default because of the "small but nontrivial increase in the size" of the generated .swf file). In short, the four main approaches (with greater detail shown in the Best Practices document) for turning on accessibility in Flex 3 applications are: (1) globally via the flex-config.xml file, (2) per application using the Flex 3 command-line compiler (mxmlc and its -accessible option), (3) using FlexBuilder 3's compiler option for accessible applications in project properties, and (4) at runtime by adding the appropriate query parameter.

Before moving onto some concrete examples of ensuring greater accessibility in Flex applications, I want to reference a couple more useful resources. These include the Flex Accessibility FAQ (includes a list of the 28 components with built-in accessibility support), Accessibility and Flex: We Can Do Better, Adobe Needs to Improve Accessibility in Flash Player, and the Adobe Flex 3 Help section Creating Accessible Applications.


Tab Order

The Flex 3 Help article Creating Tab Order and Reading Order talks about using the Tab key and specific ordering of elements via the Tab button to help improve a Flex application's accessibility. It is very easy in Flex to explicitly take advantage of tab order support using the tabIndex attribute. Flash applications do have a default (automatic) tab order that is overridden if at least one Flex component has tabIndex explicitly specified. Tab ordering will be demonstrated in the example at the end of this blog entry.


Detecting a Screen Reader

There is a Flash class called flash.accessibility.Accessibility that provides an active property that allows Flex code to detect whether a screen reader is being used. This allows the Flex application to present different things depending on the presence or absence of a screen reader. This will also be demonstrated in the example at the end of this blog entry.


Colors

I had a manager on my first real Flex application who constantly wanted to tweak the colors of the application because of issues that made it difficult for him to see the effects and contrasts due to his color blindness. I learned then the value of making it easy to change color settings. I have liked the ColorPicker component since then for making it really easy for the user to select preferred colors.


Tool Tips

The example at the end of this blog entry includes Tool Tips on the form items as well as on the rendered chart. Tool Tips are especially useful when the graphic or image does not contain any text and the tool tip can describe what the image shows.


The Example

With the above concepts described, it is time to move onto the example. This single example demonstrates the above specific concepts. It also takes advantage of the Google Charts API as described in one of my previous blog entries to build the chart dynamically and to populate an associated Tool Tip for the chart. Besides demonstrating tool tips, it also demonstrates tab ordering, color selection, and detecting absence or presence of a screen reader. In addition to demonstrating some Flex accessibility features and the use of Google Charts API, this example also demonstrates use of E4X features of Flex and ActionScript 3.0.

Here is the code listing for AccessibilityExample.mxml:


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="1150" height="700"
applicationComplete="initializeApp();">

<!-- Example of some of Flex's Accessibility Features for the blog
Dustin's Cogitations and Speculations. That blog is available at
http://marxsoftware.blogspot.com/.-->

<mx:XMLList id="departmentsInfo">
<data>
<department name="Accounting" totalEmployees="3"/>
<department name="Engineering" totalEmployees="25" />
<department name="Human Resources" totalEmployees="2" />
<department name="Management" totalEmployees="10" />
<department name="Sales" totalEmployees="15" />
<department name="Support" totalEmployees="50" />
</data>
</mx:XMLList>

<mx:Script>
<![CDATA[
import flash.accessibility.Accessibility;
import mx.controls.Alert;

/**
* Initialize this application by indicating whether or not a
* screen reader is being used and by generating a chart using
* Google Chart API.
*/
public function initializeApp():void
{
ascertainScreenReaderUse();
generateChart();
}

/**
* Determine whether a screen reader is being used and indicate
* any such use.
*/
private function ascertainScreenReaderUse():void
{
const screenReaderStatus:String = "A screen reader is ";
if ( Accessibility.active == true )
{
screenReaderIndicator.text = screenReaderStatus + "being used.";
}
else
{
screenReaderIndicator.text = screenReaderStatus + "not being used.";
}
}

/**
* Generate chart for display.
*/
private function generateChart():void
{
// Use Google Charts (http://code.google.com/apis/chart/basics.html)
// to build a 750x300 3D pie chart from the XML data for departments and
// employees and scaled to between zero and fifty employees per department.
const urlBase:String =
"http://chart.apis.google.com/chart?cht=p3&chs=750x300&chd=t:"
var urlDataVar:String = "";
var urlDataLabelsVar:String = "";
var toolTipVarStr:String = "Distribution of Employees by Department: ";
for each (var department:XML in departmentsInfo.department)
{
urlDataVar += department.@totalEmployees;
urlDataVar += ",";
const deptNameAndCount:String =
department.@name + " (" + department.@totalEmployees + ")";
urlDataLabelsVar += deptNameAndCount + "|";

toolTipVarStr += deptNameAndCount;
toolTipVarStr += ", ";
}
chart.toolTip = toolTipVarStr;
const urlDataVarSize:uint = urlDataVar.length;
const urlData:String = urlDataVar.substr(0, urlDataVarSize-1);
const urlDataLabelsVarSize:uint = urlDataLabelsVar.length;
const urlLabelsStr:String = urlDataLabelsVar.substr(0, urlDataLabelsVarSize-1);
const urlLabels:String = "&chl=" + urlLabelsStr;
const urlScaling:String = "&chds=0,50";
const url:String = urlBase + urlData + urlLabels + urlScaling;
chart.source = url;
}
]]>
</mx:Script>

<mx:Panel id="mainPanel" title="Dustin's Accessibility with Flex Example"
layout="horizontal"
width="{application.width-10}"
height="{application.height-75}"
backgroundColor="{backgroundColorSelector.selectedColor}">
<mx:Form id="mainForm" width="{mainPanel.width*0.35}">
<mx:FormHeading label="Enter Personnel Information"
tabIndex="1" />
<mx:FormItem id="formItemName" label="Name" required="true"
tabIndex="2" toolTip="Name">
<mx:TextInput id="textInputItemName" tabIndex="3" />
</mx:FormItem>
<mx:FormItem id="formItemAddress1" label="Address" required="true"
tabIndex="4" toolTip="Address">
<mx:TextInput id="textInputAddress1" tabIndex="5" />
</mx:FormItem>
<mx:FormItem id="formItemAddress2" label="Address (cont)" required="false"
tabIndex="6" toolTip="Address (cont)">
<mx:TextInput id="textInputAddress2" tabIndex="7" />
</mx:FormItem>
<mx:FormItem id="formItemCity" label="City" required="true"
tabIndex="8" toolTip="City">
<mx:TextInput id="textInputCity" tabIndex="9" />
</mx:FormItem>
<mx:FormItem id="formItemState" label="State" required="true"
tabIndex="10" toolTip="State">
<mx:TextInput id="textInputState" tabIndex="11" />
</mx:FormItem>
<mx:FormItem id="formItemZip" label="Zip Code" required="true"
tabIndex="12" toolTip="Zip Code">
<mx:TextInput id="textInputZip" tabIndex="13" />
</mx:FormItem>
<mx:FormItem id="formItemFavoriteColor"
label="Favorite Color" required="false"
toolTip="Favorite Color">
<mx:RadioButtonGroup id="favoriteColorRadioGroup" />
<mx:RadioButton groupName="favoriteColorRadioGroup"
label="Blue"
tabIndex="14"
color="#000099" />
<mx:RadioButton groupName="favoriteColorRadioGroup"
label="Green"
tabIndex="15"
color="#00CC00" />
<mx:RadioButton groupName="favoriteColorRadioGroup"
label="Orange"
tabIndex="16"
color="#FF9900" />
<mx:RadioButton groupName="favoriteColorRadioGroup"
label="Red"
tabIndex="17"
color="#FF0000" />
<mx:RadioButton groupName="favoriteColorRadioGroup"
label="Yellow"
tabIndex="18"
color="#FFFF00" />
</mx:FormItem>
</mx:Form>
<mx:Panel id="secondaryPanel"
title="Administration Features"
backgroundColor="{backgroundColorSelector.selectedColor}"
width="{mainPanel.width*0.65}"
height="{mainPanel.height}">
<mx:Label id="backgroundColorSelectorLabel"
text="Select Background Color"
fontWeight="bold"
tabIndex="19" toolTip="Select Background Color" />
<mx:ColorPicker id="backgroundColorSelector" selectedColor="#CCCCCC"
tabIndex="20" toolTip="Select Background Color" />
<mx:Spacer height="15" />
<mx:Label text="Screen Reader Status" fontWeight="bold" />
<mx:Label id="screenReaderIndicator" />
<mx:Spacer height="25" />
<mx:Image id="chart" width="{secondaryPanel.width}" />
</mx:Panel>
</mx:Panel>

</mx:Application>




Some Issues/Limitations To Be Aware Of

There are a few caveats worth considering regarding Flex and Flash accessibility support. The Flash Player 10 Accessibility Overview points out that Internet Explorer is the required web browser for screen reader users. This same document also points out that screen reader users need a screen reader that includes a Flash Player implementation of Microsoft Active Accessibility (MSAA).

Other issues to be aware of include the need for screen reader users to download and run scripts provided here for using Flex with JAWS (a popular screen reader). For other issues to keep in mind when applying Flex to Rich Internet Applications with high accessibility, see Frustration with Accessibility in Flash and Flex, Adobe Needs to Improve Accessibility in Flash, Accessibility in Flash Bug and Issue List, Accessibility and Flex: We Can Do Better, and Flash and Accessibility.


Conclusion

This blog entry has only scratched the surface of Flex support for greater accessibility. While there are still some spots where Flex and Flash support for accessibility could be improved, it is encouraging to see the strides taken in making Flash more accessible and in allowing Flex to take advantage of these Flash improvements.

Saturday, November 1, 2008

Apache Commons ToStringBuilder

I have found the humble Java toString() to be one of the most useful and powerful techniques in debugging and troubleshooting Java applications. That is only true, unfortunately, when and if the developers on the team override the Object.toString() method to provide useful details.

The usefulness of the Java toString() method is documented in Joshua Bloch's Effective Java (Item 10 in the Second Edition and Item 9 in the First Edition). The toString() method's usefulness is so proven that some languages newer than Java have taken the concept even further. Scala, for example, provides a type of class (case class) in which a reasonable default toString() is provided. ActionScript provides an ObjectUtil.toString() method that can be used to print an object's contents even when that object's class does not have its own toString implementation.

Unfortunately, toString can be an oft-forgotten method in a new class's implementation and, even when implemented, may suffer some implementation details that reduce its usefulness. These negative considerations include lack of maintenance of the toString() method (such as not adding important new data members of a class to it), lack of standard format or coverage of different data types (especially Collections), handling of nulls, and the sheer tedium associated with hand-writing toString() methods.

The vast Apache Commons project (formerly known as Jakarta Commons) is rich with useful libraries, but I think many of them are often unappreciated by many of us because we're unaware of them. While there have been several blog entries and articles on Apache Commons, I still find myself learning about some of the useful features available in the Apache Commons project (which is really a repository of multiple libraries that in many ways are not very related). Perhaps even more surprisingly, I find that even some relatively experienced Java developers who are familiar with some of the more common pieces of Apache Commons (such as Commons Logging) are not familiar with the ToStringBuilder in the Commons Lang component.

The beauty of the Commons ToStringBuilder is that it helps overcome many of the issues that have traditionally reduced the effectiveness of toString() method implementations. In the remainder of this blog entry, I intend to demonstrate how easy it is to apply ToStringBuilder to any Java class. I will do this using several simple classes that mostly consist of data members and toString() implementations using ToStringBuilder.

The first class with code listed here (ToStringDemonstrator) is the main class for running the examples that demonstrate ToStringBuilder in action. This class doesn't do much itself, but it does print out the contents of each object via the objects' toString() implementations.


ToStringDemonstrator.java


package dustin.examples.common;

/**
* This example class demonstrates the high utility of the Apache Commons
* ToStringBuilder class and is associated with a blog entry at
* http://marxsoftware.blogspot.com/.
*
* @author Dustin
*/
public class ToStringDemonstrator
{
/**
* Main executable for demonstrating Jakarta Commons ToStringBuilder.
*
* @param args The command line arguments; none expected.
*/
public static void main(String[] arguments)
{
final Parent parent = new Parent();
System.out.println("PARENT:\n" + parent + "\n");

final ReflectionChild reflectionChild = new ReflectionChild();
System.out.println("CHILD/REFLECTION:\n" + reflectionChild + "\n");

final ReflectionChildExcludes reflectionExcludesChild =
new ReflectionChildExcludes();
System.out.println(
"CHILD/REFLECTION/EXCLUDES:\n" + reflectionExcludesChild + "\n");

final TransientsChild transientsChild = new TransientsChild();
System.out.println("CHILD/TRANSIENTS:\n" + transientsChild + "\n");

final ExplicitChild explicitChild = new ExplicitChild();
System.out.println("CHILD/EXPLICIT:\n" + explicitChild + "\n");

final StyleChild styleChild = new StyleChild();
System.out.println("CHILD/STYLE:\n" + styleChild + "\n");
}
}



The main executable class whose code is shown above instantiates several objects and implicitly calls their toString() implementations via System.out calls on those instantiated objects. The objects include one parent class and a bunch of its child classes. The inheritance hierarchy helps demonstrate ToStringBuilder's treatment of parent data members in child toString() implementations.

The output of each class's toString() implementation demonstrates a different characteristic of ToStringBuilder. I will go through the code for each object along with the String representation of each class generated by ToStringBuilder.


Parent.java

This class serves as a parent class for the rest of the classes in the example. It also demonstrates ToStringBuilder.reflectionToString(Object). This method allows one to take advantage of reflection and, in a single line of code, return the String representation of the class's data members. In this example, the object's this reference is passed to the method so that the same object's String representation is generated via reflection. Note, however, that this could be used on a separate class instance as well if that instance was significant to this class but did not have its own useful toString() implementation.

This class intentionally includes many different attributes of different types and with different modifiers (such as static and transient) to more fully demonstrate default behavior and custom behavior of the ToStringBuilder and related classes.


package dustin.examples.common;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.ToStringBuilder;

/**
* An example class to which Apache Commons ToStringBuilder can be applied.
* This specific example demonstrates using the simple
* ToStringBuilder.reflectionToString(object) approach without any non-default
* toString styles being provided and without any fields that should not be
* part of the toString() being specified.
*
* @author Dustin
*/
public class Parent
{
/** String member that is private. */
private String parentString = "Hello Mars!";

/** Primitive (int) member that is private. */
private int parentInt = 12;

/** Enum (since JDK 1.5) type. */
protected enum FruitEnum
{
APPLE,
BANANA,
MANGO,
ORANGE,
PINEAPPLE,
WATERMELON
};

/** Enum member. */
protected FruitEnum fruit = FruitEnum.MANGO;

/** Array (of Enums) member. */
protected FruitEnum[] fruits = { FruitEnum.APPLE, FruitEnum.PINEAPPLE };

/** Null Array. */
protected String[] nullArray = null;

/** List (of Enums) member. */
protected List<FruitEnum> fruitsList = new ArrayList<FruitEnum>();

/** Null List. */
protected List<String> nullList = null;

/** Map (of Enums) member. */
protected Map<FruitEnum,String> fruitsMap =
new EnumMap<FruitEnum,String>(FruitEnum.class);

/** Null value for data member. */
protected String nullString = null;

/** Transient data member. */
protected transient String transientString = "Transient!";

/** Static data member. */
protected static int staticCount = 0;

/** No-arguments constructor. */
public Parent()
{
fruitsList.add(FruitEnum.BANANA);
fruitsList.add(FruitEnum.ORANGE);

fruitsMap.put(FruitEnum.APPLE, "One a day keeps the doctor away.");
fruitsMap.put(FruitEnum.MANGO, "Paulie really likes these.");
fruitsMap.put(FruitEnum.PINEAPPLE,
"Crossbreeding of an apple tree and a pine tree?");
}

/**
* The most important method of this class because this entire example is on
* ToStringBuilder.
*
* @return The String representation of this class as constructed via
* reflection by Apache Commons ToStringBuilder.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
}




The output of this class's toString method is shown next:


PARENT:
dustin.examples.common.Parent@10b30a7[parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]


The generated output demonstrates how easy it was, with one line of code, to represent much of the object's state via its toString() method. The generated output includes handling of an array, a List, Map, and even a null value. The example also demonstrates that the default setting of ToStringBuilder does not include statics or transients.



ReflectionChild.java

This class extends the Parent class shown above. This example will demonstrate ToStringBuilder's handling of inherited data, including of the parent class's private data.


package dustin.examples.common;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
* This class demonstrates Apache Commons ToStringBuilder behavior in an
* inheritance/extends relationship.
*
* @author Dustin
*/
public class ReflectionChild extends Parent
{
private String childString = "Hello Jupiter!";

/** No-arguments constructor. */
public ReflectionChild() {}

/**
* Demonstrating automatically generated (via reflection) toString() of a
* child class using ToStringBuilder (Apache Commons).
*
* @return String constructed from this object via reflection by the Apache
* Commons ToStringBuilder class.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
}


As with the Parent.toString() implementation, this implementation uses the simple, default ToStringBuilder.reflectionToString method on itself (this). The output from running this class's toString() is shown next:


CHILD/REFLECTION:
dustin.examples.common.ReflectionChild@66848c[childString=Hello Jupiter!,parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]


One of the most interesting aspects of this child's toString() output is that even the parent's private data members (privateInt and privateString) are included in the ToStringBuilder-generated String. It is convenient that the child class, with the same single line of code, can provide a String representation of its content (including what it inherits from its parent).


ReflectionChildExcludes.java

There are times when we don't want every member to be included in the generated String representation of the object contents. As shown above, one way to prevent this is to make the data member static or transient. However, it is not really a good idea to make a data member transient or static simply for its toString() implementation. Fortunately, the ReflectionToStringBuilder class (extends ToStringBuilder) provides an easy mechanism for excluding fields that you don't want included in the toString() generation. In this case, I am not comfortable having a child class display its parent's private data as though it was the child's data. I can exclude a private element (or any other desired field) with the ReflectionToStringBuilder.setExcludeFieldNames method. In this case, I'll exclude the parent's two private data members (privateInt and privateString) and a data member from this same class (ignoreString) that I don't want included in the String representation.

This example takes ReflectionToStringBuilder even further than simply excluding certain fields from the generated String representation. The example also uses other methods on ReflectionToStringBuilder to specify that static members should be included and that transient members should be included.


package dustin.examples.common;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;

/**
* This class demonstrates use of the reflective capabilities of Apache Commons
* ToStringBuilder with data members not to be included in the toString()
* specified.
*
* @author Dustin
*/
public class ReflectionChildExcludes extends Parent
{
/** String defined for this child. */
private String childString = "Hello Pluto!"; // still a planet in this example

/** Child's static member. */
private static String staticChildString = "I am static!";

private transient String transientChildString = "I am transient!";

/** String to be ignored in toString() construction. */
private String ignoreString = "Ignore Me!";

/** String to not be ignored. */
private String doNotIgnoreString = "Do not ignore me!";

/**
* Uses Apache Commons ReflectionToStringBuilder to generate String
* representation of me.
*
* @return String representation of me.
*/
@Override
public String toString()
{
final ReflectionToStringBuilder reflectionToStringBuilder =
new ReflectionToStringBuilder(this);
reflectionToStringBuilder.setAppendStatics(true);
reflectionToStringBuilder.setAppendTransients(true);
reflectionToStringBuilder.setExcludeFieldNames(
new String[] {"parentString", "parentInt", "ignoreString"});
return reflectionToStringBuilder.toString();
}
}


The output of this code shows that the specified fields are not included in the output and that the static and transient fields are included in the output.


CHILD/REFLECTION/EXCLUDES:
dustin.examples.common.ReflectionChildExcludes@e09713[childString=Hello Pluto!,staticChildString=I am static!,transientChildString=I am transient!,doNotIgnoreString=Do not ignore me!,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>,transientString=Transient!,staticCount=0]



TransientsChild.java

This example demonstrates how to explicitly specify that transient data members should be included in the String representation using simple ToStringBuilder.reflectionToString(Object,ToStringStyle,boolean) rather than ReflectionToStringBuilder. In this example, the default style is still being used, so null is passed in as the second parameter. The 'true' passed as the third parameter indicates that transient data members should be included in the generated String representation of this object.


package dustin.examples.common;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
* Class demonstrating Apache Commons ToStringBuilder's explicit inclusion of
* transient data members in the generated String.
*
* @author Dustin
*/
public class TransientsChild extends Parent
{
/** Transient member of this child class. */
private transient String childString = "Hello Neptune!";

/** No-arguments constructor. */
public TransientsChild() {}

/**
* Use Apache Commons ToStringBuilder to generate a String representation of
* me that includes transient data members.
*
* @return String representation of me.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this, null, true);
}
}


The output of this code is shown next.


CHILD/TRANSIENTS:
dustin.examples.common.TransientsChild@e0e1c6[childString=Hello Neptune!,parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>,transientString=Transient!]


This output shows that this object's transient members (including those inherited from its parent) are included in the String representation that is generated.


ExplicitChild.java

All of the examples so far have used reflection to generate the String representation of the object's state. The reflection approach is convenient and offers the significant advantage of reflecting the current state of the object even if a developer adding a new data member to the class definition forgets to add it to the toString() implementation.

However, there are also reasons that reflection may not be the preferred approach for implementation a toString() method. In such cases, ToStringBuilder supports building a String representation with explicit specification of which fields to include in that representation. This is similar to hand-writing the toString() implementation, but offers the advantage of handling arrays, collections, nulls, and other special circumstances implicitly.


package dustin.examples.common;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
* This class demonstrates use of ToStringBuilder's append method to append
* only explicitly specified data members of this class to the class's toString()
* implementation.
*
* @author Dustin
*/
public class ExplicitChild extends Parent
{
private String childString = "Hello Venus!";

public ExplicitChild() {}

/**
* Users Apache Commons ToStringBuilder to construct a String representation
* of me.
*
* @return String representation of me constructed via Apache Commons
* ToStringBuilder.
*/
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("childString", this.childString)
.append("fruitsList", this.fruitsList)
.append(this.fruitsMap) // member name intentionally NOT specified
.append("nullString", this.nullString)
.toString();
}
}


The code above explicitly includes four data members in its generated String representation. One of them is intentionally included without the field name. The output is shown next:


CHILD/EXPLICIT:
dustin.examples.common.ExplicitChild@1389e4[childString=Hello Venus!,fruitsList=[BANANA, ORANGE],{APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]


Only the explicitly specified data members are included in the String representation and the fruitsMap does not have a field name included.


StyleChild.java

The ToStringBuilder and ReflectionToStringBuilder provide many benefits including the ability to take advantage of reflection to make a change-resistance toString() implementation and the advantage of automatic handling of special circumstances like Collections, arrays, null values, etc. However, all examples so far have used a default style and there may be reasons for a different toString() style. For example, coding conventions may require a different toString() style. ToStringBuilder offers some easy ways to customize style used in the String representations it generates. The next example indicates how easy it is to adjust several key aspects of a String representation using StandardToStringStyle, a concrete extension of the abstract ToStringStyle class.


package dustin.examples.common;

import org.apache.commons.lang.builder.StandardToStringStyle;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
* This class demonstrates use of Apache Commons ToStringBuilder with use of
* non-default styles.
*
* @author Dustin
*/
public class StyleChild extends Parent
{
/**
* String representation of me generated with Apache Commons ToStringBuilder
* using non-default toString style.
*
* @return String representation of me.
*/
@Override
public String toString()
{
final StandardToStringStyle style = new StandardToStringStyle();
style.setNullText("---NULL---");
style.setFieldSeparator(";");
style.setArrayStart("{{{");
style.setArrayEnd("}}}");
style.setArraySeparator("|");
style.setFieldNameValueSeparator(":");
return ToStringBuilder.reflectionToString(this, style);
}
}


Previous examples demonstrated that the default ToStringBuilder style included null representation as <null>, used comma (,) to separate fields, used used a single curly brace ({ and }) to demarcate arrays, used comma to separate elements in an array, and used equals sign (=) to tie a field name to its value. In this example, those settings have all been overridden and the output of the example demonstrates this.


CHILD/STYLE:
dustin.examples.common.StyleChild@157f0dc[parentString:Hello Mars!;parentInt:12;fruit:MANGO;fruits:{{{APPLE|PINEAPPLE}}};nullArray:---NULL---;fruitsList:[BANANA, ORANGE];nullList:---NULL---;fruitsMap:{APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?};nullString:---NULL---]


As the output above demonstrates, the new style has been easily applied to the String representation generated by the Apache Commons ToStringBuilder.


Performance Considerations

UPDATE (2 November 2008): This entire paragraph on "Performance Considerations" has been added since the original posting of this blog. As detailed in the Reflection API Trail of the Java Tutorial, the benefits of reflection do not come without drawbacks. One of the primary drawbacks is the reflection performance cost. The Javadoc documentation for ToStringBuilder (2.4) talks about the performance limitations of the reflective approach and also mentions problems associated with using the reflecting approach with a security manager. Please see feedback comments from Santhosh and davidecr for details regarding their previous experience with performance penalties associated with using ToStringBuilder's reflection capabilities.


Conclusion

This blog entry has focused on Apache Commons Lang component's ToStringBuilder and related classes. Even the several examples used in this blog entry have not fully demonstrated all of the features of ToStringBuilder and ReflectionToStringBuilder.

There are other ways to build a String representation of an object without hand-writing it or using the Apache Commons ToStringBuilder. For example, Dmitriy Setrakyan has written on JavaLobby about GridGain's Refactor-Safe ToStringBuilder. The Eclipse IDE has a toString generation plug-in and smart-codegen for NetBeans/Maven includes a toString generator.

The lang.builder.* package contains many other builder classes for methods that are common to many Java classes. These builders include HashCodeBuilder, EqualsBuilder, and CompareToBuilder. On top of these builders, Commons Lang also provides numerous other classes that make working with the JDK classes easier.


Additional Resources on Apache Commons

* Using the Jakarta Commons, Part 1
* Using the Jakarta Commons, Part 2
* Using the Jakarta Commons, Part 3


* Extend the JDK Classes with Jakarta Commons, Part 1
* Extend the JDK Classes with Jakarta Commons, Part 2
* Extend the JDK Classes with Jakarta Commons, Part 3