/* * @(#)ProcessBuilder.java 1.6 04/02/07 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * @author Martin Buchholz * @version 1.6, 04/02/07 */ package java.lang; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * This class is used to create operating system processes. * *
Each ProcessBuilder
instance manages a collection
* of process attributes. The {@link #start()} method creates a new
* {@link Process} instance with those attributes. The {@link
* #start()} method can be invoked repeatedly from the same instance
* to create new subprocesses with identical or related attributes.
*
*
Each process builder manages these process attributes: * *
user.dir
.
*
* false
, meaning that the standard output and error
* output of a subprocess are sent to two separate streams, which can
* be accessed using the {@link Process#getInputStream()} and {@link
* Process#getErrorStream()} methods. If the value is set to
* true
, the standard error is merged with the standard
* output. This makes it easier to correlate error messages with the
* corresponding output. In this case, the merged data can be read
* from the stream returned by {@link Process#getInputStream()}, while
* reading from the stream returned by {@link
* Process#getErrorStream()} will get an immediate end of file.
*
* Modifying a process builder's attributes will affect processes * subsequently started by that object's {@link #start()} method, but * will never affect previously started processes or the Java process * itself. * *
Most error checking is performed by the {@link #start()} method. * It is possible to modify the state of an object so that {@link * #start()} will fail. For example, setting the command attribute to * an empty list will not throw an exception unless {@link #start()} * is invoked. * *
Note that this class is not synchronized.
* If multiple threads access a ProcessBuilder
instance
* concurrently, and at least one of the threads modifies one of the
* attributes structurally, it must be synchronized externally.
*
*
Starting a new process which uses the default working directory * and environment is easy: * *
* ** Process p = new ProcessBuilder("myCommand", "myArg").start(); *
Here is an example that starts a process with a modified working * directory and environment: * *
* ** ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2"); * Map<String, String> env = pb.environment(); * env.put("VAR1", "myValue"); * env.remove("OTHERVAR"); * env.put("VAR2", env.get("VAR1") + "suffix"); * pb.directory("myDir"); * Process p = pb.start(); *
To start a process with an explicit set of environment
* variables, first call {@link java.util.Map#clear() Map.clear()}
* before adding environment variables.
*
* @since 1.5
*/
public final class ProcessBuilder
{
private Listcommand
list. Subsequent
* updates to the list will be reflected in the state of the
* process builder. It is not checked whether
* command
corresponds to a valid operating system
* command.
null
*/
public ProcessBuilder(Listcommand
* array, in the same order. It is not checked whether
* command
corresponds to a valid operating system
* command.
*
* @param command A string array containing the program and its arguments
*/
public ProcessBuilder(String... command) {
this.command = new ArrayListcommand
list. Subsequent updates to the list will
* be reflected in the state of the process builder. It is not
* checked whether command
corresponds to a valid
* operating system command.
*
* @param command The list containing the program and its arguments
* @return This process builder
*
* @throws NullPointerException
* If the argument is null
*/
public ProcessBuilder command(Listcommand
array, in the same order. It is not
* checked whether command
corresponds to a valid
* operating system command.
*
* @param command A string array containing the program and its arguments
* @return This process builder
*/
public ProcessBuilder command(String... command) {
this.command = new ArrayListThe returned object may be modified using ordinary {@link
* java.util.Map Map} operations. These modifications will be
* visible to subprocesses started via the {@link #start()}
* method. Two ProcessBuilder
instances always
* contain independent process environments, so changes to the
* returned map will never be reflected in any other
* ProcessBuilder
instance or the values returned by
* {@link System#getenv System.getenv}.
*
*
If the system does not support environment variables, an * empty map is returned. * *
The returned map does not permit null keys or values. * Attempting to insert or query the presence of a null key or * value will throw a {@link NullPointerException}. * Attempting to query the presence of a key or value which is not * of type {@link String} will throw a {@link ClassCastException}. * *
The behavior of the returned map is system-dependent. A * system may not allow modifications to environment variables or * may forbid certain variable names or values. For this reason, * attempts to modify the map may fail with * {@link UnsupportedOperationException} or * {@link IllegalArgumentException} * if the modification is not permitted by the operating system. * *
Since the external format of environment variable names and * values is system-dependent, there may not be a one-to-one * mapping between them and Java's Unicode strings. Nevertheless, * the map is implemented in such a way that environment variables * which are not modified by Java code will have an unmodified * native representation in the subprocess. * *
The returned map and its collection views may not obey the * general contract of the {@link Object#equals} and * {@link Object#hashCode} methods. * *
The returned map is typically case-sensitive on all platforms. * *
If a security manager exists, its
* {@link SecurityManager#checkPermission checkPermission}
* method is called with a
* {@link RuntimePermission}("getenv.*")
* permission. This may result in a {@link SecurityException} being
* thrown.
*
*
When passing information to a Java subprocess, * system properties * are generally preferred over environment variables.
* * @return This process builder's environment * * @throws SecurityException * If a security manager exists and its * {@link SecurityManager#checkPermission checkPermission} * method doesn't allow access to the process environment * * @see Runtime#exec(String[],String[],java.io.File) * @see System#getenv() */ public Mapnull
-- this means to use
* the working directory of the current Java process, usually the
* directory named by the system property user.dir
,
* as the working directory of the child process.
*
* @return This process builder's working directory
*/
public File directory() {
return directory;
}
/**
* Sets this process builder's working directory.
*
* Subprocesses subsequently started by this object's {@link
* #start()} method will use this as their working directory.
* The argument may be null
-- this means to use the
* working directory of the current Java process, usually the
* directory named by the system property user.dir
,
* as the working directory of the child process.
*
* @param directory The new working directory
* @return This process builder
*/
public ProcessBuilder directory(File directory) {
this.directory = directory;
return this;
}
/**
* Tells whether this process builder merges standard error and
* standard output.
*
* If this property is true
, then any error output
* generated by subprocesses subsequently started by this object's
* {@link #start()} method will be merged with the standard
* output, so that both can be read using the
* {@link Process#getInputStream()} method. This makes it easier
* to correlate error messages with the corresponding output.
* The initial value is false
.
redirectErrorStream
property
*/
public boolean redirectErrorStream() {
return redirectErrorStream;
}
/**
* Sets this process builder's redirectErrorStream
property.
*
* If this property is true
, then any error output
* generated by subprocesses subsequently started by this object's
* {@link #start()} method will be merged with the standard
* output, so that both can be read using the
* {@link Process#getInputStream()} method. This makes it easier
* to correlate error messages with the corresponding output.
* The initial value is false
.
The new process will * invoke the command and arguments given by {@link #command()}, * in a working directory as given by {@link #directory()}, * with a process environment as given by {@link #environment()}. * *
This method checks that the command is a valid operating * system command. Which commands are valid is system-dependent, * but at the very least the command must be a non-empty list of * non-null strings. * *
If there is a security manager, its
* {@link SecurityManager#checkExec checkExec}
* method is called with the first component of this object's
* command
array as its argument. This may result in
* a {@link SecurityException} being thrown.
*
*
Starting an operating system process is highly system-dependent. * Among the many things that can go wrong are: *
In such cases an exception will be thrown. The exact nature * of the exception is system-dependent, but it will always be a * subclass of {@link IOException}. * *
Subsequent modifications to this process builder will not * affect the returned {@link Process}.
* * @return A new {@link Process} object for managing the subprocess * * @throws NullPointerException * If an element of the command list is null * * @throws IndexOutOfBoundsException * If the command is an empty list (has size0
)
*
* @throws SecurityException
* If a security manager exists and its
* {@link SecurityManager#checkExec checkExec}
* method doesn't allow creation of the subprocess
*
* @throws IOException
* If an I/O error occurs
*
* @see Runtime#exec(String[], String[], java.io.File)
* @see SecurityManager#checkExec(String)
*/
public Process start() throws IOException {
// Must convert to array first -- a malicious user-supplied
// list might try to circumvent the security check.
String[] cmdarray = command.toArray(new String[command.size()]);
for (String arg : cmdarray)
if (arg == null)
throw new NullPointerException();
// Throws IndexOutOfBoundsException if command is empty
String prog = cmdarray[0];
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkExec(prog);
String dir = directory == null ? null : directory.toString();
return ProcessImpl.start(cmdarray,
environment,
dir,
redirectErrorStream);
}
}