External programs can be invoked from Java code using the exec() method of java.lang.Runtime class. As a result, a reference to the Process
class is returned to the JVM. The exitValue()
method can be used to observe the return value of the process. There are several nuances of Rutime.exec() that are discussed below.
Non-Compliant Code Example
This non-compliant code example calls notemaker
, a hypothetical cross-platform notepad application. Despite compiling successfully, it throws an IllegalThreadStateException
if the notemaker
process has not completed.
...
Code Block | ||
---|---|---|
| ||
public class exec { public static void main(String args[]) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); int exitVal = proc.waitFor(); } catch (Throwable t) { t.printStackTrace();} } } |
Compliant Solution
An inefficient solution would be to exhaust the output (or stderr) stream before beginning to wait for the process. A better option is to empty both the stderr and output streams. The code below illustrates this but is not the best solution since it does not process any arguments passed to the external program (notemaker
) and in turn exits with an OS-specific non-zero exit code.
...
Code Block | ||
---|---|---|
| ||
class exec extends Thread { InputStream is; String type; OutputStream os; exec(InputStream is, String type) { this(is, type, null); } exec(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) { ioe.printStackTrace(); } } } public class execMe { 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? exec errorGobbler = new exec(proc.getErrorStream(), "ERROR"); // any output? exec outputGobbler = new exec(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) { t.printStackTrace(); } } } |
References
Javaworld article http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?
More Java Pitfalls, Pitfall 1
Sun Java Documentation