Tuesday, November 29, 2011

Software Developers Hate Worthless Tasks

Most software developers that I know, especially the best ones, loathe worthless tasks. This is probably true of most people who strive to do what they do to the best of their ability, but I'm not aware of any area in which this attitude is more prevalent than in software development. The best software developers are passionate about what they do because they build things, they create things, and they make what they imagine become reality. On the other side, however, developers can quickly become disillusioned and lose productivity when faced with tasks they perceive as unimportant or "busy work."

Finding Value in One's Work

Over the years, I've seen some accomplished software developers leave their positions to pursue managerial positions or even completely different careers. Because this is often different than what I see from the 'typical' software developer, I often ask what led to the change. In some cases, it's as simple as responding to pressure to take a management role to "justify" their higher salary. In other cases, it's developers who are tired of learning new things at the pace often required in software development. The most common reasons that I hear from these folks have to do more with boredom or loss of interest in the work itself. These are typically people who are not being sufficiently challenged anymore and often are putting time and effort into something they perceive as having very little or no value.

Some of the lowest points in my career in software development have been when a project or task that I've put significant time, energy, and creativity into is terminated or significantly reduced in scope. Although I've generally received the same monetary remuneration as I would have for a successful delivered product into getting to that point, the feeling in such cases is more of discouragement than of satisfaction. Although compensated for my time and effort, it still hurts to think that the time and effort have no lasting value.

Canceled assignments or tasks are not the only source of disillusionment related to not finding value in one's work. Working on unnecessary tasks or "busy work" can be almost as difficult on a software developer. There always seems to be plenty of truly useful and contributory things to do and this makes it even more difficult to work on things that seem to have much less or no value.

Process

One of the biggest perceived enemies of software development productivity from the perspective of many software developers is onerous process. In Process Kills Developer Passion, James Turner writes, "the blind application of process best practices across all development is turning what should be a creative process into chartered accountancy with a side of prison." Turner makes a point that I've been making to anyone who will listen for the last few years: not all developers are equal and they shouldn't all be treated exactly the same way. Many projects make the mistake of assuming the "least common denominator" and enforcing processes on everyone to accommodate that "least common denominator." Turner articulates this more colorfully than I do: "companies need to start acknowledging that there is a qualitative difference between developers. Making all of them wear the same weighted yokes to ensure the least among them doesn't screw up is detrimental to overall morale and efficiency of the whole."

I think most of us who have worked in the industry for some time do realize that a degree of process is justified and even beneficial. The degree depends on the project, the skills and experience of the developers, and the size of the team. There are many benefits to standardization and code conventions. There are similarly well-advertised benefits to unit testing and other quality processes. That being stated, the best developers can ascertain which processes fit which situations best and which don't fit certain situations as well.

Meetings

I once was told, "It takes a very good meeting to beat having no meeting at all." This is often very sage advice. However, I have seen situations where a short, well-run meeting provides tremendous benefit. Most meetings waste peoples' time, especially if the meeting organizer starts late and "fills the time." The best meetings start promptly and address only what must be addressed. I've worked with people in the same office who will not talk or coordinate unless forced to by a third party and this is often facilitated by a short, informal meeting. Similarly, difficult design decisions and architecture trade-offs can be discussed effectively in meetings. It seems to be the natural tendency of meetings to go long, into the weeds, and become very dissatisfying as they waste developers' time. Well-run meetings, however, can have the opposite effect: they can help developers have clearer direction and be more efficient in working together as a team.

I previously blogged on one tip for effective software development meetings. Taking notes in a meeting, especially in a way such that participants can see them live as they are taken, has numerous advantages. These include getting everyone on the same page at the same time, documenting major decisions for future reference, and in having material to send those who did not make the meeting.

Not Every Idea Should Be Implemented

Not all ideas are created equal. Developers are often understandably impatient when they are coerced into implementing poor or useless ideas. This can be especially painful when the idea is counterproductive. It is difficult to justify to oneself spending time on something that will almost certainly never be used or, even worse, might make the user experience worse.

Scripting Tedious Tasks

Many developers look for ways to script especially tedious tasks rather than performing the tedious tasks manually even if the time spent writing the script matches the time that would have been spent completing the tasks directly. This is perhaps one of the best examples proving that most developers loathe tedious tasks. There are often many positives to this typical developer reaction to tedious tasks. First, it often turns out that the tasks that we thought we'd only do once need to be implemented again. It may be that the script can be applied to a similar situation or it may be that the script needs to be applied against a new set of input. Second, the act of writing a script provides more value than simply getting a task done; it can lead to improved familiarity with the scripting language and can sometimes provide a nice codification of the problem at hand.

Convention over Configuration (Configuration by Exception)

A major development in the software development industry in recent years has been the rapid adoption of convention over configuration (configuration by exception) in various languages and frameworks. The idea here is that developers need provide configuration information only when the configuration is different than the configuration provided by default (conventional configuration). This saves developers time and tedious effort to provide configuration details for common tasks.

Less Boilerplate Code

Another recent trend in software development is the focus on reduced boilerplate code. Convention over configuration has helped with this. Many of the alternative JVM languages tout less boilerplate code as one of their advantages over Java. For example, some Java developers feel that Groovy's property support is one of its nicest features. Even Java has embraced reduced boilerplate code in multiple areas. One of the features that make many libraries and frameworks popular is the ability to write and maintain less boilerplate code thanks to the framework or library. In cases where neither language nor framework has reduced the boilerplate code, IDEs and code-generators have been used successfully to implement boilerplate code with less tedium and with less risk. Writing boilerplate code is not only tedious, it can be easier to make mistakes when it must be implemented by hand.

Some Things Are Worth More Than They Appear

One mistake I have made multiple times and seen other developers make is to decide that a particular task is useless or of little value. I'm often correct in my identification of worthless or low-value tasks, but on rare occasions I am surprised when a seemingly useless task provides some real value or a tangible benefit. Observing why this is the case helps me refine my ability to differentiate between worthwhile and worthless tasks. Such situations have also reminded me to keep an open mind on the value of new ideas until I've thought carefully about the action and its good and bad ramifications. One of the most important things a software development manager can do is to assign worthwhile tasks to developers and to ensure that they understand the value in all assigned tasks.

