Before JDK 5.0, the only way to fork off a process and execute it local to the user runtime was to use the exec() method of the java.lang.Runtime class. JDK 5.0 adds a new way of executing a command in a separate process, through a class called ProcessBuilder. You can find ProcessBuilder in the java.lang package (like Runtime andProcess). This tip discusses and compares both approaches.
If you're familiar with the Runtime class, you know that it also allows you to discover memory usage and add a shutdown hook. But probably the most popular use of the class prior to 5.0 was to execute a command in a separate process. This was done through one of the six versions of the exec() method of Runtime:
Here's an example, DoRuntime, that shows how to execute a command with the original Runtime class. The command to run is passed in from the command line.
You get output that looks something like this (which depends on the contents of the directory):
As coded, the command executes in the current working directory with its environment variables intact.
If you want to run the command in a different directory, and you need to add more arguments to the exec() command, you change:
So what's wrong with this approach? Why create a new approach? The problem is that the Runtime.exec approach doesn't necessarily make it easy to customize and invoke subprocesses. The new ProcessBuilder class simplifies things. Through various methods in the class, you can easily modify the environment variables for a process and start the process.
Here's a simple use of ProcessBuilder that duplicates the functions of the DoRuntime example:
With ProcessBuilder, you call start() to execute the command. Prior to calling start(), you can manipulate how the Process will be created. If you want the process to start in a different directory, you don't pass a File in as a command line argument. Instead, you set the process builder's working directory by passing the File to the directory()method:
With methods such as environment() for adding and removing environment variables from the process space, and start() for starting a new process,ProcessBuilder should make it easier to invoke a subprocess with a modified process environment.
You can get the initial set of environment variables by calling the getenv() method of System. Understand that not all platforms support changing environment variables. If you try to change an environment variable on a platform that forbids it, the operation will throw either an UnsupportedOperationException or anIllegalArgumentException. Also, when running with a security manager, you'll need the RuntimePermission for "getenv.*", otherwise a SecurityException will be thrown.
Remember not to forget the start() call after configuring your instance. And, keep using the Process class to manipulate the streams for the process and to get its exit status.
A word of caution about the examples in this tip. It is possible that the examples will deadlock if the subprocess generates enough output to overflow the system. A more robust solution requires draining the process stdout and stderr in separate threads.