KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > lang > ProcessBuilder


1 /*
2  * @(#)ProcessBuilder.java 1.6 04/02/07
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  *
7  * @author Martin Buchholz
8  * @version 1.6, 04/02/07
9  */

10
11 package java.lang;
12
13 import java.io.File JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18
19 /**
20  * This class is used to create operating system processes.
21  *
22  * <p>Each <code>ProcessBuilder</code> instance manages a collection
23  * of process attributes. The {@link #start()} method creates a new
24  * {@link Process} instance with those attributes. The {@link
25  * #start()} method can be invoked repeatedly from the same instance
26  * to create new subprocesses with identical or related attributes.
27  *
28  * <p>Each process builder manages these process attributes:
29  *
30  * <ul>
31  *
32  * <li>a <i>command</i>, a list of strings which signifies the
33  * external program file to be invoked and its arguments, if any.
34  * Which string lists represent a valid operating system command is
35  * system-dependent. For example, it is common for each conceptual
36  * argument to be an element in this list, but there are operating
37  * systems where programs are expected to tokenize command line
38  * strings themselves - on such a system a Java implementation might
39  * require commands to contain exactly two elements.
40  *
41  * <li>an <i>environment</i>, which is a system-dependent mapping from
42  * <i>variables</i> to <i>values</i>. The initial value is a copy of
43  * the environment of the current process (see {@link System#getenv()}).
44  *
45  * <li>a <i>working directory</i>. The default value is the current
46  * working directory of the current process, usually the directory
47  * named by the system property <code>user.dir</code>.
48  *
49  * <li>a <i>redirectErrorStream</i> property. Initially, this property
50  * is <code>false</code>, meaning that the standard output and error
51  * output of a subprocess are sent to two separate streams, which can
52  * be accessed using the {@link Process#getInputStream()} and {@link
53  * Process#getErrorStream()} methods. If the value is set to
54  * <code>true</code>, the standard error is merged with the standard
55  * output. This makes it easier to correlate error messages with the
56  * corresponding output. In this case, the merged data can be read
57  * from the stream returned by {@link Process#getInputStream()}, while
58  * reading from the stream returned by {@link
59  * Process#getErrorStream()} will get an immediate end of file.
60  *
61  * </ul>
62  *
63  * <p>Modifying a process builder's attributes will affect processes
64  * subsequently started by that object's {@link #start()} method, but
65  * will never affect previously started processes or the Java process
66  * itself.
67  *
68  * <p>Most error checking is performed by the {@link #start()} method.
69  * It is possible to modify the state of an object so that {@link
70  * #start()} will fail. For example, setting the command attribute to
71  * an empty list will not throw an exception unless {@link #start()}
72  * is invoked.
73  *
74  * <p><strong>Note that this class is not synchronized.</strong>
75  * If multiple threads access a <code>ProcessBuilder</code> instance
76  * concurrently, and at least one of the threads modifies one of the
77  * attributes structurally, it <i>must</i> be synchronized externally.
78  *
79  * <p>Starting a new process which uses the default working directory
80  * and environment is easy:
81  *
82  * <blockquote><pre>
83  * Process p = new ProcessBuilder("myCommand", "myArg").start();
84  * </pre></blockquote>
85  *
86  * <p>Here is an example that starts a process with a modified working
87  * directory and environment:
88  *
89  * <blockquote><pre>
90  * ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
91  * Map&lt;String, String&gt; env = pb.environment();
92  * env.put("VAR1", "myValue");
93  * env.remove("OTHERVAR");
94  * env.put("VAR2", env.get("VAR1") + "suffix");
95  * pb.directory("myDir");
96  * Process p = pb.start();
97  * </pre></blockquote>
98  *
99  * <p>To start a process with an explicit set of environment
100  * variables, first call {@link java.util.Map#clear() Map.clear()}
101  * before adding environment variables.
102  *
103  * @since 1.5
104  */