Execution Matters

Even an idea with potential for value can lead to no or dramatically reduced value if not implemented correctly. For example, incorrect application of unit tests can dramatically reduce the ratio of value to cost for implementing and using unit tests. Similarly, code reviews and use of code quality tools can provide great value when executed correctly or provide much less value for the cost if executed incorrectly.

Conclusion

Most of us do better work when we enjoy what we do and when we perceive that what we do has value. Worthless or low-value tasks are more likely to be seen as tedious and are more likely to not be done well. Developers will be happier and more motivated when they do not have worthless tasks forced upon them.

Saturday, November 26, 2011

A First Look at Building Java with Gradle

I left JavaOne 2011 with several take-aways. One of the most significant was a renewed interest in learning more about Gradle. In this post, I look at using Gradle and at migrating a simple Ant build script to Gradle.

Gradle installation is easy. Gradle can be downloaded and unzipped into the desired location. I am using Gradle 1.0 Milestone 6 for examples in this post.

Once Gradle is downloaded and unzipped, the environment variable GRADLE_HOME can be set to the directory of the unzipped Gradle installation and the PATH should be set to $GRADLE_HOME/bin or %GRADLE_HOME%\bin. The Gradle installation page tells us that JVM options used by Gradle can be set via either GRADLE_OPTS or JAVA_OPTS. The Grade installation and configuration in the path can be confirmed by running gradle -v at the command line once the environment variable settings are sourced.

Besides verifying correct configuration of the Gradle installation, the output from running the -v option also reminds us that Gradle is built on (and brings the best of) Groovy (1.8.4), Ant (1.8.2), Ivy (2.2.0), and the Java Virtual Machine to our task execution.

The Gradle Build Script Basics documentation describes the default "build configuration script" (something like an Ant build.xml file) called build.gradle. This same page provides a Hello, World example implementation of such a build configuration script. An adapted version of that example is shown in the next code listing.

build.gradle - Hello Gradle Example
task hello_world {
   doLast {
      println 'Hello, Gradle!'
   }
}

The task (roughly equivalent to an Ant target) is defined above with the name "hello_world" and can be invoked by Gradle the same way an Ant task is invoked by Ant (assuming the default build configuration script file names in both cases): gradle hello_world. The next screen snapshot shows this both with and without the -q (for quiet) option.

An interesting observation I made while playing with even this simple build configuration file is the importance of proper curly brace placement. The initial curly brace on the same line as the task declaration must remain on that line. When I tried to move it down to the next line to line up in the same column as the closing brace, I saw the error in the next screen snapshot and the representative text output that follows the image.

Error Message from Opening Curly Brace on Next Line
FAILURE: Build failed with an exception.

* Where:
Build file 'C:\java\examples\groovyExamples\gradleExample\build.gradle' line: 2

