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