in black and white
Main menu
Share a book About us Home
Biology Business Chemistry Computers Culture Economics Fiction Games Guide History Management Mathematical Medicine Mental Fitnes Physics Psychology Scince Sport Technics

More Java Pitfalls Share Reactor - Daconta M,C.

Daconta M,C. More Java Pitfalls Share Reactor - Wiley publishing, 2003. - 476 p.
ISBN: 0-471-23751-5
Download (direct link): morejavapitfallssharereactor2003.pdf
Previous << 1 .. 3 4 5 6 7 8 < 9 > 10 11 12 13 14 15 .. 166 >> Next

12 00 package org.javapitfalls.item1;
03 import java.util.*;
45 00 import*;
06 public class BadExecJavac2
07 {
08 public static void main(String args[])
09 {
10 try
11 {
12 Runtime rt = Runtime.getRuntime();
13 Process proc = rt.exec("javac");
14 int exitVal = proc.waitFor();
15 System.out.println("Process exitValue: " + exitVal);
16 } catch (Throwable t)
17 {
18 t.printStackTrace();
19 }
20 }
21 }
Listing 1.2
Unfortunately, a run of BadExecJavac2 produces no output. The program hangs and never completes! Why is the javac process never completing? The javadoc documentation provides the answer. It says, "Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock." So, is this just a case of programmers not following "RTFM" (read the f-ing manual)? The answer is partially yes. In this case, reading the manual would get you halfway there. It tells you that you need to handle the streams to your external process but does not tell you how. Besides RTFM, there is another variable at play here that cannot be ignored when you examine the large number of programmer questions and errors over this API in the newsgroups. The Runtime.exec() and Process APIs seem extremely simple, but that simplicity is deceiving, because the simple (translate to obvious) use of the API is prone to error.
When Runtime.execQ Won't 7
The lesson here for the API designer is to reserve simple APIs for simple operations. Operations prone to complexities and platform-specific dependencies should reflect the domain accurately. It is possible for an abstraction to be carried too far. An example of a more complete API to handle these operations is the JConfig library (available at So, now let's follow the documentation and handle the output of the javac process. When you run javac without any arguments, it produces a set of usage statements that describe how to run the program and the meaning of all the available program options. Knowing that this is going to the stderr stream, it is easy to write a program to exhaust that stream before waiting on the process to exit. Listing 1.3 does just that. While that approach will work, it is not a good general solution. That is why the program in Listing 1.3 is named MediocreExecJavac; it is only a mediocre solution. A better solution would empty both the standard error stream and the standard output stream. And the best solution would empty these streams simultaneously (this is demonstrated later).
01 package 1 m e t i s l l a LM t i p a v a j & r o
03 import java.util.*;
04 import*;
06 public class MediocreExecJavac
07 {
08 public static void main(String args[])
09 {
10 try
11 {
12 Runtime rt = Runtime.getRuntime();
13 Process proc = rt.exec("javac");
14 InputStream stderr = proc.getErrorStream();
15 InputStreamReader isr = new InputStreamReader(stderr);
16 BufferedReader br = new BufferedReader(isr);
17 String line = null;
18 System.out.println("<ERROR>");
19 while ( (line = br.readLine()) != null)
20 System.out.println(line);
21 System.out.println("</ERROR>");
22 int exitVal = proc.waitFor();
23 System.out.println("Process exitValue: " + exitVal);
24 } catch (Throwable t)
25 {
26 t.printStackTrace();
27 }
28 }
29 }
Listing 1.3
8 Item 1
A run of MediocreExecJavac produces the following:
Usage: javac <options> <source files> where <options> includes
Generate all debugging info Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
Optimize; may hinder debugging or enlarge class
Generate no warnings
... some output removed for brevity ...
Process exitValue: 2
So, MediocreExecJavac works and produces an exit value of 2. Normally, an exit value of 0 means success and nonzero means error. Unfortunately, the meaning of these exit values is operating system-specific. The Win32 error code for a value of 2 is the error for "file not found." This makes sense, since javac expects us to follow the program with the source code file to compile. So, the second pitfall to avoid with Run-time.exec() is ensuring you process the input and output streams if the program you are launching produces output or expects input.
Going back to windows, many new programmers stumble on Runtime.exec() when trying to use it for nonexecutable commands like dir and copy. So, we replace "javac" with "dir" as the argument to exec() like this:
This line is replaced in the source file called BadExecWinDir, which when run produces the following:
As stated earlier, the error value of 2 means file not found—meaning that the executable named dir.exe could not be found. That is because the directory command is part of the window command interpreter and not a separate executable. To run the window command interpreter, you execute either or cmd.exe depending on the windows operating system you are using. Listing 1.4 runs a copy of the Windows Command Interpreter and then executes the user-supplied command (like dir).
Previous << 1 .. 3 4 5 6 7 8 < 9 > 10 11 12 13 14 15 .. 166 >> Next