* What went wrong:
Could not compile build file 'C:\java\examples\groovyExamples\gradleExample\build.gradle'.
Cause: startup failed:
build file 'C:\java\examples\groovyExamples\gradleExample\build.gradle': 2: Ambiguous expression could be a parameterless closure expression, an isolated open code block, or it may continue a previous statement;
   solution: Add an explicit parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...}, and also either remove the previous newline, or add an explicit semicolon ';' @ line 2, column 1.
   {
   ^

1 error


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.636 secs

Fortunately, the above error message is easily avoided by placing the opening curly brace of the Gradle task closure on the same line as the Gradle task's definition.

A default target can be specified in an Ant build.xml via the project element's default attribute. Gradle supports a similar specification of default tasks to be run as shown in the next code snippet which builds upon the previous example. Running this task now does not require specification of the task as shown in the next screen snapshot.

build.gradle - Hello Gradle Example with Defaults
defaultTasks 'hello_world'

task hello_world {
   doLast
   {
      println 'Hello, Gradle!'
   }
}

Gradle is intentionally constructed to be flexible and support a wide variety of tasks. For those interested in specific, commonly used tasks, there are Gradle plug-ins available to reduce the need for newly written Grade code. For purposes of this post, the Java plug-in is of particular interest. It is easy to apply this plug-in as shown in the next version of build.gradle which builds upon the previous two examples and adds the application of the Java plug-in.

build.gradle - Hello Gradle Example with Java Plug-in
apply plugin: 'java'

defaultTasks 'hello_world'

task hello_world {
   doLast
   {
      println 'Hello, Gradle!'
   }
}

The simple addition of the single line apply plugin: 'java' brings numerous Java-related tasks automatically to this build configuration file. I think that the easiest way to quickly ascertain which tasks are automatically available via the Java plugin is to run gradle tasks against a build configuration file that does not apply the plugin and then run gradle tasks against a build configuration file with the Java plugin applied. Doing just that is shown in the next two screen snapshots. (Note that gradle tasks is similar to Ant's -p option except that Ant requires the target's description attribute to be specified for a target to show up in response to the -p option.)

Comparing these two screen snapshots helps us see which tasks are made available by using the Java plugin in Gradle. Before looking at the Java specific tasks, I think it's also worth noting some other tasks available in Gradle without a plugin (the "Help tasks"). In particular, the images indicate the "tasks" task (one of the "Help tasks") we used to see all available tasks. There are similar built-in "Help tasks" tasks for viewing build properties ("properties"), viewing project dependencies ("dependencies"), viewing sub-projects ("projects"), and viewing help ("help"). Many of these tasks are available via deprecated command-line options as well.

The only other task listed for the build configuration file that did not apply the Java plugin was the custom defined "hello_world" task. However, the build configuration file that added the single line to apply the Java plugin had numerous other tasks besides the custom-provided "hello_world" task and besides the always-available "help tasks." These tasks include the "Build tasks" ("assemble", "build", "buildDependents", "buildNeeded, "classes", "clean", "jar", "testClasses"), "Documentation tasks" ("javadoc"), and the "Verification tasks" ("check" and "test").

It is also interesting to see the color syntax used in the output from running gradle tasks. This is a nice touch that adds to the readability of the output. If for some reason the color syntax is undesirable, the --no-color option can be specified to turn it off.

Convention over Configuration is an increasingly popular software development tenet that tools and frameworks such as Ruby on Rails, Maven, and JPA ("configuration by exception") have all embraced. Gradle similarly adopts conventions with the Java plugin tasks to reduce need for explicit configuration specification.

The Gradle Java Quickstart Page describes the Gradle conventions for the Java plugin (I have added the emphasis):

Gradle expects to find your production source code under src/main/java and your test source code under src/test/java. In addition, any files under src/main/resources will be included in the JAR file as resources, and any files under src/test/resources will be included in the classpath used to run the tests. All output files are created under the build directory, with the JAR file ending up in the build/libs directory.

Gradle's conventions for Java builds may look suspiciously similar to conventions Maven users are already familiar with.

For purposes of this blog post, I have changed my personal NetBeans-inspired directory convention to work with Gradle's expected convention. In other words, instead of using dist for the constructed JAR and using src and test for source and test code respectively, I move the files into src/main/java and test/main/java and plan for the JAR to be assembled into the build directory rather than in the dist directory.

With the source code in the appropriate expected directory, I show (in the next screen snapshot) the building of the Java code with the "build" task and the generation of Javadoc with the "javadoc" task. Again, the color syntax makes the output more readable.

The next screen snapshot shows the generated files under the build directory. None of this was specified as it relied upon Gradle's conventions.

The two folders "blogScriptConfigFiles" and "images" are not part of the Gradle build and are simply folders I created for managing versions of the build.gradle files and screen snapshots for this blog post. The other folders are either source or generated artifacts.

In case it wasn't clear above, I wanted to add a reminder here that to build and document this simple Java application, I only really needed the following Gradle build configuration file:

build.gradle - Minimum Required To Build Simple Java Application
apply plugin: 'java'

As long as the source files are in the appropriate location according to Gradle conventions and the build is not too complex, I don't need to specify any tasks (similar to Ant targets) at all. I included a simple build.xml example in my blog post Learning Java via Simple Tests that I often use as a starting point for building simple Java examples. However, even the simplest Ant build file doesn't get as easy as the one-liner Gradle build configuration file just shown!

Conclusion

Gradle appears to have great potential as a build tool. By combining the power of Groovy with the power of the JVM Ant, and Ivy, Gradle seems poised to provide a winning combination that should be difficult to beat when it comes to building Java applications. I look forward to Gradle 1.0 leaving Milestone status behind, hopefully in the very near future.

Compressing JPG Images with Groovy

I recently had need to reduce the size of a large number of JPG images. I did not want to do this one at a time in a graphics manipulation tool, so scripting seemed like the obvious choice. These days, Groovy is my generally my preferred tool for scripting jobs. I noted that Eric Wendelin had posted Crush images on the command-line with Groovy that demonstrates using Groovy in conjunction with ImageMagick to "[compress] images the Groovy way." This looks like an easy and attractive way to do it, but it requires ImageMagick and, if on Windows, Cygwin. I decided to write a similar script using straight Java and Groovy to see if I could reduce required dependencies. Note that this script would have been more concise had I used the Java Advanced Imaging API, but that would have meant another separate dependency.

rescaleJpgImage.groovy
import java.awt.Image
import java.awt.image.BufferedImage
import java.awt.image.RenderedImage
import javax.imageio.IIOImage
import javax.imageio.ImageIO
import javax.imageio.ImageWriteParam
import javax.imageio.ImageWriter
import javax.imageio.stream.FileImageOutputStream

if (args.length < 2)
{
   println "USAGE: groovy rescaleImage.groovy <sourceDirectory> <scalingFactor>\n"
   println "\twhere <sourceDirectory> is directory from which image files come"
   println "\t      <scalingFactor> is scaling factor for reduced image (0 to 1)"
   System.exit(-1)
}

def directoryPath = args[0]
def directory = new File(directoryPath)
if (!directory.isDirectory())
{
   println "${directoryPath} is NOT an existing directory!"
   System.exit(-1)
}

def scaleFactor = args[1] as float
def iter = ImageIO.getImageWritersByFormatName("jpeg")
def writer = (ImageWriter)iter.next()
def imageWriteParameters = writer.getDefaultWriteParam()
imageWriteParameters.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriteParameters.setCompressionQuality(scaleFactor);

def backupDirName = "backup_" + System.currentTimeMillis()
def targetDirectory = new File(backupDirName)
targetDirectory.mkdir()
def targetDirectoryPath = targetDirectory.canonicalPath
directory.eachFile
{ file ->
   def fullFileName = file.canonicalPath
   def fileName = file.name
   def sourceImage = ImageIO.read(new File(fullFileName))
   def targetName = targetDirectoryPath + File.separator + fileName
   println "Copying ${fullFileName} to ${targetName} with scale factor of ${scaleFactor} ..."
   def targetFile = new File(targetName);
   def output = new FileImageOutputStream(targetFile);
   writer.setOutput(output);
   def image = new IIOImage(sourceImage, null, null);
   writer.write(null, image, imageWriteParameters);
}
writer.dispose();

The Groovy script above does not change the input image files, but instead re-writes them with the same name to a new directory. The new directory is named by concatenating the milliseconds since epoch time after "backup_". The files are written as JPG files and are scaled by the provided scaling factor.

This script was sufficient for my needs, but there are many ways it could be improved. Groovy's built-in CLI-based command-line support could be used for nicer handling of command-line options. There could be better checks to verify that the provided scaling factor is a numeral between 0 and 1. This script also assumes that all images provided to be compressed are already JPEG format. Using libraries such as Java Advanced Imaging API, ImageMagick, or imgscalr might have made the script even more concise.

Compression Results

UPDATE: This section added to original post based on feedback from Eric Wendelin (see below). The following screen snapshot attempts to show compression results in terms of image file sizes. The leftmost files are the original files. The middle set of files were generated by running the above script against the original files with a scaling factor of 0.5. The rightmost set of files are the result of running the script against the original files with a scaling factor of 0.25.

As a final part of comparing the compression results, I include an image taken during the week in San Francisco for JavaOne 2011 in all three forms below. The first is the original image (3.62 MB), the second is the image generated with scale factor of 0.5 (482 KB) and the third is the image generated with scale factor of 0.25 (326 KB).

All three versions of the Fisherman's Wharf image above, including the original, are JPEG format. One of the downsides of the script above is that this format is assumed. The next two screen snapshots show what happens when the earlier PNG image comparing file sizes is run through this script.

Adding the ability to handle other image formats as input files is where a separate image manipulation library such as ImageMagick might be particularly useful.

Conclusion

This is another example of how Groovy combines the best of scripting with the feature-rich libraries of Java. The above script could have been written in Java, but Groovy seems a better fit for scripts like this one.

Friday, November 25, 2011

File Management in Java with Guava's Files Class

Both Groovy and Java SE 7 provide improvements for file management in Java as I discussed in posts here, here, here, and here. However, when a particular Java application is not able to yet use Java SE 7 or Groovy for its file management, an improved file management experience can still be obtained by using Guava's Files class.

File handling has always seemed a little more difficult in Java than in many other programming languages. Java SE 7 certainly improves Java's file handling capabilities dramatically with NIO.2, but not every project can make use of Java SE 7. For those projects not able to use Java SE 7, Guava's Files class is a nice intermediate solution for easier file handling. It is important to note here that Java SE 7 introduces a Files class of its own, so any use of Guava's Files class in Java SE 7 must be fully scoped if the Java version of Files is used in the same code. My examples have been written and built in Java SE 7, but I avoid using Java SE 7's Files class and so don't need to fully scope Guava's same-named class.

File Creation

Guava's Files class includes a couple overloaded write methods for easily writing content to a file. The next code sample demonstrates using Files.write(byte[],File).

Demonstrating Files.write(byte[],File)
   /**
    * Demonstrate writing bytes to a specified file.
    * 
    * @param fileName Name of file to be written to.
    * @param contents Contents to be written to file.
    */
   public void demoFileWrite(final String fileName, final String contents)
   {
      checkNotNull(fileName, "Provided file name for writing must NOT be null.");
      checkNotNull(contents, "Unable to write null contents.");
      final File newFile = new File(fileName);
      try
      {
         Files.write(contents.getBytes(), newFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(  "ERROR trying to write to file '" + fileName + "' - "
                     + fileIoEx.toString());
      }
   }

There are a couple observations that can be from this first code sample that will apply to all other code samples in this post. First, I make use of the statically imported Guava Preconditions class to provide an easy check ensuring that the provided parameters are not null. The second common feature of this code is that it explicitly catches and "handles" the checked exception IOException. All other Files methods demonstrated in this post similarly throw this same checked exception.

File Copying

The next example demonstrates how easy it is to copy files using Guava's Files.copy(File,File) method (one of several overloaded methods with name 'copy').

Demonstrating Files.copy(File,File)
   /**
    * Demonstrate simple file copying in Guava. This demonstrates one of the
    * numerous overloaded copy methods provided by Guava's Files class. The
    * version demonstrated here copies one provided File instance to another
    * provided File instance.
    * 
    * @param sourceFileName Name of file that is to be copied.
    * @param targetFileName Name of file that is result of file copying.
    */
   public void demoSimpleFileCopy(
      final String sourceFileName, final String targetFileName)
   {
      checkNotNull(sourceFileName, "Copy source file name must NOT be null.");
      checkNotNull(targetFileName, "Copy target file name must NOT be null.");
      final File sourceFile = new File(sourceFileName);
      final File targetFile = new File(targetFileName);
      try
      {
         Files.copy(sourceFile, targetFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to copy file '" + sourceFileName
            + "' to file '" + targetFileName + "' - " + fileIoEx.toString());
      }
   }
File Moving

Moving files with Guava is as easy as copying. The Files.move(File,File) method is demonstrated in the next code snippet.

Demonstrating Files.move(File,File)
  /**
    * Demonstrate moving a file with Guava's Files.move(File,File).
    * 
    * @param sourceFileName Path/name of File to be moved.
    * @param targetFileName Path/name of Destination of file.
    */
   public void demoMove(final String sourceFileName, final String targetFileName)
   {
      checkNotNull(sourceFileName, "Move source file name must NOT be null.");
      checkNotNull(targetFileName, "Move destination name must NOT be null.");
      final File sourceFile = new File(sourceFileName);
      final File targetFile = new File(targetFileName);
      try
      {
         Files.move(sourceFile, targetFile);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to move file '" + sourceFileName
            + "' to '" + targetFileName + "' - " + fileIoEx.toString());
      }
   }
Comparing Files

Determining if two files are the same is straightforward with the Gauva Files.equal(File,File) method.

Demonstrating Files.equal(File,File)
   /**
    * Demonstrate using Guava's Files.equal(File,File) to compare contents of
    * two files.
    * 
    * @param fileName1 Name of first file to be compared.
    * @param fileName2 Name of second file to be compared.
    */
   public void demoEqual(final String fileName1, final String fileName2)
   {
      checkNotNull(fileName1, "First file name for comparison must NOT be null.");
      checkNotNull(fileName2, "Second file name for comparison must NOT be null.");
      final File file1 = new File(fileName1);
      final File file2 = new File(fileName2);
      try
      {
         out.println(
             "File '" + fileName1 + "' "
           + (Files.equal(file1, file2) ? "IS" : "is NOT")
           + " the same as file '" + fileName2 + "'.");
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to compare two files '"
            + fileName1 + "' and '" + fileName2 + "' - " + fileIoEx.toString());
      }
   }
Touching Files

Touching a file to create a new empty file or to update the timestamp on an existing file can be easily accomplished with Guava's Files.touch(File) as shown in the next code sample.

Demonstrating Files.touch(File)
   /**
    * Demonstrate Guava's Files.touch(File) method.
    * 
    * @param fileNameToBeTouched Name of file to be 'touch'-ed.
    */
   public void demoTouch(final String fileNameToBeTouched)
   {
      checkNotNull(fileNameToBeTouched, "Unable to 'touch' a null filename.");
      final File fileToTouch = new File(fileNameToBeTouched);
      try
      {
         Files.touch(fileToTouch);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to touch file '" + fileNameToBeTouched
            + "' - " + fileIoEx.toString());
      }
   }