105
106 public final class ProcessBuilder
107 {
108     private List JavaDoc<String JavaDoc> command;
109     private File JavaDoc directory;
110     private Map JavaDoc<String JavaDoc,String JavaDoc> environment;
111     private boolean redirectErrorStream;
112
113     /**
114      * Constructs a process builder with the specified operating
115      * system program and arguments. This constructor does <i>not</i>
116      * make a copy of the <code>command</code> list. Subsequent
117      * updates to the list will be reflected in the state of the
118      * process builder. It is not checked whether
119      * <code>command</code> corresponds to a valid operating system
120      * command.</p>
121      *
122      * @param command The list containing the program and its arguments
123      *
124      * @throws NullPointerException
125      * If the argument is <code>null</code>
126      */

127     public ProcessBuilder(List JavaDoc<String JavaDoc> command) {
128     if (command == null)
129         throw new NullPointerException JavaDoc();
130     this.command = command;
131     }
132
133     /**
134      * Constructs a process builder with the specified operating
135      * system program and arguments. This is a convenience
136      * constructor that sets the process builder's command to a string
137      * list containing the same strings as the <code>command</code>
138      * array, in the same order. It is not checked whether
139      * <code>command</code> corresponds to a valid operating system
140      * command.</p>
141      *
142      * @param command A string array containing the program and its arguments
143      */

144     public ProcessBuilder(String JavaDoc... command) {
145     this.command = new ArrayList JavaDoc<String JavaDoc>(command.length);
146     for (String JavaDoc arg : command)
147         this.command.add(arg);
148     }
149
150     /**
151      * Sets this process builder's operating system program and
152      * arguments. This method does <i>not</i> make a copy of the
153      * <code>command</code> list. Subsequent updates to the list will
154      * be reflected in the state of the process builder. It is not
155      * checked whether <code>command</code> corresponds to a valid
156      * operating system command.</p>
157      *
158      * @param command The list containing the program and its arguments
159      * @return This process builder
160      *
161      * @throws NullPointerException
162      * If the argument is <code>null</code>
163      */

164     public ProcessBuilder JavaDoc command(List JavaDoc<String JavaDoc> command) {
165     if (command == null)
166         throw new NullPointerException JavaDoc();
167     this.command = command;
168     return this;
169     }
170
171     /**
172      * Sets this process builder's operating system program and
173      * arguments. This is a convenience method that sets the command
174      * to a string list containing the same strings as the
175      * <code>command</code> array, in the same order. It is not
176      * checked whether <code>command</code> corresponds to a valid
177      * operating system command.</p>
178      *
179      * @param command A string array containing the program and its arguments
180      * @return This process builder
181      */

182     public ProcessBuilder JavaDoc command(String JavaDoc... command) {
183     this.command = new ArrayList JavaDoc<String JavaDoc>(command.length);
184     for (String JavaDoc arg : command)
185         this.command.add(arg);
186     return this;
187     }
188
189     /**
190      * Returns this process builder's operating system program and
191      * arguments. The returned list is <i>not</i> a copy. Subsequent
192      * updates to the list will be reflected in the state of this
193      * process builder.</p>
194      *
195      * @return This process builder's program and its arguments
196      */

197     public List JavaDoc<String JavaDoc> command() {
198     return command;
199     }
200
201     /**
202      * Returns a string map view of this process builder's environment.
203      *
204      * Whenever a process builder is created, the environment is
205      * initialized to a copy of the current process environment (see
206      * {@link System#getenv()}). Subprocesses subsequently started by
207      * this object's {@link #start()} method will use this map as
208      * their environment.
209      *
210      * <p>The returned object may be modified using ordinary {@link
211      * java.util.Map Map} operations. These modifications will be
212      * visible to subprocesses started via the {@link #start()}
213      * method. Two <code>ProcessBuilder</code> instances always
214      * contain independent process environments, so changes to the
215      * returned map will never be reflected in any other
216      * <code>ProcessBuilder</code> instance or the values returned by
217      * {@link System#getenv System.getenv}.
218      *
219      * <p>If the system does not support environment variables, an
220      * empty map is returned.
221      *
222      * <p>The returned map does not permit null keys or values.
223      * Attempting to insert or query the presence of a null key or
224      * value will throw a {@link NullPointerException}.
225      * Attempting to query the presence of a key or value which is not
226      * of type {@link String} will throw a {@link ClassCastException}.
227      *
228      * <p>The behavior of the returned map is system-dependent. A
229      * system may not allow modifications to environment variables or
230      * may forbid certain variable names or values. For this reason,
231      * attempts to modify the map may fail with
232      * {@link UnsupportedOperationException} or
233      * {@link IllegalArgumentException}
234      * if the modification is not permitted by the operating system.
235      *
236      * <p>Since the external format of environment variable names and
237      * values is system-dependent, there may not be a one-to-one
238      * mapping between them and Java's Unicode strings. Nevertheless,
239      * the map is implemented in such a way that environment variables
240      * which are not modified by Java code will have an unmodified
241      * native representation in the subprocess.
242      *
243      * <p>The returned map and its collection views may not obey the
244      * general contract of the {@link Object#equals} and
245      * {@link Object#hashCode} methods.
246      *
247      * <p>The returned map is typically case-sensitive on all platforms.
248      *
249      * <p>If a security manager exists, its
250      * {@link SecurityManager#checkPermission checkPermission}
251      * method is called with a
252      * <code>{@link RuntimePermission}("getenv.*")</code>
253      * permission. This may result in a {@link SecurityException} being
254      * thrown.
255      *
256      * <p>When passing information to a Java subprocess,
257      * <a HREF=System.html#EnvironmentVSSystemProperties>system properties</a>
258      * are generally preferred over environment variables.</p>
259      *
260      * @return This process builder's environment
261      *
262      * @throws SecurityException
263      * If a security manager exists and its
264      * {@link SecurityManager#checkPermission checkPermission}
265      * method doesn't allow access to the process environment
266      *
267      * @see Runtime#exec(String[],String[],java.io.File)
268      * @see System#getenv()
269      */

270     public Map JavaDoc<String JavaDoc,String JavaDoc> environment() {
271     SecurityManager JavaDoc security = System.getSecurityManager();
272     if (security != null)
273         security.checkPermission(new RuntimePermission JavaDoc("getenv.*"));
274
275     if (environment == null)
276         environment = ProcessEnvironment.environment();
277
278     assert environment != null;
279
280     return environment;
281     }
282
283     // Only for use by Runtime.exec(...envp...)
284
ProcessBuilder JavaDoc environment(String JavaDoc[] envp) {
285     assert environment == null;
286     if (envp != null) {
287         environment = ProcessEnvironment.emptyEnvironment(envp.length);
288         assert environment != null;
289
290         for (String JavaDoc envstring : envp) {
291         // Before 1.5, we blindly passed invalid envstrings
292
// to the child process.
293
// We would like to throw an exception, but do not,
294
// for compatibility with old broken code.
295

296         // Silently discard any trailing junk.
297
if (envstring.indexOf((int) '\u0000') != -1)
298             envstring = envstring.replaceFirst("\u0000.*", "");
299
300         int eqlsign =
301             envstring.indexOf('=', ProcessEnvironment.MIN_NAME_LENGTH);
302         // Silently ignore envstrings lacking the required `='.
303
if (eqlsign != -1)
304             environment.put(envstring.substring(0,eqlsign),
305                     envstring.substring(eqlsign+1));
306         }
307     }
308     return this;
309     }
310
311     /**
312      * Returns this process builder's working directory.
313      *
314      * Subprocesses subsequently started by this object's {@link
315      * #start()} method will use this as their working directory.
316      * The returned value may be <code>null</code> -- this means to use
317      * the working directory of the current Java process, usually the
318      * directory named by the system property <code>user.dir</code>,
319      * as the working directory of the child process.</p>
320      *
321      * @return This process builder's working directory
322      */

323     public File JavaDoc directory() {
324     return directory;
325     }
326
327     /**
328      * Sets this process builder's working directory.
329      *
330      * Subprocesses subsequently started by this object's {@link
331      * #start()} method will use this as their working directory.
332      * The argument may be <code>null</code> -- this means to use the
333      * working directory of the current Java process, usually the
334      * directory named by the system property <code>user.dir</code>,
335      * as the working directory of the child process.</p>
336      *
337      * @param directory The new working directory
338      * @return This process builder
339      */

340     public ProcessBuilder JavaDoc directory(File JavaDoc directory) {
341     this.directory = directory;
342     return this;
343     }
344
345     /**
346      * Tells whether this process builder merges standard error and
347      * standard output.
348      *
349      * <p>If this property is <code>true</code>, then any error output
350      * generated by subprocesses subsequently started by this object's
351      * {@link #start()} method will be merged with the standard
352      * output, so that both can be read using the
353      * {@link Process#getInputStream()} method. This makes it easier
354      * to correlate error messages with the corresponding output.
355      * The initial value is <code>false</code>.</p>
356      *
357      * @return This process builder's <code>redirectErrorStream</code> property
358      */

359     public boolean redirectErrorStream() {
360     return redirectErrorStream;
361     }
362
363     /**
364      * Sets this process builder's <code>redirectErrorStream</code> property.
365      *
366      * <p>If this property is <code>true</code>, then any error output
367      * generated by subprocesses subsequently started by this object's
368      * {@link #start()} method will be merged with the standard
369      * output, so that both can be read using the
370      * {@link Process#getInputStream()} method. This makes it easier
371      * to correlate error messages with the corresponding output.
372      * The initial value is <code>false</code>.</p>
373      *
374      * @param redirectErrorStream The new property value
375      * @return This process builder
376      */

377     public ProcessBuilder JavaDoc redirectErrorStream(boolean redirectErrorStream) {
378     this.redirectErrorStream = redirectErrorStream;
379     return this;
380     }
381
382     /**
383      * Starts a new process using the attributes of this process builder.
384      *
385      * <p>The new process will
386      * invoke the command and arguments given by {@link #command()},
387      * in a working directory as given by {@link #directory()},
388      * with a process environment as given by {@link #environment()}.
389      *
390      * <p>This method checks that the command is a valid operating
391      * system command. Which commands are valid is system-dependent,
392      * but at the very least the command must be a non-empty list of
393      * non-null strings.
394      *
395      * <p>If there is a security manager, its
396      * {@link SecurityManager#checkExec checkExec}
397      * method is called with the first component of this object's
398      * <code>command</code> array as its argument. This may result in
399      * a {@link SecurityException} being thrown.
400      *
401      * <p>Starting an operating system process is highly system-dependent.
402      * Among the many things that can go wrong are:
403      * <ul>
404      * <li>The operating system program file was not found.
405      * <li>Access to the program file was denied.
406      * <li>The working directory does not exist.
407      * </ul>
408      *
409      * <p>In such cases an exception will be thrown. The exact nature
410      * of the exception is system-dependent, but it will always be a
411      * subclass of {@link IOException}.
412      *
413      * <p>Subsequent modifications to this process builder will not
414      * affect the returned {@link Process}.</p>
415      *
416      * @return A new {@link Process} object for managing the subprocess
417      *
418      * @throws NullPointerException
419      * If an element of the command list is null
420      *
421      * @throws IndexOutOfBoundsException
422      * If the command is an empty list (has size <code>0</code>)
423      *
424      * @throws SecurityException
425      * If a security manager exists and its
426      * {@link SecurityManager#checkExec checkExec}
427      * method doesn't allow creation of the subprocess
428      *
429      * @throws IOException
430      * If an I/O error occurs
431      *
432      * @see Runtime#exec(String[], String[], java.io.File)
433      * @see SecurityManager#checkExec(String)
434      */

435     public Process JavaDoc start() throws IOException JavaDoc {
436     // Must convert to array first -- a malicious user-supplied
437
// list might try to circumvent the security check.
438
String JavaDoc[] cmdarray = command.toArray(new String JavaDoc[command.size()]);
439     for (String JavaDoc arg : cmdarray)
440         if (arg == null)
441         throw new NullPointerException JavaDoc();
442     // Throws IndexOutOfBoundsException if command is empty
443
String JavaDoc prog = cmdarray[0];
444
445     SecurityManager JavaDoc security = System.getSecurityManager();
446     if (security != null)
447         security.checkExec(prog);
448
449     String JavaDoc dir = directory == null ? null : directory.toString();
450
451     return ProcessImpl.start(cmdarray,
452                  environment,
453                  dir,
454                  redirectErrorStream);
455     }
456 }
457
Popular Tags