Download (direct link):
4 Item 1
Effective String Tokenizing (Item 18). The operation of the StringTokenizer class is frequently misunderstood. Interesting problems occur with multiple character delimiting strings. This pitfall examines those problems, and explains how they can be avoided.
JLayered Pane Pitfalls (Item 19). This pitfall examines issues with using Swing's JLayered Pane, particularly related to the use of layout mangers with it.
When File.renameTo() Won't (Item 20). The interaction with files in Java can produce unexpected results, particularly in working across filesystems or platforms. This pitfall examines those unexpected results and offers solutions for resolving them.
Item 1: When Runtime.exec() Won't1
The class java.lang.Runtime has a static method called getRuntime() to retrieve the current Java runtime environment. This is the only way to get a reference to the Runtime object. With that reference you can run external programs by invoking the exec() method of the Runtime class. One popular reason to do this is to launch a browser to display some kind of help page in HTML. There are four overloaded versions of the exec() command. Those method prototypes are:
¦ public Process exec(String command);
¦¦ public Process exec(String  cmdArray);
¦ public Process exec(String command, String  envp);
¦¦ public Process exec(String  cmdArray, String  envp);
The general idea behind all of the methods is that a command (and possible a set of arguments) are passed to an operating system-specific function call to create an operating system-specific process (a running program) with a reference to a Process class returned to the Java Virtual Machine (VM). The Process class is an abstract class because there will be a specific subclass of Process for each operating system. There are three possible input parameters to these methods: a single String that represents both the program to execute and any arguments to that program, an array of Strings that separate the program from its arguments, and an array of environment variables. The environment variables are passed in the form name=value. It is important to note that if you use the version of exec() with a single String for both the program and its arguments, the String is parsed using whitespace as the delimiter via the StringTokenizer class.
The prevalent first test of an API is to code its most obvious methods. For example, to exec a process that is external to the JVM, we use the exec() method. To see the value that the external process returns, we use the exitValue() method on the Process class. In our first example, we will attempt to execute the Java compiler (javac.exe). Listing 1.1 is a program to do that.
1 This pitfall was first printed by JavaWorld (www.javaworld.com) in "When Runtime.exec() won't", December 2000 (http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?) and is reprinted here with permission. The pitfall has been updated from reader feedback.
When Runtime.exec() Won't 5
01 02 package org.javapitfalls.item1;
03 import java.util.*;
04 05 import java.io.*;
06 public class BadExecJavac
08 public static void main(String args)
12 Runtime rt = Runtime.getRuntime();
13 Process proc = rt.exec("javac");
14 int exitVal = proc.exitValue();
15 System.out.println("Process exitValue: " + exitVal);
16 } catch (Throwable t)
Listing 1.1 BadExecJavac.java
A run of BadExecJavac produces the following:
E:\classes\org\javapitfalls\item1 >java ^
java.lang.IllegalThreadStateException: process has not exited at java.lang.Win32Process.exitValue(Native Method) at BadExecJavac.main(BadExecJavac.java:13)
The program failed to work because the exitValue() method will throw an IllegalThreadStateException if the external process has not yet completed. While this is stated in the documentation, it is strange in that it begs the question: why not just make this method wait until it can give me a valid answer? A more thorough look at the methods available in the Process class reveals a waitFor() method that does precisely that. In fact, the waitFor() method returns the exit value, which means that you would not use both methods in conjunction. You choose one or the other. The only possible reason for you to use the exitValue() method over the waitFor() method is that you do not want to have your program block waiting on an external process that may never complete. Personally, I would prefer a boolean parameter called waitFor be passed into the exitValue() method to determine whether or not the current thread should wait. I think a boolean would be better because the name exitValue() is a better name for this method and it is unnecessary to have two methods perform the same function under different conditions. Such simple "condition" discrimination is the domain of an input parameter.
6 Item 1
So, the first pitfall relating to Runtime.exec() is beware the IllegalThread-StateException and either catch it or wait for the process to complete. Now, let's fix the problem in the above program and wait for the process to complete. In Listing 1.2, the program again attempts to exec the program javac.exe and then waits for the external process to complete.