Retrieving File Contents

With a simplicity reminiscent of Groovy's GDK extension File.getText(), Guava's Files.toString(File,Charset) makes it easy to retrieve text contents of a file.

Demonstrating Files.toString(File,Charset)
   /**
    * Demonstrate retrieving text contents of a specified file with Guava's 
    * Files.toString(File) method.
    * 
    * @param nameOfFileToGetTextFrom Name of file from which text is to be
    *    retrieved.
    */
   public void demoToString(final String nameOfFileToGetTextFrom)
   {
      checkNotNull(nameOfFileToGetTextFrom, "Unable to retrieve text from null.");
      final File sourceFile = new File(nameOfFileToGetTextFrom);
      try
      {
         final String fileContents = Files.toString(sourceFile, Charset.defaultCharset());
         out.println(
              "Contents of File '" + nameOfFileToGetTextFrom
            + "' are: " + fileContents);
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to get text contents of file '"
            + nameOfFileToGetTextFrom + "' - " + fileIoEx.toString());
      }
   }
Temporary Directory Creation

Guava makes it easy to generate a temporary directory with Files.createTempDir().

Demonstrating Files.createTempDir()
   /**
    * Demonstrate Guava's Files.createTempDir() method for creating a temporary
    * directory.
    */
   public void demoTemporaryDirectoryCreation()
   {
      final File newTempDir = Files.createTempDir();
      try
      {
         out.println(
            "New temporary directory is '" + newTempDir.getCanonicalPath() + "'.");
      }
      catch (IOException ioEx)
      {
         err.println("ERROR: Unable to create temporary directory - " + ioEx.toString());
      }
   }

