Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

INVOKING PROGRAMS FROM JAVA
APPLICATIONS

These tips were developed using Java(tm) 2 SDK, Standard Edition, 
v 1.2.2.
The issue Using remote method invocation to access legacy databases
discussed how RMI (Remote Method Invocation) can be used to communicate between programs. Another technique for communication is the Runtime.exec method. You can use this method to invoke a program from within a running Java application. Runtime.exec also allows you to perform operations related to the program, such as control the program's standard input and output, wait until it completes execution, and get its exit status. Here's a simple C application that illustrates these features:
#include int main() { printf("testing\n"); return 0; } This application writes a string "testing" to standard output, and then terminates with an exit status of 0. To execute this simple program within a Java application, compile the C application: $ cc test.c -o test (your C compiler might require different parameters) and then invoke the program using this Java code: import java.io.*; import java.util.ArrayList; public class ExecDemo { static public String[] runCommand(String cmd) throws IOException { // set up list to capture command output lines ArrayList list = new ArrayList(); // start command running Process proc = Runtime.getRuntime().exec(cmd); // get command's output stream and // put a buffered reader input stream on it InputStream istr = proc.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(istr)); // read output lines from command String str; while ((str = br.readLine()) != null) list.add(str); // wait for command to terminate try { proc.waitFor(); } catch (InterruptedException e) { System.err.println("process was interrupted"); } // check its exit value if (proc.exitValue() != 0) System.err.println("exit value was non-zero"); // close stream br.close(); // return list of strings to caller return (String[])list.toArray(new String[0]); } public static void main(String args[]) throws IOException { try { // run a command String outlist[] = runCommand("test"); // display its output for (int i = 0; i < outlist.length; i++) System.out.println(outlist[i]); } catch (IOException e) { System.err.println(e); } } } The demo calls a method runCommand to actually run the program. String outlist[] = runCommand("test"); This method hooks an input stream to the program's output stream, so that it can read the program's output, and save it into a list of strings. InputStream istr = proc.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(istr)); String str; while ((str = br.readLine()) != null) list.add(str); After all the output has been read, waitFor is called to wait on the program to terminate, and then exitValue is called to get the exit value of the program. If you've done much systems programming, for example with UNIX system calls, this approach will be a familiar one. (This example assumes that the current directory is in your shell search path; more on this subject below). If you're on a UNIX system, you can replace: runCommand("test"); with: runCommand("ls -l"); to get a full (long) listing of files in the current directory. But getting a listing in this way highlights a fundamental weakness of using Runtime.exec -- the programs you invoke aren't necessarily portable. That is, Runtime.exec is portable, and exists across different Java implementations, but the invoked programs are not. There's no program named "ls" on Windows systems. Suppose that you're running Windows NT and you decide to remedy this problem by saying: runCommand("dir"); where "dir" is the equivalent command to "ls". This doesn't work, because "dir" is not an executable program. Instead it is built into the shell (command interpreter) CMD.EXE. So you need to say: runCommand("cmd /c dir"); where "cmd /c command" says "invoke a shell and execute the single specified command and then exit." Similarly, for a UNIX shell like the Korn shell, you might say: runCommand("ksh -c alias"); where "alias" is a command built into the shell. The output in this case is a list of all your shell aliases. In the example above of obtaining a directory listing, you can use portable Java facilities to achieve the same result. For example, saying: import java.io.File; public class DumpFiles { public static void main(String args[]) { String list[] = new File(".").list(); for (int i = 0; i < list.length; i++) System.out.println(list[i]); } } gives you a list of all files and directories in the current directory. So using ls/dir probably doesn't make sense in most cases. A situation where it makes sense to use Runtime.exec is one in which you allow the user to specify an editor or word processor (like Emacs or Vi or Word) to edit files. This is a common feature in large applications. The application would have a configuration file with the local path of the editor, and Runtime.exec would be called with this path. One tricky aspect of Runtime.exec is how it finds files. For example, if you say: Runtime.exec("ls"); how is the "ls" program found? Experiments with JDK 1.2.2 indicate that the PATH environment variable is searched. This is just like what happens when you execute commands with a shell. But the documentation doesn't address this point, so it pays to be careful. You can't assume that a search path has been set. It might make more sense to use Runtime.exec in a limited way as discussed above, with absolute paths specified. There's also a variant of Runtime.exec that allows you to specify environment strings.

Any comments? email to:
richard@dreamscity.net