...
This recommendation discusses several issues resulting from the improper use of the exec()
method. Similarly the is also prone to misuse.
Noncompliant Code Example (exitValue()
)
This noncompliant code example invokes notemaker
, a hypothetical cross-platform notepad application, using the exec()
method, which returns an object of a subclass of the abstract
class java.lang.Process
. The exitValue()
method returns the exit value for processes that have terminated; but it throws an IllegalThreadStateException
when invoked on an active process. Because this noncompliant example program fails to wait for the notemaker
process to terminate, the call to exitValue()
is likely to throw an {IllegalThreadStateException}}.
Code Block | ||
---|---|---|
| ||
public class Exec { public static void main(String args[]) throws IOException { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); int exitVal = proc.exitValue(); } } |
Noncompliant Code Example (waitFor()
)
In this noncompliant code example, the waitFor()
method blocks the calling thread until the invoked process terminates. This prevents the IllegalThreadStateException
seen in the previous example. However, the example program may experience an arbitrary delay before termination. First, the invoked notemaker process could legitimately require lengthy execution before completion. Although this possibility can present difficulties in practice, it is irrelevant for the purposes of this guideline. Second, output from the notemaker process can exhaust the available buffer for the standard output or standard error stream. When this occurs, it can block the notemaker process as well, preventing all forward progress for both processes. Note that many platforms limit the buffer size available for the standard output streams.
...
Code Block | ||
---|---|---|
| ||
class ExecStreamGobbler extends Thread { InputStream is; String type; OutputStream os; ExecStreamGobbler(InputStream is, String type) { this(is, type, null); } ExecStreamGobbler(InputStream is, String type, OutputStream redirect) { this.is = is; this.type = type; this.os = redirect; } public void run() { try { PrintWriter pw = null; if (os != null) { pw = new PrintWriter(os); } InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { if (pw != null) { pw.println(line); pw.flush(); } System.out.println(type + ">" + line); } if (pw != null) { pw.flush(); } } catch (IOException ioe) { /* Forward to handler */ } } } public class ExecMeExec { public static void main(String[] args) { // ... perform command argument check ... try { FileOutputStream fos = new FileOutputStream("c:\\output.txt"); Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); // Any error message? ExecStreamGobbler errorGobbler = new ExecStreamGobbler(proc.getErrorStream(), "ERROR"); // Any output? ExecStreamGobbler outputGobbler = new ExecStreamGobbler(proc.getInputStream(), "OUTPUT", fos); errorGobbler.start(); outputGobbler.start(); // Any error? int exitVal = proc.waitFor(); errorGobbler.join(); // Handle condition where the outputGobbler.join(); // process ends before the threads finish fos.flush(); fos.close(); } catch (Throwable t) { /* forward to handler */ } } } |
When the output and error streams are handled separately, they must be drained concurrentlyindependently. Failure to do so can cause the program to block indefinitely.
Compliant Solution (
...
Windows)
This compliant solution (based on the Sun forums query Runtime.exec hangs even If I drain output), uses a ProcessBuilder
to simplify the handling mechanism by merging the error and output streams. The readToPrompt()
method reads the output of the invoked process; details of its implementation necessarily depend on the exact formatting of the command prompt used by the platform's command interpreter.
Code Block | ||
---|---|---|
| ||
public class CmdExec { public static void main(String[] args) throws IOException { ProcessBuilder pb = new ProcessBuilder("cmd"); pb = pb.redirectErrorStream(true); Process p = pb.start(); InputStream is = p.getInputStream(); OutputStream os = p.getOutputStream(); PrintWriter pw = new PrintWriter(os, true); readToPrompt(is); pw.println("dir"); readToPrompt(is); } private static void readToPrompt(InputStream is) throws IOException { String s = ""; for (;;) { int i = is.read(); if (i < 0) { System.out.println(); System.out.println("EOF"); System.exit(0); } char c = (char)i; // Safe s += c; if (s.endsWith("\r\n") { System.out.print(s); s = ""; } // Detects prompt, to break out if (c == '>' && s.length() > 2 && s.charAt(1) == ':') { System.out.print(s); break; } } } } |
...