I don't provide a code sample here, but it's worth noting that Guava provides a convenience method for creating a new directory will all necessary new parent directories with its Files.createParentDirs(File) method.

Retrieving Contents of File as Lines

There are times when it is most convenient to get the contents of a file as a series of lines so that each line can be processed. This is commonly done in Groovy with overloaded versions of readLines() and eachLine(). Guava provides similar functionality to Groovy's File.readLines() with its Files.readLines(File, Charset) method. This is demonstrated in the following code sample.

Demonstrating Files.readLines(File,Charset)
   /**
    * Demonstrate extracting lines from file.
    * 
    * @param fileName Name of file from which lines are desired.
    */
   public void demoRetrievingLinesFromFile(final String fileName)
   {
      final File file = new File(fileName);
      try
      {
         final List<String> lines = Files.readLines(file, Charset.defaultCharset());
         for (final String line : lines)
         {
            out.println(">> " + line);
         }
      }
      catch (IOException ioEx)
      {
         err.println(
              "ERROR trying to retrieve lines from file '"
            + fileName + "' - " + ioEx.toString());
      }
   }

The other overloaded version of readLines is interesting because it allows a LineProcessor callback to be specified to terminate returning of lines earlier than the end of the file.

Reading File's First Line

I have run into numerous situations in which it has been useful to read only the first line of file. This first line might tell my code what type of script is being run, provide XML prolog information, or other interesting overview data of a file's contents. Guava makes it easy to retrieve just the first line with the Files.readFirstLine(File,Charset) method. This is demonstrated in the next code listing.

Demonstrating Files.readFirstLine(File,Charset)
   /**
    * Demonstrate extracting first line of file.
    * 
    * @param fileName File from which first line is to be extracted.
    */
   public void demoRetrievingFirstLineFromFile(final String fileName)
   {
      final File file = new File(fileName);
      try
      {
         final String line = Files.readFirstLine(file, Charset.defaultCharset());
         out.println("First line of '" + fileName + "' is '" + line + "'.");
      }
      catch (IOException fileIoEx)
      {
         err.println(
              "ERROR trying to retrieve first line of file '"
            + fileName + "' - " + fileIoEx.toString());
      }
   }
There's Much More

Although I have discussed and demonstrated several useful Files methods in this post, the class has many more to offer. Some of these include the ability to append to an existing file with Files.append(CharSequence,File,Charset), getting a file's checksum with Files.getChecksum(File,Checksum), getting a file's digest with Files.getDigest(File,MessageDigest), accessing a BufferedReader with Files.newReader(File,Charset), accessing a BufferedWriter with Files.newWriter(File,Charset), and accessing a MappedByteBuffer mapped to an underlying file via overloaded Files.map methods.

Conclusion

File handling in Java is much easier and more convenient with Guava's Files class. Guava brings file-handling convenience to Java applications that cannot make use of Groovy's or Java SE 7's file handling conveniences.

Thursday, November 24, 2011

The Thankful Software Developer

Neil McAllister's Fatal Exception blog post What I'm thankful for as a developer is timely given the Thanksgiving holiday weekend in the United States. In that post, McAllister expresses gratitude for open source tools, online documentation and support, modern IDEs, desktop virtualization, distributed version control, and jQuery. I use the remainder of this post to look at some of the thing I'm thankful for as a developer.

Open Source Tools, Libraries, Frameworks, and More

Like McAllister and doubtless thousands or even millions of other developers, I'm thankful for open source tools and online documentation and support. I've been the beneficiary of others' work in the open source community with products (tools, libraries, frameworks, etc.) such as Struts, Java, JFreeChart, Flex, OpenLaszlo, Groovy, Ruby on Rails, Spring Framework, Guava, Apache Commons, TopLink Essentials, EclipseLink, Ant, JUnit, NetBeans, Eclipse, GlassFish, Apache POI, Apache ActiveMQ, Apache Batik, Apache FOP, and numerous others.

Online Documentation and Resources

I also agree with McAllister that one of the things most developers should be thankful for is online documentation and resources such as articles, blogs, and even electronic books. Each of these types of online resources has its own set of advantages. I covered these and their advantages in the post Useful Online Java Developer Resources. Portions of that post are Java-specific, but many apply to any programming language. As I stated in that post, I am appreciative of content aggregation sites such as DZone, Reddit (programming and java), and Java.net. I am similarly appreciative of focused news sites such as JavaWorld. I similarly appreciate online documentation that vendors make available.

