java left logo
java middle logo
java right logo
 

Home arrow Java SE Tips arrow java.util arrow From Runtime.exec() to ProcessBuilder
 
 
Main Menu
Home
Java Tutorials
Book Reviews
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Java Network
Java Forums
Java Blog




Most Visited Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews
Top Rated Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews


Statistics
Registered Users: 3908
Java SE Tips: 614
Java ME Tips: 202
Java EE Tips: 183
Other API Tips: 779
Java Applications: 298
Java Libraries: 209
Java Games: 16
Book Reviews:
 
 
 
From Runtime.exec() to ProcessBuilder E-mail
User Rating: / 213
PoorBest 

This Tech Tip reprinted with permission by java.sun.com

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 and Process). 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:

public Process exec(String command)
             throws IOException

public Process exec(String command,
                    String[] envp)
             throws IOException

public Process exec(String command,
                    String[] envp,
                    File dir)
             throws IOException

public Process exec(String[] cmdarray)
             throws IOExceptionjava

public Process exec(String[] cmdarray,
                    String[] envp)
             throws IOException

public Process exec(String[] cmdarray,
                    String[] envp,
                    File dir)
             throws IOException

Before you call the exec() method, you specify the command and its arguments, environment variable settings, and working directory. All versions of the method return a java.lang.Process object for managing the created process. This allows you to get the input or output stream of the subprocess and exit status (among other available information).

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.

   import java.io.*;
   import java.util.*;
   
   public class DoRuntime {
     public static void main(String args[]) throws IOException {

       if (args.length <= 0) {
         System.err.println("Need command to run");
         System.exit(-1);
       }

       Runtime runtime = Runtime.getRuntime();
       Process process = runtime.exec(args);
       InputStream is = process.getInputStream();
       InputStreamReader isr = new InputStreamReader(is);
       BufferedReader br = new BufferedReader(isr);
       String line;

       System.out.printf("Output of running %s is:"
           Arrays.toString(args));

       while ((line = br.readLine()) != null) {
         System.out.println(line);
       }

     }
    

If you run DoRuntime in Solaris like this:

  java DoRuntime ls

You get output that looks something like this (which depends on the contents of the directory):

  Output of running ls is:DoRuntime.class
  DoRuntime.java   

Linux users could also pass in "ls" as the command to get a directory listing.

On a Microsoft Windows platform, commands such as "dir" are internal to the command processor so the single command-line argument would be the quoted string: "cmd /c dir" (again, output would depend on the contents of the directory).

  >  java DoRuntime "cmd /c dir"

  Output of running cmd /c dir is: ...

  Directory of C:\...

  07/15/2005  09:30 AM    <DIR>          .
  07/15/2005  09:30 AM    <DIR>          ..
  07/15/2005  09:30 AM             1,146 DoRuntime.class
  07/15/2005  09:23 AM               724 DoRuntime.java
  ...

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:

    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec(command);

to:

    File file = new File(other directory);
    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec(command, null, file);

The second parameter in the call to the exec() method identifies the environment variable settings. Because the parameter is "null", the subprocess inherits the environment settings of the current process.

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:

   import java.io.*;
   import java.util.*;
   
   public class DoProcessBuilder {
     public static void main(String args[]) throws IOException {

       if (args.length <= 0) {
         System.err.println("Need command to run");
         System.exit(-1);
       }

       Process process = new ProcessBuilder(args).start();
       InputStream is = process.getInputStream();
       InputStreamReader isr = new InputStreamReader(is);
       BufferedReader br = new BufferedReader(isr);
       String line;

       System.out.printf("Output of running %s is:"
          Arrays.toString(args));

       while ((line = br.readLine()) != null) {
         System.out.println(line);
       }

     }
    
  > java DoProcessBuilder ls
  Output of running ls is:DoProcessBuilder.class 
  DoProcessBuilder.java
  DoRuntime.class
  DoRuntime.java   

Notice that the following two lines in DoRuntime:

    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec(command);

were changed to the following line in DoProcessBuilder:

    Process process = new ProcessBuilder(command).start();

The ProcessBuilder class has two constructors. One constructor accepts a List for the command and its arguments. The other constructor accepts a variable number of String arguments.

   public ProcessBuilder(List command)
   public ProcessBuilder(String... command)

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:

   public ProcessBuilder directory(File directory)

There isn't an obvious setter type method in ProcessBuilder for setting environment variables. Instead, you get a Map of the variables through the environment() method, then you manipulate the Map:

   ProcessBuilder processBuilder = new ProcessBuilder(command);
   Map env = processBuilder.environment();
   // manipulate env

The options for manipulating the environment include adding environment variables with the put() method, and removing them with the remove() method. For example:

   ProcessBuilder processBuilder = new ProcessBuilder(
                                       command, arg1, arg2);
   Map env = processBuilder.environment();
   env.put("var1", "value");
   env.remove("var3");

After the environment variables and directory are set, call start():

   processBuilder.directory("Dir");
   Process p = processBuilder.start();

You can also clear() all the variables from the environment and explicitly set the ones you want.

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 an IllegalArgumentException. 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.

For more information about ProcessBuilder, see the class definition.

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.


 Related Tips

 
< Prev   Next >
 
       
         
     
 
 
 
   
 
 
java bottom left
java bottom middle
java bottom right
RSS 0.91 FeedRSS 1.0 FeedRSS 2.0 FeedATOM FeedOPML Feed

Home - About Us - Privacy Policy
Copyright 2005 - 2008 www.java-tips.org
Java is a trademark of Sun Microsystems, Inc.