|
1 /* |
|
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package java.lang; |
|
27 |
|
28 import java.io.File; |
|
29 import java.io.IOException; |
|
30 import java.util.ArrayList; |
|
31 import java.util.List; |
|
32 import java.util.Map; |
|
33 |
|
34 /** |
|
35 * This class is used to create operating system processes. |
|
36 * |
|
37 * <p>Each <code>ProcessBuilder</code> instance manages a collection |
|
38 * of process attributes. The {@link #start()} method creates a new |
|
39 * {@link Process} instance with those attributes. The {@link |
|
40 * #start()} method can be invoked repeatedly from the same instance |
|
41 * to create new subprocesses with identical or related attributes. |
|
42 * |
|
43 * <p>Each process builder manages these process attributes: |
|
44 * |
|
45 * <ul> |
|
46 * |
|
47 * <li>a <i>command</i>, a list of strings which signifies the |
|
48 * external program file to be invoked and its arguments, if any. |
|
49 * Which string lists represent a valid operating system command is |
|
50 * system-dependent. For example, it is common for each conceptual |
|
51 * argument to be an element in this list, but there are operating |
|
52 * systems where programs are expected to tokenize command line |
|
53 * strings themselves - on such a system a Java implementation might |
|
54 * require commands to contain exactly two elements. |
|
55 * |
|
56 * <li>an <i>environment</i>, which is a system-dependent mapping from |
|
57 * <i>variables</i> to <i>values</i>. The initial value is a copy of |
|
58 * the environment of the current process (see {@link System#getenv()}). |
|
59 * |
|
60 * <li>a <i>working directory</i>. The default value is the current |
|
61 * working directory of the current process, usually the directory |
|
62 * named by the system property <code>user.dir</code>. |
|
63 * |
|
64 * <li>a <i>redirectErrorStream</i> property. Initially, this property |
|
65 * is <code>false</code>, meaning that the standard output and error |
|
66 * output of a subprocess are sent to two separate streams, which can |
|
67 * be accessed using the {@link Process#getInputStream()} and {@link |
|
68 * Process#getErrorStream()} methods. If the value is set to |
|
69 * <code>true</code>, the standard error is merged with the standard |
|
70 * output. This makes it easier to correlate error messages with the |
|
71 * corresponding output. In this case, the merged data can be read |
|
72 * from the stream returned by {@link Process#getInputStream()}, while |
|
73 * reading from the stream returned by {@link |
|
74 * Process#getErrorStream()} will get an immediate end of file. |
|
75 * |
|
76 * </ul> |
|
77 * |
|
78 * <p>Modifying a process builder's attributes will affect processes |
|
79 * subsequently started by that object's {@link #start()} method, but |
|
80 * will never affect previously started processes or the Java process |
|
81 * itself. |
|
82 * |
|
83 * <p>Most error checking is performed by the {@link #start()} method. |
|
84 * It is possible to modify the state of an object so that {@link |
|
85 * #start()} will fail. For example, setting the command attribute to |
|
86 * an empty list will not throw an exception unless {@link #start()} |
|
87 * is invoked. |
|
88 * |
|
89 * <p><strong>Note that this class is not synchronized.</strong> |
|
90 * If multiple threads access a <code>ProcessBuilder</code> instance |
|
91 * concurrently, and at least one of the threads modifies one of the |
|
92 * attributes structurally, it <i>must</i> be synchronized externally. |
|
93 * |
|
94 * <p>Starting a new process which uses the default working directory |
|
95 * and environment is easy: |
|
96 * |
|
97 * <blockquote><pre> |
|
98 * Process p = new ProcessBuilder("myCommand", "myArg").start(); |
|
99 * </pre></blockquote> |
|
100 * |
|
101 * <p>Here is an example that starts a process with a modified working |
|
102 * directory and environment: |
|
103 * |
|
104 * <blockquote><pre> |
|
105 * ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2"); |
|
106 * Map<String, String> env = pb.environment(); |
|
107 * env.put("VAR1", "myValue"); |
|
108 * env.remove("OTHERVAR"); |
|
109 * env.put("VAR2", env.get("VAR1") + "suffix"); |
|
110 * pb.directory(new File("myDir")); |
|
111 * Process p = pb.start(); |
|
112 * </pre></blockquote> |
|
113 * |
|
114 * <p>To start a process with an explicit set of environment |
|
115 * variables, first call {@link java.util.Map#clear() Map.clear()} |
|
116 * before adding environment variables. |
|
117 * |
|
118 * @since 1.5 |
|
119 */ |
|
120 |
|
121 public final class ProcessBuilder |
|
122 { |
|
123 private List<String> command; |
|
124 private File directory; |
|
125 private Map<String,String> environment; |
|
126 private boolean redirectErrorStream; |
|
127 |
|
128 /** |
|
129 * Constructs a process builder with the specified operating |
|
130 * system program and arguments. This constructor does <i>not</i> |
|
131 * make a copy of the <code>command</code> list. Subsequent |
|
132 * updates to the list will be reflected in the state of the |
|
133 * process builder. It is not checked whether |
|
134 * <code>command</code> corresponds to a valid operating system |
|
135 * command.</p> |
|
136 * |
|
137 * @param command The list containing the program and its arguments |
|
138 * |
|
139 * @throws NullPointerException |
|
140 * If the argument is <code>null</code> |
|
141 */ |
|
142 public ProcessBuilder(List<String> command) { |
|
143 if (command == null) |
|
144 throw new NullPointerException(); |
|
145 this.command = command; |
|
146 } |
|
147 |
|
148 /** |
|
149 * Constructs a process builder with the specified operating |
|
150 * system program and arguments. This is a convenience |
|
151 * constructor that sets the process builder's command to a string |
|
152 * list containing the same strings as the <code>command</code> |
|
153 * array, in the same order. It is not checked whether |
|
154 * <code>command</code> corresponds to a valid operating system |
|
155 * command.</p> |
|
156 * |
|
157 * @param command A string array containing the program and its arguments |
|
158 */ |
|
159 public ProcessBuilder(String... command) { |
|
160 this.command = new ArrayList<String>(command.length); |
|
161 for (String arg : command) |
|
162 this.command.add(arg); |
|
163 } |
|
164 |
|
165 /** |
|
166 * Sets this process builder's operating system program and |
|
167 * arguments. This method does <i>not</i> make a copy of the |
|
168 * <code>command</code> list. Subsequent updates to the list will |
|
169 * be reflected in the state of the process builder. It is not |
|
170 * checked whether <code>command</code> corresponds to a valid |
|
171 * operating system command.</p> |
|
172 * |
|
173 * @param command The list containing the program and its arguments |
|
174 * @return This process builder |
|
175 * |
|
176 * @throws NullPointerException |
|
177 * If the argument is <code>null</code> |
|
178 */ |
|
179 public ProcessBuilder command(List<String> command) { |
|
180 if (command == null) |
|
181 throw new NullPointerException(); |
|
182 this.command = command; |
|
183 return this; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Sets this process builder's operating system program and |
|
188 * arguments. This is a convenience method that sets the command |
|
189 * to a string list containing the same strings as the |
|
190 * <code>command</code> array, in the same order. It is not |
|
191 * checked whether <code>command</code> corresponds to a valid |
|
192 * operating system command.</p> |
|
193 * |
|
194 * @param command A string array containing the program and its arguments |
|
195 * @return This process builder |
|
196 */ |
|
197 public ProcessBuilder command(String... command) { |
|
198 this.command = new ArrayList<String>(command.length); |
|
199 for (String arg : command) |
|
200 this.command.add(arg); |
|
201 return this; |
|
202 } |
|
203 |
|
204 /** |
|
205 * Returns this process builder's operating system program and |
|
206 * arguments. The returned list is <i>not</i> a copy. Subsequent |
|
207 * updates to the list will be reflected in the state of this |
|
208 * process builder.</p> |
|
209 * |
|
210 * @return This process builder's program and its arguments |
|
211 */ |
|
212 public List<String> command() { |
|
213 return command; |
|
214 } |
|
215 |
|
216 /** |
|
217 * Returns a string map view of this process builder's environment. |
|
218 * |
|
219 * Whenever a process builder is created, the environment is |
|
220 * initialized to a copy of the current process environment (see |
|
221 * {@link System#getenv()}). Subprocesses subsequently started by |
|
222 * this object's {@link #start()} method will use this map as |
|
223 * their environment. |
|
224 * |
|
225 * <p>The returned object may be modified using ordinary {@link |
|
226 * java.util.Map Map} operations. These modifications will be |
|
227 * visible to subprocesses started via the {@link #start()} |
|
228 * method. Two <code>ProcessBuilder</code> instances always |
|
229 * contain independent process environments, so changes to the |
|
230 * returned map will never be reflected in any other |
|
231 * <code>ProcessBuilder</code> instance or the values returned by |
|
232 * {@link System#getenv System.getenv}. |
|
233 * |
|
234 * <p>If the system does not support environment variables, an |
|
235 * empty map is returned. |
|
236 * |
|
237 * <p>The returned map does not permit null keys or values. |
|
238 * Attempting to insert or query the presence of a null key or |
|
239 * value will throw a {@link NullPointerException}. |
|
240 * Attempting to query the presence of a key or value which is not |
|
241 * of type {@link String} will throw a {@link ClassCastException}. |
|
242 * |
|
243 * <p>The behavior of the returned map is system-dependent. A |
|
244 * system may not allow modifications to environment variables or |
|
245 * may forbid certain variable names or values. For this reason, |
|
246 * attempts to modify the map may fail with |
|
247 * {@link UnsupportedOperationException} or |
|
248 * {@link IllegalArgumentException} |
|
249 * if the modification is not permitted by the operating system. |
|
250 * |
|
251 * <p>Since the external format of environment variable names and |
|
252 * values is system-dependent, there may not be a one-to-one |
|
253 * mapping between them and Java's Unicode strings. Nevertheless, |
|
254 * the map is implemented in such a way that environment variables |
|
255 * which are not modified by Java code will have an unmodified |
|
256 * native representation in the subprocess. |
|
257 * |
|
258 * <p>The returned map and its collection views may not obey the |
|
259 * general contract of the {@link Object#equals} and |
|
260 * {@link Object#hashCode} methods. |
|
261 * |
|
262 * <p>The returned map is typically case-sensitive on all platforms. |
|
263 * |
|
264 * <p>If a security manager exists, its |
|
265 * {@link SecurityManager#checkPermission checkPermission} |
|
266 * method is called with a |
|
267 * <code>{@link RuntimePermission}("getenv.*")</code> |
|
268 * permission. This may result in a {@link SecurityException} being |
|
269 * thrown. |
|
270 * |
|
271 * <p>When passing information to a Java subprocess, |
|
272 * <a href=System.html#EnvironmentVSSystemProperties>system properties</a> |
|
273 * are generally preferred over environment variables.</p> |
|
274 * |
|
275 * @return This process builder's environment |
|
276 * |
|
277 * @throws SecurityException |
|
278 * If a security manager exists and its |
|
279 * {@link SecurityManager#checkPermission checkPermission} |
|
280 * method doesn't allow access to the process environment |
|
281 * |
|
282 * @see Runtime#exec(String[],String[],java.io.File) |
|
283 * @see System#getenv() |
|
284 */ |
|
285 public Map<String,String> environment() { |
|
286 SecurityManager security = System.getSecurityManager(); |
|
287 if (security != null) |
|
288 security.checkPermission(new RuntimePermission("getenv.*")); |
|
289 |
|
290 if (environment == null) |
|
291 environment = ProcessEnvironment.environment(); |
|
292 |
|
293 assert environment != null; |
|
294 |
|
295 return environment; |
|
296 } |
|
297 |
|
298 // Only for use by Runtime.exec(...envp...) |
|
299 ProcessBuilder environment(String[] envp) { |
|
300 assert environment == null; |
|
301 if (envp != null) { |
|
302 environment = ProcessEnvironment.emptyEnvironment(envp.length); |
|
303 assert environment != null; |
|
304 |
|
305 for (String envstring : envp) { |
|
306 // Before 1.5, we blindly passed invalid envstrings |
|
307 // to the child process. |
|
308 // We would like to throw an exception, but do not, |
|
309 // for compatibility with old broken code. |
|
310 |
|
311 // Silently discard any trailing junk. |
|
312 if (envstring.indexOf((int) '\u0000') != -1) |
|
313 envstring = envstring.replaceFirst("\u0000.*", ""); |
|
314 |
|
315 int eqlsign = |
|
316 envstring.indexOf('=', ProcessEnvironment.MIN_NAME_LENGTH); |
|
317 // Silently ignore envstrings lacking the required `='. |
|
318 if (eqlsign != -1) |
|
319 environment.put(envstring.substring(0,eqlsign), |
|
320 envstring.substring(eqlsign+1)); |
|
321 } |
|
322 } |
|
323 return this; |
|
324 } |
|
325 |
|
326 /** |
|
327 * Returns this process builder's working directory. |
|
328 * |
|
329 * Subprocesses subsequently started by this object's {@link |
|
330 * #start()} method will use this as their working directory. |
|
331 * The returned value may be <code>null</code> -- this means to use |
|
332 * the working directory of the current Java process, usually the |
|
333 * directory named by the system property <code>user.dir</code>, |
|
334 * as the working directory of the child process.</p> |
|
335 * |
|
336 * @return This process builder's working directory |
|
337 */ |
|
338 public File directory() { |
|
339 return directory; |
|
340 } |
|
341 |
|
342 /** |
|
343 * Sets this process builder's working directory. |
|
344 * |
|
345 * Subprocesses subsequently started by this object's {@link |
|
346 * #start()} method will use this as their working directory. |
|
347 * The argument may be <code>null</code> -- this means to use the |
|
348 * working directory of the current Java process, usually the |
|
349 * directory named by the system property <code>user.dir</code>, |
|
350 * as the working directory of the child process.</p> |
|
351 * |
|
352 * @param directory The new working directory |
|
353 * @return This process builder |
|
354 */ |
|
355 public ProcessBuilder directory(File directory) { |
|
356 this.directory = directory; |
|
357 return this; |
|
358 } |
|
359 |
|
360 /** |
|
361 * Tells whether this process builder merges standard error and |
|
362 * standard output. |
|
363 * |
|
364 * <p>If this property is <code>true</code>, then any error output |
|
365 * generated by subprocesses subsequently started by this object's |
|
366 * {@link #start()} method will be merged with the standard |
|
367 * output, so that both can be read using the |
|
368 * {@link Process#getInputStream()} method. This makes it easier |
|
369 * to correlate error messages with the corresponding output. |
|
370 * The initial value is <code>false</code>.</p> |
|
371 * |
|
372 * @return This process builder's <code>redirectErrorStream</code> property |
|
373 */ |
|
374 public boolean redirectErrorStream() { |
|
375 return redirectErrorStream; |
|
376 } |
|
377 |
|
378 /** |
|
379 * Sets this process builder's <code>redirectErrorStream</code> property. |
|
380 * |
|
381 * <p>If this property is <code>true</code>, then any error output |
|
382 * generated by subprocesses subsequently started by this object's |
|
383 * {@link #start()} method will be merged with the standard |
|
384 * output, so that both can be read using the |
|
385 * {@link Process#getInputStream()} method. This makes it easier |
|
386 * to correlate error messages with the corresponding output. |
|
387 * The initial value is <code>false</code>.</p> |
|
388 * |
|
389 * @param redirectErrorStream The new property value |
|
390 * @return This process builder |
|
391 */ |
|
392 public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) { |
|
393 this.redirectErrorStream = redirectErrorStream; |
|
394 return this; |
|
395 } |
|
396 |
|
397 /** |
|
398 * Starts a new process using the attributes of this process builder. |
|
399 * |
|
400 * <p>The new process will |
|
401 * invoke the command and arguments given by {@link #command()}, |
|
402 * in a working directory as given by {@link #directory()}, |
|
403 * with a process environment as given by {@link #environment()}. |
|
404 * |
|
405 * <p>This method checks that the command is a valid operating |
|
406 * system command. Which commands are valid is system-dependent, |
|
407 * but at the very least the command must be a non-empty list of |
|
408 * non-null strings. |
|
409 * |
|
410 * <p>If there is a security manager, its |
|
411 * {@link SecurityManager#checkExec checkExec} |
|
412 * method is called with the first component of this object's |
|
413 * <code>command</code> array as its argument. This may result in |
|
414 * a {@link SecurityException} being thrown. |
|
415 * |
|
416 * <p>Starting an operating system process is highly system-dependent. |
|
417 * Among the many things that can go wrong are: |
|
418 * <ul> |
|
419 * <li>The operating system program file was not found. |
|
420 * <li>Access to the program file was denied. |
|
421 * <li>The working directory does not exist. |
|
422 * </ul> |
|
423 * |
|
424 * <p>In such cases an exception will be thrown. The exact nature |
|
425 * of the exception is system-dependent, but it will always be a |
|
426 * subclass of {@link IOException}. |
|
427 * |
|
428 * <p>Subsequent modifications to this process builder will not |
|
429 * affect the returned {@link Process}.</p> |
|
430 * |
|
431 * @return A new {@link Process} object for managing the subprocess |
|
432 * |
|
433 * @throws NullPointerException |
|
434 * If an element of the command list is null |
|
435 * |
|
436 * @throws IndexOutOfBoundsException |
|
437 * If the command is an empty list (has size <code>0</code>) |
|
438 * |
|
439 * @throws SecurityException |
|
440 * If a security manager exists and its |
|
441 * {@link SecurityManager#checkExec checkExec} |
|
442 * method doesn't allow creation of the subprocess |
|
443 * |
|
444 * @throws IOException |
|
445 * If an I/O error occurs |
|
446 * |
|
447 * @see Runtime#exec(String[], String[], java.io.File) |
|
448 * @see SecurityManager#checkExec(String) |
|
449 */ |
|
450 public Process start() throws IOException { |
|
451 // Must convert to array first -- a malicious user-supplied |
|
452 // list might try to circumvent the security check. |
|
453 String[] cmdarray = command.toArray(new String[command.size()]); |
|
454 for (String arg : cmdarray) |
|
455 if (arg == null) |
|
456 throw new NullPointerException(); |
|
457 // Throws IndexOutOfBoundsException if command is empty |
|
458 String prog = cmdarray[0]; |
|
459 |
|
460 SecurityManager security = System.getSecurityManager(); |
|
461 if (security != null) |
|
462 security.checkExec(prog); |
|
463 |
|
464 String dir = directory == null ? null : directory.toString(); |
|
465 |
|
466 try { |
|
467 return ProcessImpl.start(cmdarray, |
|
468 environment, |
|
469 dir, |
|
470 redirectErrorStream); |
|
471 } catch (IOException e) { |
|
472 // It's much easier for us to create a high-quality error |
|
473 // message than the low-level C code which found the problem. |
|
474 throw new IOException( |
|
475 "Cannot run program \"" + prog + "\"" |
|
476 + (dir == null ? "" : " (in directory \"" + dir + "\")") |
|
477 + ": " + e.getMessage(), |
|
478 e); |
|
479 } |
|
480 } |
|
481 } |