The Internet was hitting mainstream about the same time that I started my professional career in software development. Although it was available from the beginning of my career, it took a couple years to catch on and I think there are a couple of reasons for this. First, the percentage of people putting content on the web was much lower then. Most pages were static on sites such as GeoCities. Although not terribly difficult to use, these sites were just difficult enough to prevent a large percentage of developers from creating web sites and documenting their experiences. Blogging and so-called micro-blogging have changed all that. The second major development leading to increased usefulness of online resources in software development is today's powerful search engines. The Google search engine, as discussed in my post Useful Online Java Developer Resources, is still my favorite. The combination of availability of resources and the ability to quickly and efficiently find relevant resources have made online resources invaluable to software developers.

Community and Lessons Learned

One can learn valuable lessons from hard experience. It's even better, though, to learn from others' hard experiences. I have come to value learning from others' experiences and their lessons learned. There is a wealth of knowledge and insight out there. This is one of the reasons that I've advocated more developers writing blogs.

Contrarian Opinions

I continue to value contrarian opinions in software development. I don't always agree with them, but I appreciate people who challenge potential herd mentality. They may be wrong in the end, but the challenge helps me and others to think through the merits and drawbacks of any particular approach, language, toolkit, framework, or methodology. The Spring Framework arose from someone recognizing flaws with EJBs in 1.x and 2.x versions. Younger developers today may take it for granted that people think badly of EJB 1.x and EJB 2.x, but I remember a lot of Kool-aid drinking going on at the time as "experts" told us what was good for us. Given this, I am appreciate of posts such as Stephen Colebourne's recent Scala feels like EJB 2, and other thoughts and follow-up Scala EJB 2 Follow-up. I don't know enough about Scala to take sides, but it seems nice to see an opinion of Scala that doesn't imply that Scala will solve world hunger.

Modern Language Features

I am thankful to have so many new language features at my disposal. For example, garbage collection has been a tremendous boon to productivity. Similarly, I look forward to improvements in Java such as better modularity (fewer class loader and classpath issues) and lambda expressions.

Choice

I'm a strong believer in using the correct tool for the job and, in most cases, no one tool covers every job as well as I'd like. Therefore I appreciate having a wide variety of languages, tools, and frameworks to choose from. I particularly enjoy the recent surge in alternative JVM languages. I use Java for most production tasks, but use other languages (especially Groovy) where they are a better fit.

The Joy of Software Development

In his famous book The Mythical Man-Month, Frederick P. Brooks, Jr., wrote in the 25th Anniversary Edition prologue, "Too many interests, too many exciting opportunities for learning, research, and thought. What a marvelous predicament! Not only is the end not in sight, the pace is not slackening. We have many future joys." Brooks wrote earlier in the book, "Programming then is fun because it gratifies creative longings built deep within us and delights sensibilities we have in common with all men." Brooks also stated, "To only a fraction of the human race does God give the privilege of earning one's own bread doing what one would have gladly pursued free, for passion. I am very thankful." This is what we have to be most thankful for in software development: there are so many new things to learn and try and play with. I am grateful to have found a career in which I can make a living and enjoy what I do.

Monday, November 21, 2011

Two Generally Useful Guava Annotations

Guava currently (Release 10) includes four annotations in its com.google.common.annotations package: Beta, VisibleForTesting, GwtCompatible, and GwtIncompatible. The last two are specific to use with Google Web Toolkit (GWT), but the former two can be useful in a more general context.

The @Beta annotation is used within Guava's own code base to indicate "that a public API (public class, method or field) is subject to incompatible changes, or even removal, in a future release." Although this annotation is used to indicate at-risk public API constructs in Guava, it can also be used in code that has access to Guava on its classpath. Developers can use this annotation to advertise their own at-risk public API constructs.

The @Beta annotation is defined as a @Documented, which means that it marks something that is part of the public API and should be considered by Javadoc and other source code documentation tools.

The @VisibleForTesting annotation "indicates that the visibility of a type or member has been relaxed to make the code testable." I have never liked having to relax type or member visibility to make something testable. It feels wrong to have to compromise one's design to allow testing to occur. This annotation is better than nothing in such a case because it at least makes it clear to others using the construct that there is a reason for its otherwise surprisingly relaxed visibility.

Conclusion

