001 /*
002 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.lang;
027
028 import java.io.File;
029 import java.io.IOException;
030 import java.util.ArrayList;
031 import java.util.List;
032 import java.util.Map;
033
034 /**
035 * This class is used to create operating system processes.
036 *
037 * <p>Each <code>ProcessBuilder</code> instance manages a collection
038 * of process attributes. The {@link #start()} method creates a new
039 * {@link Process} instance with those attributes. The {@link
040 * #start()} method can be invoked repeatedly from the same instance
041 * to create new subprocesses with identical or related attributes.
042 *
043 * <p>Each process builder manages these process attributes:
044 *
045 * <ul>
046 *
047 * <li>a <i>command</i>, a list of strings which signifies the
048 * external program file to be invoked and its arguments, if any.
049 * Which string lists represent a valid operating system command is
050 * system-dependent. For example, it is common for each conceptual
051 * argument to be an element in this list, but there are operating
052 * systems where programs are expected to tokenize command line
053 * strings themselves - on such a system a Java implementation might
054 * require commands to contain exactly two elements.
055 *
056 * <li>an <i>environment</i>, which is a system-dependent mapping from
057 * <i>variables</i> to <i>values</i>. The initial value is a copy of
058 * the environment of the current process (see {@link System#getenv()}).
059 *
060 * <li>a <i>working directory</i>. The default value is the current
061 * working directory of the current process, usually the directory
062 * named by the system property <code>user.dir</code>.
063 *
064 * <li>a <i>redirectErrorStream</i> property. Initially, this property
065 * is <code>false</code>, meaning that the standard output and error
066 * output of a subprocess are sent to two separate streams, which can
067 * be accessed using the {@link Process#getInputStream()} and {@link
068 * Process#getErrorStream()} methods. If the value is set to
069 * <code>true</code>, the standard error is merged with the standard
070 * output. This makes it easier to correlate error messages with the
071 * corresponding output. In this case, the merged data can be read
072 * from the stream returned by {@link Process#getInputStream()}, while
073 * reading from the stream returned by {@link
074 * Process#getErrorStream()} will get an immediate end of file.
075 *
076 * </ul>
077 *
078 * <p>Modifying a process builder's attributes will affect processes
079 * subsequently started by that object's {@link #start()} method, but
080 * will never affect previously started processes or the Java process
081 * itself.
082 *
083 * <p>Most error checking is performed by the {@link #start()} method.
084 * It is possible to modify the state of an object so that {@link
085 * #start()} will fail. For example, setting the command attribute to
086 * an empty list will not throw an exception unless {@link #start()}
087 * is invoked.
088 *
089 * <p><strong>Note that this class is not synchronized.</strong>
090 * If multiple threads access a <code>ProcessBuilder</code> instance
091 * concurrently, and at least one of the threads modifies one of the
092 * attributes structurally, it <i>must</i> be synchronized externally.
093 *
094 * <p>Starting a new process which uses the default working directory
095 * and environment is easy:
096 *
097 * <blockquote><pre>
098 * Process p = new ProcessBuilder("myCommand", "myArg").start();
099 * </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 private List<String> command;
123 private File directory;
124 private Map<String, String> environment;
125 private boolean redirectErrorStream;
126
127 /**
128 * Constructs a process builder with the specified operating
129 * system program and arguments. This constructor does <i>not</i>
130 * make a copy of the <code>command</code> list. Subsequent
131 * updates to the list will be reflected in the state of the
132 * process builder. It is not checked whether
133 * <code>command</code> corresponds to a valid operating system
134 * command.</p>
135 *
136 * @param command The list containing the program and its arguments
137 *
138 * @throws NullPointerException
139 * If the argument is <code>null</code>
140 */
141 public ProcessBuilder(List<String> command) {
142 if (command == null)
143 throw new NullPointerException();
144 this .command = command;
145 }
146
147 /**
148 * Constructs a process builder with the specified operating
149 * system program and arguments. This is a convenience
150 * constructor that sets the process builder's command to a string
151 * list containing the same strings as the <code>command</code>
152 * array, in the same order. It is not checked whether
153 * <code>command</code> corresponds to a valid operating system
154 * command.</p>
155 *
156 * @param command A string array containing the program and its arguments
157 */
158 public ProcessBuilder(String... command) {
159 this .command = new ArrayList<String>(command.length);
160 for (String arg : command)
161 this .command.add(arg);
162 }
163
164 /**
165 * Sets this process builder's operating system program and
166 * arguments. This method does <i>not</i> make a copy of the
167 * <code>command</code> list. Subsequent updates to the list will
168 * be reflected in the state of the process builder. It is not
169 * checked whether <code>command</code> corresponds to a valid
170 * operating system command.</p>
171 *
172 * @param command The list containing the program and its arguments
173 * @return This process builder
174 *
175 * @throws NullPointerException
176 * If the argument is <code>null</code>
177 */
178 public ProcessBuilder command(List<String> command) {
179 if (command == null)
180 throw new NullPointerException();
181 this .command = command;
182 return this ;
183 }
184
185 /**
186 * Sets this process builder's operating system program and
187 * arguments. This is a convenience method that sets the command
188 * to a string list containing the same strings as the
189 * <code>command</code> array, in the same order. It is not
190 * checked whether <code>command</code> corresponds to a valid
191 * operating system command.</p>
192 *
193 * @param command A string array containing the program and its arguments
194 * @return This process builder
195 */
196 public ProcessBuilder command(String... command) {
197 this .command = new ArrayList<String>(command.length);
198 for (String arg : command)
199 this .command.add(arg);
200 return this ;
201 }
202
203 /**
204 * Returns this process builder's operating system program and
205 * arguments. The returned list is <i>not</i> a copy. Subsequent
206 * updates to the list will be reflected in the state of this
207 * process builder.</p>
208 *
209 * @return This process builder's program and its arguments
210 */
211 public List<String> command() {
212 return command;
213 }
214
215 /**
216 * Returns a string map view of this process builder's environment.
217 *
218 * Whenever a process builder is created, the environment is
219 * initialized to a copy of the current process environment (see
220 * {@link System#getenv()}). Subprocesses subsequently started by
221 * this object's {@link #start()} method will use this map as
222 * their environment.
223 *
224 * <p>The returned object may be modified using ordinary {@link
225 * java.util.Map Map} operations. These modifications will be
226 * visible to subprocesses started via the {@link #start()}
227 * method. Two <code>ProcessBuilder</code> instances always
228 * contain independent process environments, so changes to the
229 * returned map will never be reflected in any other
230 * <code>ProcessBuilder</code> instance or the values returned by
231 * {@link System#getenv System.getenv}.
232 *
233 * <p>If the system does not support environment variables, an
234 * empty map is returned.
235 *
236 * <p>The returned map does not permit null keys or values.
237 * Attempting to insert or query the presence of a null key or
238 * value will throw a {@link NullPointerException}.
239 * Attempting to query the presence of a key or value which is not
240 * of type {@link String} will throw a {@link ClassCastException}.
241 *
242 * <p>The behavior of the returned map is system-dependent. A
243 * system may not allow modifications to environment variables or
244 * may forbid certain variable names or values. For this reason,
245 * attempts to modify the map may fail with
246 * {@link UnsupportedOperationException} or
247 * {@link IllegalArgumentException}
248 * if the modification is not permitted by the operating system.
249 *
250 * <p>Since the external format of environment variable names and
251 * values is system-dependent, there may not be a one-to-one
252 * mapping between them and Java's Unicode strings. Nevertheless,
253 * the map is implemented in such a way that environment variables
254 * which are not modified by Java code will have an unmodified
255 * native representation in the subprocess.
256 *
257 * <p>The returned map and its collection views may not obey the
258 * general contract of the {@link Object#equals} and
259 * {@link Object#hashCode} methods.
260 *
261 * <p>The returned map is typically case-sensitive on all platforms.
262 *
263 * <p>If a security manager exists, its
264 * {@link SecurityManager#checkPermission checkPermission}
265 * method is called with a
266 * <code>{@link RuntimePermission}("getenv.*")</code>
267 * permission. This may result in a {@link SecurityException} being
268 * thrown.
269 *
270 * <p>When passing information to a Java subprocess,
271 * <a href=System.html#EnvironmentVSSystemProperties>system properties</a>
272 * are generally preferred over environment variables.</p>
273 *
274 * @return This process builder's environment
275 *
276 * @throws SecurityException
277 * If a security manager exists and its
278 * {@link SecurityManager#checkPermission checkPermission}
279 * method doesn't allow access to the process environment
280 *
281 * @see Runtime#exec(String[],String[],java.io.File)
282 * @see System#getenv()
283 */
284 public Map<String, String> environment() {
285 SecurityManager security = System.getSecurityManager();
286 if (security != null)
287 security.checkPermission(new RuntimePermission("getenv.*"));
288
289 if (environment == null)
290 environment = ProcessEnvironment.environment();
291
292 assert environment != null;
293
294 return environment;
295 }
296
297 // Only for use by Runtime.exec(...envp...)
298 ProcessBuilder environment(String[] envp) {
299 assert environment == null;
300 if (envp != null) {
301 environment = ProcessEnvironment
302 .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 = envstring.indexOf('=',
316 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(
393 boolean redirectErrorStream) {
394 this .redirectErrorStream = redirectErrorStream;
395 return this ;
396 }
397
398 /**
399 * Starts a new process using the attributes of this process builder.
400 *
401 * <p>The new process will
402 * invoke the command and arguments given by {@link #command()},
403 * in a working directory as given by {@link #directory()},
404 * with a process environment as given by {@link #environment()}.
405 *
406 * <p>This method checks that the command is a valid operating
407 * system command. Which commands are valid is system-dependent,
408 * but at the very least the command must be a non-empty list of
409 * non-null strings.
410 *
411 * <p>If there is a security manager, its
412 * {@link SecurityManager#checkExec checkExec}
413 * method is called with the first component of this object's
414 * <code>command</code> array as its argument. This may result in
415 * a {@link SecurityException} being thrown.
416 *
417 * <p>Starting an operating system process is highly system-dependent.
418 * Among the many things that can go wrong are:
419 * <ul>
420 * <li>The operating system program file was not found.
421 * <li>Access to the program file was denied.
422 * <li>The working directory does not exist.
423 * </ul>
424 *
425 * <p>In such cases an exception will be thrown. The exact nature
426 * of the exception is system-dependent, but it will always be a
427 * subclass of {@link IOException}.
428 *
429 * <p>Subsequent modifications to this process builder will not
430 * affect the returned {@link Process}.</p>
431 *
432 * @return A new {@link Process} object for managing the subprocess
433 *
434 * @throws NullPointerException
435 * If an element of the command list is null
436 *
437 * @throws IndexOutOfBoundsException
438 * If the command is an empty list (has size <code>0</code>)
439 *
440 * @throws SecurityException
441 * If a security manager exists and its
442 * {@link SecurityManager#checkExec checkExec}
443 * method doesn't allow creation of the subprocess
444 *
445 * @throws IOException
446 * If an I/O error occurs
447 *
448 * @see Runtime#exec(String[], String[], java.io.File)
449 * @see SecurityManager#checkExec(String)
450 */
451 public Process start() throws IOException {
452 // Must convert to array first -- a malicious user-supplied
453 // list might try to circumvent the security check.
454 String[] cmdarray = command.toArray(new String[command.size()]);
455 for (String arg : cmdarray)
456 if (arg == null)
457 throw new NullPointerException();
458 // Throws IndexOutOfBoundsException if command is empty
459 String prog = cmdarray[0];
460
461 SecurityManager security = System.getSecurityManager();
462 if (security != null)
463 security.checkExec(prog);
464
465 String dir = directory == null ? null : directory.toString();
466
467 try {
468 return ProcessImpl.start(cmdarray, environment, dir,
469 redirectErrorStream);
470 } catch (IOException e) {
471 // It's much easier for us to create a high-quality error
472 // message than the low-level C code which found the problem.
473 throw new IOException("Cannot run program \""
474 + prog
475 + "\""
476 + (dir == null ? "" : " (in directory \"" + dir
477 + "\")") + ": " + e.getMessage(), e);
478 }
479 }
480 }
|