Guava provides two annotations that are not part of the standard Java distribution, but cover situations that we often run into during Java development. The @Beta annotation indicates a construct in a public API that may be changed or removed. The @VisibleForTesting annotation advertises to other developers (or reminds the code's author) when a decision was made for relaxed visibility to make testing possible or easier.

Monday, November 14, 2011

Effective Javadoc Documentation Illustrated in Familiar Projects

Three years ago, I wrote about practices that I believe lead to more effective Javadoc in my post More Effective Javadoc. In this post, I look at some familiar projects which provide good examples of effective Javadoc documentation practices. I, of course, will only be covering a very tiny representative sample of the many good projects and many good Javadoc ideas that are out there.

1. Advertising Ultimate Demise of Deprecated Method (Guava)

The current version of Guava (Release 10) provides some good examples of more informative statements of deprecation. The next screen snapshot shows the @deprecated text for methods Files.deleteDirectoryContents(File) and Files.deleteRecursively(File). In both methods' cases, the documentation states why the method is deprecated and, most refreshingly, states when it is envisioned that the method will be removed (Release 11 in these cases). I like the idea of stating in the deprecation statement when the deprecated thing is going away. It is easy to learn to ignore @deprecated and @Deprecated if one believes they are really never going to go away. Stating a planned removal version or date implies more urgency in not using deprecated features and provides fair warning to users.

Although the source code for each of these methods employs the @Deprecated annotation, the code from both cases does not specify this text with Javadoc's @deprecated, but instead simply specifies the deprecation details as part of the normal method description text with bold tags around the word "Deprecated." I'm not sure why this was done instead of using the Javadoc tag explicitly intended for this purpose.

2. Documenting Use of an API (Java SE, Java EE, Guava, Joda Time)

When learning how to use a new API, it is helpful when the Javadoc documentation provides examples of using that API. I first learned how to marshal and unmarshal JAXB objects by reading the Javadoc documentation for Marshaller and Unmarshaller respectively. Both of these classes take advantage of class-level documentation to describe how to use the class's APIs.

Guava's class-level description for Stopwatch shows how to use most of that class's features in a concise and easily understandable class usage description.

Use of an API can be documented at the method level as well as at the class level. Examples of this are Guava's Throwables.propagateIfInstanceOf method and the overloaded Throwables.propagateIfPossible methods . The Javadoc documentation for these methods shows "example usage" for each.

API documentation is not limited to the class level or method level. The javax.management package-level documentation provides a nice overview of Java Management Extensions (JMX). The first sentence of the package description (which is what's always shown at top) is simple enough: "Provides the core classes for the Java Management Extensions." However, there are far more details in the rest of the package description. The next screen snapshot shows a small portion of that package documentation.

Another example of a useful package-level description is the package description for Joda Time package org.joda.time. This core package describes many of the concepts applicable to the entire project in one location.

3. Explicitly Declaring Throws Clause for Unchecked Exceptions (Guava)

In my post More Effective Javadoc, I stated that it is best to "document all thrown exceptions" whether they are checked or unchecked. Guava's InetAddresses.forString(String) method's documentation does this, specifying that it throws the runtime exception IllegalArgumentException.

4. Using -linksource (JFreeChart, Guava)

For an open source project, a nice benefit that can be provided to developers using that project is to allow linking of Javadoc documentation to underlying source code. The next screen snapshots indicate this for JFreeChart and Guava. There are two images for each project, with the first image showing the Javadoc with link to source code annotated and the second image showing the source code displayed when the class name is clicked on in the Javadoc.

It is very convenient to be able to move easily between the Javadoc documentation and the source code. Of course, this can also be done in an IDE that supports Javadoc presentation in conjunction with code.

Conclusion

This post has highlighted several familiar projects who Javadoc documentation provides examples of more effective Javadoc-based documentation.

Thursday, November 10, 2011

Speaking at RMOUG Training Days 2012

The Rocky Mountain Oracle Users Group (RMOUG) has announced that the keynote speaker at RMOUG Training Days 2012 will be Cary Millsap of Method R Consulting. RMOUG Training Days 2012 are scheduled for 14-16 February 2012 at the Colorado Convention Center in Denver, Colorado.

I will be speaking at this conference. I will be sole presenter of "JavaFX 2.0: Java in the Rich Internet Application Space" and will be a co-presenter of "Things Developers Wish Managers Knew and Managers Wish Developers Knew." I look forward to returning to the Colorado Convention Center for another edition of RMOUG Training Days.

Saturday, November 5, 2011

Apache Harmony Retiring to the Attic

In a development that seemed destined to happen since IBM's joining OpenJDK, Apache Harmony's Project Management Committee (PMC) has voted 18-2 to move Apache Harmony to the Apache Attic. The Apache Attic page explains the purpose of the Apache Attic: "The Apache Attic was created in November 2008 to provide process and solutions to make it clear when an Apache project has reached its end of life." In short, it has been determined that Apache Harmony has reached its end of life.

Although Apache Harmony enjoyed widespread popularity among numerous Java developers hoping for an open source implementation of standard Java, it was never able to garner support from Sun or Oracle to provide an "independent, compatible implementation of Java SE." With main supporter IBM moving to OpenJDK, this latest development is not too surprising.

Not all projects die once they hit the attic and some live on after retirement. iBatis, for example, lives on as MyBatis (hosted on Google Code). This project "forking" is an example of one of the three approaches for "leaving the attic again." The other two ways of leaving the attic are to return to the Apache Incubator or to recreate a PMC for the project. At this point, my best guess is that none of the three approaches will be used. There is talk on the mailing list of moving it to the public domain, but that also appears unlikely given licensing issues. There has also been hope of Google forking it, but that may be less appealing to Google given the current lawsuit over Android.

With the retirement of Apache Harmony, OpenJDK becomes the sole large open source implementation of Java SE. With the support of Oracle, IBM, Apple, and Twitter (among others), OpenJDK has definitely had the inside track to this position.

Wednesday, November 2, 2011

Guava's Strings Class

In the post Checking for Null or Empty or White Space Only String in Java, I demonstrated common approaches in the Java ecosystem (standard Java, Guava, Apache Commons Lang, and Groovy) for checking whether a String is null, empty, or white space only similar to what C# supports with the String.IsNullOrWhiteSpace method. One of the approaches I showed was a Guava-based approach that made use of the Guava class Strings and its static isNullOrEmpty(String) method. In this post, I look at other useful functionality for working with Strings that is provided by Guava's six "static utility methods pertaining to String" that are bundled into the Strings class.

Using Guava's Strings class is straightforward because of its well-named methods. The following list enumerates the methods (all static) on the Strings class with a brief description of what each does next to the method name (these descriptions are borrowed or adapted from the Javadoc documentation).

isNullOrEmpty

Guava's Strings.isEmptyOrNull(String) method makes it easy to build simple and highly readable conditional statements that check a String for null or emptiness before acting upon said String. As previously mentioned, I have briefly covered this method before. Another code demonstration of this method is shown next.

Code Sample Using Strings.isNullOrEmpty(String)
   /**
    * Print to standard output a string message indicating whether the provided
    * String is null or empty or not null or empty. This method uses Guava's
    * Strings.isNullOrEmpty(String) method.
    * 
    * @param string String to be tested for null or empty.
    */
   private static void printStringStatusNullOrEmpty(final String string)
   {
      out.println(  "String '" + string + "' "
                  + (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
                  + " null or empty.");
   }

   /**
    * Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
    * Strings.
    */
   public static void demoIsNullOrEmpty()
   {
      printHeader("Strings.isNullOrEmpty(String)");
      printStringStatusNullOrEmpty("Dustin");
      printStringStatusNullOrEmpty(null);
      printStringStatusNullOrEmpty("");
   }

The output from running the above code is contained in the next screen snapshot. It shows that true is returned when either null or empty String ("") is passed to Strings.isNullOrEmpty(String).

nullToEmpty and emptyToNull

There are multiple times when one may want to treat a null String as an empty String or wants present a null when an empty String exists. In cases such as these when transformations between null and empty String are desired, The following code snippets demonstrate use of Strings.nullToEmpty(String) and Strings.emptyToNull(String).

nullToEmpty and emptyToNull
   /**
    * Print to standard output a simple message indicating the provided original
    * String and the provided result/output String.
    * 
    * @param originalString Original String.
    * @param resultString Output or result String created by operation.
    * @param operation The operation that acted upon the originalString to create
    *    the resultString.
    */
   private static void printOriginalAndResultStrings(
      final String originalString, final String resultString, final String operation)
   {
      out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
   }

   /** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
   public static void demoEmptyToNull()
   {
      final String operation = "Strings.emptyToNull(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
      printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
   }

   /** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
   public static void demoNullToEmpty()
   {
      final String operation = "Strings.nullToEmpty(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
      printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
   }

The output from running the above code (shown in the next screen snapshot) proves that these methods work as we'd expect: converting null to empty String or converting empty String to null.

padStart and padEnd

Another common practice when dealing with Strings in Java (or any other language) is to pad a String to a certain length with a specified character. Guava supports this easily with its Strings.padStart(String,int,char) and Strings.padEnd(String,int,char) methods, which are demonstrated in the following code listing.

padStart and padEnd
   /**
    * Demonstrate Guava Strings.padStart(String,int,char) method on example
    * Strings.
    */
   public static void demoPadStart()
   {
      final String operation = "Strings.padStart(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
      /* Do NOT call Strings.padStart(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padStart(Strings.java:97)
       */
      //printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);      
   }

   /**
    * Demonstrate Guava Strings.padEnd(String,int,char) method on example
    * Strings.
    */
   public static void demoPadEnd()
   {
      final String operation = "Strings.padEnd(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
      /*
       * Do NOT call Strings.padEnd(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padEnd(Strings.java:129)
       */
      //printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);       
   }

When executed, the above code pads the provided Strings with underscore characters either before or after the provided String depending on which method was called. In both cases, the length of the String was specified as ten. This output is shown in the next screen snapshot.

repeat

A final manipulation technique that Guava's Strings class supports is the ability to easily repeat a given String a specified number of times. This is demonstrated in the next code listing and the corresponding screen snapshot with that code's output. In this example, the provided String is repeated three times.

repeat
   /** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
   public static void demoRepeat()
   {
      final String operation = "Strings.repeat(String,int)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
      /*
       * Do NOT call Strings.repeat(String,int) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
       *         at com.google.common.base.Strings.repeat(Strings.java:153)
       */
      //printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
      printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
   }
Wrapping Up

The above examples are simple because Guava's Strings class is simple to use. The complete class containing the demonstration code shown earlier is now listed.

GuavaStrings.java
package dustin.examples;

import com.google.common.base.Strings;
import static java.lang.System.out;

/**
 * Simple demonstration of Guava's Strings class.
 * 
 * @author Dustin
 */
public class GuavaStrings
{
   /**
    * Print to standard output a string message indicating whether the provided
    * String is null or empty or not null or empty. This method uses Guava's
    * Strings.isNullOrEmpty(String) method.
    * 
    * @param string String to be tested for null or empty.
    */
   private static void printStringStatusNullOrEmpty(final String string)
   {
      out.println(  "String '" + string + "' "
                  + (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
                  + " null or empty.");
   }

   /**
    * Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
    * Strings.
    */
   public static void demoIsNullOrEmpty()
   {
      printHeader("Strings.isNullOrEmpty(String)");
      printStringStatusNullOrEmpty("Dustin");
      printStringStatusNullOrEmpty(null);
      printStringStatusNullOrEmpty("");
   }

   /**
    * Print to standard output a simple message indicating the provided original
    * String and the provided result/output String.
    * 
    * @param originalString Original String.
    * @param resultString Output or result String created by operation.
    * @param operation The operation that acted upon the originalString to create
    *    the resultString.
    */
   private static void printOriginalAndResultStrings(
      final String originalString, final String resultString, final String operation)
   {
      out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
   }

   /** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
   public static void demoEmptyToNull()
   {
      final String operation = "Strings.emptyToNull(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
      printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
   }

   /** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
   public static void demoNullToEmpty()
   {
      final String operation = "Strings.nullToEmpty(String)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
      printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
      printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
   }

   /**
    * Demonstrate Guava Strings.padStart(String,int,char) method on example
    * Strings.
    */
   public static void demoPadStart()
   {
      final String operation = "Strings.padStart(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
      /* Do NOT call Strings.padStart(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padStart(Strings.java:97)
       */
      //printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);      
   }

   /**
    * Demonstrate Guava Strings.padEnd(String,int,char) method on example
    * Strings.
    */
   public static void demoPadEnd()
   {
      final String operation = "Strings.padEnd(String,int,char)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
      /*
       * Do NOT call Strings.padEnd(String,int,char) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
     *         at com.google.common.base.Strings.padEnd(Strings.java:129)
       */
      //printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
      printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);       
   }

   /** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
   public static void demoRepeat()
   {
      final String operation = "Strings.repeat(String,int)";
      printHeader(operation);
      printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
      /*
       * Do NOT call Strings.repeat(String,int) on a null String:
       *    Exception in thread "main" java.lang.NullPointerException
     *         at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
       *         at com.google.common.base.Strings.repeat(Strings.java:153)
       */
      //printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
      printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
   }

   /**
    * Print a separation header to standard output.
    * 
    * @param headerText Text to be placed in separation header.
    */
   public static void printHeader(final String headerText)
   {
      out.println("\n=========================================================");
      out.println("= " + headerText);
      out.println("=========================================================");
   }

   /**
    * Main function for demonstrating Guava's Strings class.
    * 
    * @param arguments Command-line arguments: none anticipated.
    */
   public static void main(final String[] arguments)
   {
      demoIsNullOrEmpty();
      demoEmptyToNull();
      demoNullToEmpty();
      demoPadStart();
      demoPadEnd();
      demoRepeat();
   }
}

The methods for padding and for repeating Strings do not take kindly to null Strings being passed to them. Indeed, passing a null to these three methods leads to NullPointerExceptions being thrown. Interestingly, these are more examples of Guava using the Preconditions class in its own code.

Conclusion

Many Java libraries and frameworks provide String manipulation functionality is classes with names like StringUtil. Guava's Strings class is one such example and the methods it supplies can make Java manipulation of Strings easier and more concise. Indeed, as I use Guava's Strings's methods, I feel almost like I'm using some of Groovy's GDK String goodness.