0N/A/*
3261N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.lang;
0N/A
0N/Aimport java.io.File;
0N/Aimport java.io.IOException;
25N/Aimport java.io.InputStream;
25N/Aimport java.io.OutputStream;
5975N/Aimport java.security.AccessControlException;
25N/Aimport java.util.Arrays;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.List;
0N/Aimport java.util.Map;
0N/A
0N/A/**
0N/A * This class is used to create operating system processes.
0N/A *
25N/A * <p>Each {@code ProcessBuilder} instance manages a collection
0N/A * of process attributes. The {@link #start()} method creates a new
0N/A * {@link Process} instance with those attributes. The {@link
0N/A * #start()} method can be invoked repeatedly from the same instance
0N/A * to create new subprocesses with identical or related attributes.
0N/A *
0N/A * <p>Each process builder manages these process attributes:
0N/A *
0N/A * <ul>
0N/A *
0N/A * <li>a <i>command</i>, a list of strings which signifies the
0N/A * external program file to be invoked and its arguments, if any.
0N/A * Which string lists represent a valid operating system command is
0N/A * system-dependent. For example, it is common for each conceptual
0N/A * argument to be an element in this list, but there are operating
0N/A * systems where programs are expected to tokenize command line
0N/A * strings themselves - on such a system a Java implementation might
0N/A * require commands to contain exactly two elements.
0N/A *
0N/A * <li>an <i>environment</i>, which is a system-dependent mapping from
0N/A * <i>variables</i> to <i>values</i>. The initial value is a copy of
0N/A * the environment of the current process (see {@link System#getenv()}).
0N/A *
0N/A * <li>a <i>working directory</i>. The default value is the current
0N/A * working directory of the current process, usually the directory
25N/A * named by the system property {@code user.dir}.
25N/A *
25N/A * <li><a name="redirect-input">a source of <i>standard input</i>.
25N/A * By default, the subprocess reads input from a pipe. Java code
25N/A * can access this pipe via the output stream returned by
25N/A * {@link Process#getOutputStream()}. However, standard input may
25N/A * be redirected to another source using
25N/A * {@link #redirectInput(Redirect) redirectInput}.
25N/A * In this case, {@link Process#getOutputStream()} will return a
25N/A * <i>null output stream</i>, for which:
25N/A *
25N/A * <ul>
25N/A * <li>the {@link OutputStream#write(int) write} methods always
25N/A * throw {@code IOException}
25N/A * <li>the {@link OutputStream#close() close} method does nothing
25N/A * </ul>
25N/A *
25N/A * <li><a name="redirect-output">a destination for <i>standard output</i>
25N/A * and <i>standard error</i>. By default, the subprocess writes standard
25N/A * output and standard error to pipes. Java code can access these pipes
25N/A * via the input streams returned by {@link Process#getInputStream()} and
25N/A * {@link Process#getErrorStream()}. However, standard output and
25N/A * standard error may be redirected to other destinations using
25N/A * {@link #redirectOutput(Redirect) redirectOutput} and
25N/A * {@link #redirectError(Redirect) redirectError}.
25N/A * In this case, {@link Process#getInputStream()} and/or
25N/A * {@link Process#getErrorStream()} will return a <i>null input
25N/A * stream</i>, for which:
25N/A *
25N/A * <ul>
25N/A * <li>the {@link InputStream#read() read} methods always return
25N/A * {@code -1}
25N/A * <li>the {@link InputStream#available() available} method always returns
25N/A * {@code 0}
25N/A * <li>the {@link InputStream#close() close} method does nothing
25N/A * </ul>
0N/A *
0N/A * <li>a <i>redirectErrorStream</i> property. Initially, this property
25N/A * is {@code false}, meaning that the standard output and error
0N/A * output of a subprocess are sent to two separate streams, which can
0N/A * be accessed using the {@link Process#getInputStream()} and {@link
25N/A * Process#getErrorStream()} methods.
25N/A *
25N/A * <p>If the value is set to {@code true}, then:
25N/A *
25N/A * <ul>
25N/A * <li>standard error is merged with the standard output and always sent
25N/A * to the same destination (this makes it easier to correlate error
25N/A * messages with the corresponding output)
25N/A * <li>the common destination of standard error and standard output can be
25N/A * redirected using
25N/A * {@link #redirectOutput(Redirect) redirectOutput}
25N/A * <li>any redirection set by the
25N/A * {@link #redirectError(Redirect) redirectError}
25N/A * method is ignored when creating a subprocess
25N/A * <li>the stream returned from {@link Process#getErrorStream()} will
25N/A * always be a <a href="#redirect-output">null input stream</a>
25N/A * </ul>
0N/A *
0N/A * </ul>
0N/A *
0N/A * <p>Modifying a process builder's attributes will affect processes
0N/A * subsequently started by that object's {@link #start()} method, but
0N/A * will never affect previously started processes or the Java process
0N/A * itself.
0N/A *
0N/A * <p>Most error checking is performed by the {@link #start()} method.
0N/A * It is possible to modify the state of an object so that {@link
0N/A * #start()} will fail. For example, setting the command attribute to
0N/A * an empty list will not throw an exception unless {@link #start()}
0N/A * is invoked.
0N/A *
0N/A * <p><strong>Note that this class is not synchronized.</strong>
25N/A * If multiple threads access a {@code ProcessBuilder} instance
0N/A * concurrently, and at least one of the threads modifies one of the
0N/A * attributes structurally, it <i>must</i> be synchronized externally.
0N/A *
0N/A * <p>Starting a new process which uses the default working directory
0N/A * and environment is easy:
0N/A *
25N/A * <pre> {@code
0N/A * Process p = new ProcessBuilder("myCommand", "myArg").start();
25N/A * }</pre>
0N/A *
0N/A * <p>Here is an example that starts a process with a modified working
25N/A * directory and environment, and redirects standard output and error
25N/A * to be appended to a log file:
0N/A *
25N/A * <pre> {@code
25N/A * ProcessBuilder pb =
25N/A * new ProcessBuilder("myCommand", "myArg1", "myArg2");
25N/A * Map<String, String> env = pb.environment();
0N/A * env.put("VAR1", "myValue");
0N/A * env.remove("OTHERVAR");
0N/A * env.put("VAR2", env.get("VAR1") + "suffix");
0N/A * pb.directory(new File("myDir"));
25N/A * File log = new File("log");
25N/A * pb.redirectErrorStream(true);
25N/A * pb.redirectOutput(Redirect.appendTo(log));
0N/A * Process p = pb.start();
25N/A * assert pb.redirectInput() == Redirect.PIPE;
25N/A * assert pb.redirectOutput().file() == log;
25N/A * assert p.getInputStream().read() == -1;
25N/A * }</pre>
0N/A *
0N/A * <p>To start a process with an explicit set of environment
0N/A * variables, first call {@link java.util.Map#clear() Map.clear()}
0N/A * before adding environment variables.
0N/A *
25N/A * @author Martin Buchholz
0N/A * @since 1.5
0N/A */
0N/A
0N/Apublic final class ProcessBuilder
0N/A{
0N/A private List<String> command;
0N/A private File directory;
0N/A private Map<String,String> environment;
0N/A private boolean redirectErrorStream;
25N/A private Redirect[] redirects;
0N/A
0N/A /**
0N/A * Constructs a process builder with the specified operating
0N/A * system program and arguments. This constructor does <i>not</i>
25N/A * make a copy of the {@code command} list. Subsequent
0N/A * updates to the list will be reflected in the state of the
0N/A * process builder. It is not checked whether
25N/A * {@code command} corresponds to a valid operating system
25N/A * command.
0N/A *
25N/A * @param command the list containing the program and its arguments
25N/A * @throws NullPointerException if the argument is null
0N/A */
0N/A public ProcessBuilder(List<String> command) {
0N/A if (command == null)
0N/A throw new NullPointerException();
0N/A this.command = command;
0N/A }
0N/A
0N/A /**
0N/A * Constructs a process builder with the specified operating
0N/A * system program and arguments. This is a convenience
0N/A * constructor that sets the process builder's command to a string
25N/A * list containing the same strings as the {@code command}
0N/A * array, in the same order. It is not checked whether
25N/A * {@code command} corresponds to a valid operating system
25N/A * command.
0N/A *
25N/A * @param command a string array containing the program and its arguments
0N/A */
0N/A public ProcessBuilder(String... command) {
3323N/A this.command = new ArrayList<>(command.length);
0N/A for (String arg : command)
0N/A this.command.add(arg);
0N/A }
0N/A
0N/A /**
0N/A * Sets this process builder's operating system program and
0N/A * arguments. This method does <i>not</i> make a copy of the
25N/A * {@code command} list. Subsequent updates to the list will
0N/A * be reflected in the state of the process builder. It is not
25N/A * checked whether {@code command} corresponds to a valid
25N/A * operating system command.
0N/A *
25N/A * @param command the list containing the program and its arguments
25N/A * @return this process builder
0N/A *
25N/A * @throws NullPointerException if the argument is null
0N/A */
0N/A public ProcessBuilder command(List<String> command) {
0N/A if (command == null)
0N/A throw new NullPointerException();
0N/A this.command = command;
0N/A return this;
0N/A }
0N/A
0N/A /**
0N/A * Sets this process builder's operating system program and
0N/A * arguments. This is a convenience method that sets the command
0N/A * to a string list containing the same strings as the
25N/A * {@code command} array, in the same order. It is not
25N/A * checked whether {@code command} corresponds to a valid
25N/A * operating system command.
0N/A *
25N/A * @param command a string array containing the program and its arguments
25N/A * @return this process builder
0N/A */
0N/A public ProcessBuilder command(String... command) {
3323N/A this.command = new ArrayList<>(command.length);
0N/A for (String arg : command)
0N/A this.command.add(arg);
0N/A return this;
0N/A }
0N/A
0N/A /**
0N/A * Returns this process builder's operating system program and
0N/A * arguments. The returned list is <i>not</i> a copy. Subsequent
0N/A * updates to the list will be reflected in the state of this
25N/A * process builder.
0N/A *
25N/A * @return this process builder's program and its arguments
0N/A */
0N/A public List<String> command() {
0N/A return command;
0N/A }
0N/A
0N/A /**
0N/A * Returns a string map view of this process builder's environment.
0N/A *
0N/A * Whenever a process builder is created, the environment is
0N/A * initialized to a copy of the current process environment (see
0N/A * {@link System#getenv()}). Subprocesses subsequently started by
0N/A * this object's {@link #start()} method will use this map as
0N/A * their environment.
0N/A *
0N/A * <p>The returned object may be modified using ordinary {@link
0N/A * java.util.Map Map} operations. These modifications will be
0N/A * visible to subprocesses started via the {@link #start()}
25N/A * method. Two {@code ProcessBuilder} instances always
0N/A * contain independent process environments, so changes to the
0N/A * returned map will never be reflected in any other
25N/A * {@code ProcessBuilder} instance or the values returned by
0N/A * {@link System#getenv System.getenv}.
0N/A *
0N/A * <p>If the system does not support environment variables, an
0N/A * empty map is returned.
0N/A *
0N/A * <p>The returned map does not permit null keys or values.
0N/A * Attempting to insert or query the presence of a null key or
0N/A * value will throw a {@link NullPointerException}.
0N/A * Attempting to query the presence of a key or value which is not
0N/A * of type {@link String} will throw a {@link ClassCastException}.
0N/A *
0N/A * <p>The behavior of the returned map is system-dependent. A
0N/A * system may not allow modifications to environment variables or
0N/A * may forbid certain variable names or values. For this reason,
0N/A * attempts to modify the map may fail with
0N/A * {@link UnsupportedOperationException} or
0N/A * {@link IllegalArgumentException}
0N/A * if the modification is not permitted by the operating system.
0N/A *
0N/A * <p>Since the external format of environment variable names and
0N/A * values is system-dependent, there may not be a one-to-one
0N/A * mapping between them and Java's Unicode strings. Nevertheless,
0N/A * the map is implemented in such a way that environment variables
0N/A * which are not modified by Java code will have an unmodified
0N/A * native representation in the subprocess.
0N/A *
0N/A * <p>The returned map and its collection views may not obey the
0N/A * general contract of the {@link Object#equals} and
0N/A * {@link Object#hashCode} methods.
0N/A *
0N/A * <p>The returned map is typically case-sensitive on all platforms.
0N/A *
0N/A * <p>If a security manager exists, its
25N/A * {@link SecurityManager#checkPermission checkPermission} method
25N/A * is called with a
25N/A * {@link RuntimePermission}{@code ("getenv.*")} permission.
25N/A * This may result in a {@link SecurityException} being thrown.
0N/A *
0N/A * <p>When passing information to a Java subprocess,
0N/A * <a href=System.html#EnvironmentVSSystemProperties>system properties</a>
25N/A * are generally preferred over environment variables.
0N/A *
25N/A * @return this process builder's environment
0N/A *
25N/A * @throws SecurityException
25N/A * if a security manager exists and its
25N/A * {@link SecurityManager#checkPermission checkPermission}
25N/A * method doesn't allow access to the process environment
0N/A *
25N/A * @see Runtime#exec(String[],String[],java.io.File)
25N/A * @see System#getenv()
0N/A */
0N/A public Map<String,String> environment() {
0N/A SecurityManager security = System.getSecurityManager();
0N/A if (security != null)
0N/A security.checkPermission(new RuntimePermission("getenv.*"));
0N/A
0N/A if (environment == null)
0N/A environment = ProcessEnvironment.environment();
0N/A
0N/A assert environment != null;
0N/A
0N/A return environment;
0N/A }
0N/A
0N/A // Only for use by Runtime.exec(...envp...)
0N/A ProcessBuilder environment(String[] envp) {
0N/A assert environment == null;
0N/A if (envp != null) {
0N/A environment = ProcessEnvironment.emptyEnvironment(envp.length);
0N/A assert environment != null;
0N/A
0N/A for (String envstring : envp) {
0N/A // Before 1.5, we blindly passed invalid envstrings
0N/A // to the child process.
0N/A // We would like to throw an exception, but do not,
0N/A // for compatibility with old broken code.
0N/A
0N/A // Silently discard any trailing junk.
0N/A if (envstring.indexOf((int) '\u0000') != -1)
0N/A envstring = envstring.replaceFirst("\u0000.*", "");
0N/A
0N/A int eqlsign =
0N/A envstring.indexOf('=', ProcessEnvironment.MIN_NAME_LENGTH);
0N/A // Silently ignore envstrings lacking the required `='.
0N/A if (eqlsign != -1)
0N/A environment.put(envstring.substring(0,eqlsign),
0N/A envstring.substring(eqlsign+1));
0N/A }
0N/A }
0N/A return this;
0N/A }
0N/A
0N/A /**
0N/A * Returns this process builder's working directory.
0N/A *
0N/A * Subprocesses subsequently started by this object's {@link
0N/A * #start()} method will use this as their working directory.
25N/A * The returned value may be {@code null} -- this means to use
0N/A * the working directory of the current Java process, usually the
25N/A * directory named by the system property {@code user.dir},
25N/A * as the working directory of the child process.
0N/A *
25N/A * @return this process builder's working directory
0N/A */
0N/A public File directory() {
0N/A return directory;
0N/A }
0N/A
0N/A /**
0N/A * Sets this process builder's working directory.
0N/A *
0N/A * Subprocesses subsequently started by this object's {@link
0N/A * #start()} method will use this as their working directory.
25N/A * The argument may be {@code null} -- this means to use the
0N/A * working directory of the current Java process, usually the
25N/A * directory named by the system property {@code user.dir},
25N/A * as the working directory of the child process.
0N/A *
25N/A * @param directory the new working directory
25N/A * @return this process builder
0N/A */
0N/A public ProcessBuilder directory(File directory) {
0N/A this.directory = directory;
0N/A return this;
0N/A }
0N/A
25N/A // ---------------- I/O Redirection ----------------
25N/A
25N/A /**
25N/A * Implements a <a href="#redirect-output">null input stream</a>.
25N/A */
25N/A static class NullInputStream extends InputStream {
2473N/A static final NullInputStream INSTANCE = new NullInputStream();
2473N/A private NullInputStream() {}
25N/A public int read() { return -1; }
25N/A public int available() { return 0; }
25N/A }
25N/A
25N/A /**
25N/A * Implements a <a href="#redirect-input">null output stream</a>.
25N/A */
25N/A static class NullOutputStream extends OutputStream {
2473N/A static final NullOutputStream INSTANCE = new NullOutputStream();
2473N/A private NullOutputStream() {}
25N/A public void write(int b) throws IOException {
25N/A throw new IOException("Stream closed");
25N/A }
25N/A }
25N/A
25N/A /**
25N/A * Represents a source of subprocess input or a destination of
25N/A * subprocess output.
25N/A *
25N/A * Each {@code Redirect} instance is one of the following:
25N/A *
25N/A * <ul>
25N/A * <li>the special value {@link #PIPE Redirect.PIPE}
25N/A * <li>the special value {@link #INHERIT Redirect.INHERIT}
25N/A * <li>a redirection to read from a file, created by an invocation of
25N/A * {@link Redirect#from Redirect.from(File)}
25N/A * <li>a redirection to write to a file, created by an invocation of
25N/A * {@link Redirect#to Redirect.to(File)}
25N/A * <li>a redirection to append to a file, created by an invocation of
25N/A * {@link Redirect#appendTo Redirect.appendTo(File)}
25N/A * </ul>
25N/A *
25N/A * <p>Each of the above categories has an associated unique
25N/A * {@link Type Type}.
25N/A *
25N/A * @since 1.7
25N/A */
25N/A public static abstract class Redirect {
25N/A /**
25N/A * The type of a {@link Redirect}.
25N/A */
25N/A public enum Type {
25N/A /**
25N/A * The type of {@link Redirect#PIPE Redirect.PIPE}.
25N/A */
25N/A PIPE,
25N/A
25N/A /**
25N/A * The type of {@link Redirect#INHERIT Redirect.INHERIT}.
25N/A */
25N/A INHERIT,
25N/A
25N/A /**
25N/A * The type of redirects returned from
25N/A * {@link Redirect#from Redirect.from(File)}.
25N/A */
25N/A READ,
25N/A
25N/A /**
25N/A * The type of redirects returned from
25N/A * {@link Redirect#to Redirect.to(File)}.
25N/A */
25N/A WRITE,
25N/A
25N/A /**
25N/A * The type of redirects returned from
25N/A * {@link Redirect#appendTo Redirect.appendTo(File)}.
25N/A */
25N/A APPEND
25N/A };
25N/A
25N/A /**
25N/A * Returns the type of this {@code Redirect}.
25N/A * @return the type of this {@code Redirect}
25N/A */
25N/A public abstract Type type();
25N/A
25N/A /**
25N/A * Indicates that subprocess I/O will be connected to the
25N/A * current Java process over a pipe.
25N/A *
25N/A * This is the default handling of subprocess standard I/O.
25N/A *
25N/A * <p>It will always be true that
25N/A * <pre> {@code
25N/A * Redirect.PIPE.file() == null &&
25N/A * Redirect.PIPE.type() == Redirect.Type.PIPE
25N/A * }</pre>
25N/A */
25N/A public static final Redirect PIPE = new Redirect() {
25N/A public Type type() { return Type.PIPE; }
25N/A public String toString() { return type().toString(); }};
25N/A
25N/A /**
25N/A * Indicates that subprocess I/O source or destination will be the
25N/A * same as those of the current process. This is the normal
25N/A * behavior of most operating system command interpreters (shells).
25N/A *
25N/A * <p>It will always be true that
25N/A * <pre> {@code
25N/A * Redirect.INHERIT.file() == null &&
25N/A * Redirect.INHERIT.type() == Redirect.Type.INHERIT
25N/A * }</pre>
25N/A */
25N/A public static final Redirect INHERIT = new Redirect() {
25N/A public Type type() { return Type.INHERIT; }
25N/A public String toString() { return type().toString(); }};
25N/A
25N/A /**
25N/A * Returns the {@link File} source or destination associated
25N/A * with this redirect, or {@code null} if there is no such file.
25N/A *
25N/A * @return the file associated with this redirect,
25N/A * or {@code null} if there is no such file
25N/A */
25N/A public File file() { return null; }
25N/A
3200N/A /**
3200N/A * When redirected to a destination file, indicates if the output
3200N/A * is to be written to the end of the file.
3200N/A */
3200N/A boolean append() {
25N/A throw new UnsupportedOperationException();
25N/A }
25N/A
25N/A /**
25N/A * Returns a redirect to read from the specified file.
25N/A *
25N/A * <p>It will always be true that
25N/A * <pre> {@code
25N/A * Redirect.from(file).file() == file &&
25N/A * Redirect.from(file).type() == Redirect.Type.READ
25N/A * }</pre>
25N/A *
25N/A * @throws NullPointerException if the specified file is null
25N/A * @return a redirect to read from the specified file
25N/A */
25N/A public static Redirect from(final File file) {
25N/A if (file == null)
25N/A throw new NullPointerException();
25N/A return new Redirect() {
25N/A public Type type() { return Type.READ; }
25N/A public File file() { return file; }
25N/A public String toString() {
25N/A return "redirect to read from file \"" + file + "\"";
25N/A }
25N/A };
25N/A }
25N/A
25N/A /**
25N/A * Returns a redirect to write to the specified file.
25N/A * If the specified file exists when the subprocess is started,
25N/A * its previous contents will be discarded.
25N/A *
25N/A * <p>It will always be true that
25N/A * <pre> {@code
25N/A * Redirect.to(file).file() == file &&
25N/A * Redirect.to(file).type() == Redirect.Type.WRITE
25N/A * }</pre>
25N/A *
25N/A * @throws NullPointerException if the specified file is null
25N/A * @return a redirect to write to the specified file
25N/A */
25N/A public static Redirect to(final File file) {
25N/A if (file == null)
25N/A throw new NullPointerException();
25N/A return new Redirect() {
25N/A public Type type() { return Type.WRITE; }
25N/A public File file() { return file; }
25N/A public String toString() {
25N/A return "redirect to write to file \"" + file + "\"";
25N/A }
3200N/A boolean append() { return false; }
25N/A };
25N/A }
25N/A
25N/A /**
25N/A * Returns a redirect to append to the specified file.
25N/A * Each write operation first advances the position to the
25N/A * end of the file and then writes the requested data.
25N/A * Whether the advancement of the position and the writing
25N/A * of the data are done in a single atomic operation is
25N/A * system-dependent and therefore unspecified.
25N/A *
25N/A * <p>It will always be true that
25N/A * <pre> {@code
25N/A * Redirect.appendTo(file).file() == file &&
25N/A * Redirect.appendTo(file).type() == Redirect.Type.APPEND
25N/A * }</pre>
25N/A *
25N/A * @throws NullPointerException if the specified file is null
25N/A * @return a redirect to append to the specified file
25N/A */
25N/A public static Redirect appendTo(final File file) {
25N/A if (file == null)
25N/A throw new NullPointerException();
25N/A return new Redirect() {
25N/A public Type type() { return Type.APPEND; }
25N/A public File file() { return file; }
25N/A public String toString() {
25N/A return "redirect to append to file \"" + file + "\"";
25N/A }
3200N/A boolean append() { return true; }
25N/A };
25N/A }
25N/A
25N/A /**
25N/A * Compares the specified object with this {@code Redirect} for
25N/A * equality. Returns {@code true} if and only if the two
25N/A * objects are identical or both objects are {@code Redirect}
25N/A * instances of the same type associated with non-null equal
25N/A * {@code File} instances.
25N/A */
25N/A public boolean equals(Object obj) {
25N/A if (obj == this)
25N/A return true;
25N/A if (! (obj instanceof Redirect))
25N/A return false;
25N/A Redirect r = (Redirect) obj;
25N/A if (r.type() != this.type())
25N/A return false;
25N/A assert this.file() != null;
25N/A return this.file().equals(r.file());
25N/A }
25N/A
25N/A /**
25N/A * Returns a hash code value for this {@code Redirect}.
25N/A * @return a hash code value for this {@code Redirect}
25N/A */
25N/A public int hashCode() {
25N/A File file = file();
25N/A if (file == null)
25N/A return super.hashCode();
25N/A else
25N/A return file.hashCode();
25N/A }
25N/A
25N/A /**
25N/A * No public constructors. Clients must use predefined
25N/A * static {@code Redirect} instances or factory methods.
25N/A */
25N/A private Redirect() {}
25N/A }
25N/A
25N/A private Redirect[] redirects() {
25N/A if (redirects == null)
25N/A redirects = new Redirect[] {
25N/A Redirect.PIPE, Redirect.PIPE, Redirect.PIPE
25N/A };
25N/A return redirects;
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard input source.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method obtain their standard input from this source.
25N/A *
25N/A * <p>If the source is {@link Redirect#PIPE Redirect.PIPE}
25N/A * (the initial value), then the standard input of a
25N/A * subprocess can be written to using the output stream
25N/A * returned by {@link Process#getOutputStream()}.
25N/A * If the source is set to any other value, then
25N/A * {@link Process#getOutputStream()} will return a
25N/A * <a href="#redirect-input">null output stream</a>.
25N/A *
25N/A * @param source the new standard input source
25N/A * @return this process builder
25N/A * @throws IllegalArgumentException
25N/A * if the redirect does not correspond to a valid source
25N/A * of data, that is, has type
25N/A * {@link Redirect.Type#WRITE WRITE} or
25N/A * {@link Redirect.Type#APPEND APPEND}
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectInput(Redirect source) {
25N/A if (source.type() == Redirect.Type.WRITE ||
25N/A source.type() == Redirect.Type.APPEND)
25N/A throw new IllegalArgumentException(
25N/A "Redirect invalid for reading: " + source);
25N/A redirects()[0] = source;
25N/A return this;
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard output destination.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method send their standard output to this destination.
25N/A *
25N/A * <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
25N/A * (the initial value), then the standard output of a subprocess
25N/A * can be read using the input stream returned by {@link
25N/A * Process#getInputStream()}.
25N/A * If the destination is set to any other value, then
25N/A * {@link Process#getInputStream()} will return a
25N/A * <a href="#redirect-output">null input stream</a>.
25N/A *
25N/A * @param destination the new standard output destination
25N/A * @return this process builder
25N/A * @throws IllegalArgumentException
25N/A * if the redirect does not correspond to a valid
25N/A * destination of data, that is, has type
25N/A * {@link Redirect.Type#READ READ}
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectOutput(Redirect destination) {
25N/A if (destination.type() == Redirect.Type.READ)
25N/A throw new IllegalArgumentException(
25N/A "Redirect invalid for writing: " + destination);
25N/A redirects()[1] = destination;
25N/A return this;
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard error destination.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method send their standard error to this destination.
25N/A *
25N/A * <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
25N/A * (the initial value), then the error output of a subprocess
25N/A * can be read using the input stream returned by {@link
25N/A * Process#getErrorStream()}.
25N/A * If the destination is set to any other value, then
25N/A * {@link Process#getErrorStream()} will return a
25N/A * <a href="#redirect-output">null input stream</a>.
25N/A *
25N/A * <p>If the {@link #redirectErrorStream redirectErrorStream}
25N/A * attribute has been set {@code true}, then the redirection set
25N/A * by this method has no effect.
25N/A *
25N/A * @param destination the new standard error destination
25N/A * @return this process builder
25N/A * @throws IllegalArgumentException
25N/A * if the redirect does not correspond to a valid
25N/A * destination of data, that is, has type
25N/A * {@link Redirect.Type#READ READ}
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectError(Redirect destination) {
25N/A if (destination.type() == Redirect.Type.READ)
25N/A throw new IllegalArgumentException(
25N/A "Redirect invalid for writing: " + destination);
25N/A redirects()[2] = destination;
25N/A return this;
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard input source to a file.
25N/A *
25N/A * <p>This is a convenience method. An invocation of the form
25N/A * {@code redirectInput(file)}
25N/A * behaves in exactly the same way as the invocation
25N/A * {@link #redirectInput(Redirect) redirectInput}
25N/A * {@code (Redirect.from(file))}.
25N/A *
25N/A * @param file the new standard input source
25N/A * @return this process builder
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectInput(File file) {
25N/A return redirectInput(Redirect.from(file));
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard output destination to a file.
25N/A *
25N/A * <p>This is a convenience method. An invocation of the form
25N/A * {@code redirectOutput(file)}
25N/A * behaves in exactly the same way as the invocation
25N/A * {@link #redirectOutput(Redirect) redirectOutput}
25N/A * {@code (Redirect.to(file))}.
25N/A *
25N/A * @param file the new standard output destination
25N/A * @return this process builder
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectOutput(File file) {
25N/A return redirectOutput(Redirect.to(file));
25N/A }
25N/A
25N/A /**
25N/A * Sets this process builder's standard error destination to a file.
25N/A *
25N/A * <p>This is a convenience method. An invocation of the form
25N/A * {@code redirectError(file)}
25N/A * behaves in exactly the same way as the invocation
25N/A * {@link #redirectError(Redirect) redirectError}
25N/A * {@code (Redirect.to(file))}.
25N/A *
25N/A * @param file the new standard error destination
25N/A * @return this process builder
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder redirectError(File file) {
25N/A return redirectError(Redirect.to(file));
25N/A }
25N/A
25N/A /**
25N/A * Returns this process builder's standard input source.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method obtain their standard input from this source.
25N/A * The initial value is {@link Redirect#PIPE Redirect.PIPE}.
25N/A *
25N/A * @return this process builder's standard input source
25N/A * @since 1.7
25N/A */
25N/A public Redirect redirectInput() {
25N/A return (redirects == null) ? Redirect.PIPE : redirects[0];
25N/A }
25N/A
25N/A /**
25N/A * Returns this process builder's standard output destination.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method redirect their standard output to this destination.
25N/A * The initial value is {@link Redirect#PIPE Redirect.PIPE}.
25N/A *
25N/A * @return this process builder's standard output destination
25N/A * @since 1.7
25N/A */
25N/A public Redirect redirectOutput() {
25N/A return (redirects == null) ? Redirect.PIPE : redirects[1];
25N/A }
25N/A
25N/A /**
25N/A * Returns this process builder's standard error destination.
25N/A *
25N/A * Subprocesses subsequently started by this object's {@link #start()}
25N/A * method redirect their standard error to this destination.
25N/A * The initial value is {@link Redirect#PIPE Redirect.PIPE}.
25N/A *
25N/A * @return this process builder's standard error destination
25N/A * @since 1.7
25N/A */
25N/A public Redirect redirectError() {
25N/A return (redirects == null) ? Redirect.PIPE : redirects[2];
25N/A }
25N/A
25N/A /**
25N/A * Sets the source and destination for subprocess standard I/O
25N/A * to be the same as those of the current Java process.
25N/A *
25N/A * <p>This is a convenience method. An invocation of the form
25N/A * <pre> {@code
25N/A * pb.inheritIO()
25N/A * }</pre>
25N/A * behaves in exactly the same way as the invocation
25N/A * <pre> {@code
25N/A * pb.redirectInput(Redirect.INHERIT)
25N/A * .redirectOutput(Redirect.INHERIT)
25N/A * .redirectError(Redirect.INHERIT)
25N/A * }</pre>
25N/A *
25N/A * This gives behavior equivalent to most operating system
25N/A * command interpreters, or the standard C library function
25N/A * {@code system()}.
25N/A *
25N/A * @return this process builder
25N/A * @since 1.7
25N/A */
25N/A public ProcessBuilder inheritIO() {
25N/A Arrays.fill(redirects(), Redirect.INHERIT);
25N/A return this;
25N/A }
25N/A
0N/A /**
0N/A * Tells whether this process builder merges standard error and
0N/A * standard output.
0N/A *
25N/A * <p>If this property is {@code true}, then any error output
0N/A * generated by subprocesses subsequently started by this object's
0N/A * {@link #start()} method will be merged with the standard
0N/A * output, so that both can be read using the
0N/A * {@link Process#getInputStream()} method. This makes it easier
0N/A * to correlate error messages with the corresponding output.
25N/A * The initial value is {@code false}.
0N/A *
25N/A * @return this process builder's {@code redirectErrorStream} property
0N/A */
0N/A public boolean redirectErrorStream() {
0N/A return redirectErrorStream;
0N/A }
0N/A
0N/A /**
25N/A * Sets this process builder's {@code redirectErrorStream} property.
0N/A *
25N/A * <p>If this property is {@code true}, then any error output
0N/A * generated by subprocesses subsequently started by this object's
0N/A * {@link #start()} method will be merged with the standard
0N/A * output, so that both can be read using the
0N/A * {@link Process#getInputStream()} method. This makes it easier
0N/A * to correlate error messages with the corresponding output.
25N/A * The initial value is {@code false}.
0N/A *
25N/A * @param redirectErrorStream the new property value
25N/A * @return this process builder
0N/A */
0N/A public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
0N/A this.redirectErrorStream = redirectErrorStream;
0N/A return this;
0N/A }
0N/A
0N/A /**
0N/A * Starts a new process using the attributes of this process builder.
0N/A *
0N/A * <p>The new process will
0N/A * invoke the command and arguments given by {@link #command()},
0N/A * in a working directory as given by {@link #directory()},
0N/A * with a process environment as given by {@link #environment()}.
0N/A *
0N/A * <p>This method checks that the command is a valid operating
0N/A * system command. Which commands are valid is system-dependent,
0N/A * but at the very least the command must be a non-empty list of
0N/A * non-null strings.
0N/A *
4103N/A * <p>A minimal set of system dependent environment variables may
4103N/A * be required to start a process on some operating systems.
4103N/A * As a result, the subprocess may inherit additional environment variable
4103N/A * settings beyond those in the process builder's {@link #environment()}.
4103N/A *
0N/A * <p>If there is a security manager, its
0N/A * {@link SecurityManager#checkExec checkExec}
0N/A * method is called with the first component of this object's
25N/A * {@code command} array as its argument. This may result in
0N/A * a {@link SecurityException} being thrown.
0N/A *
0N/A * <p>Starting an operating system process is highly system-dependent.
0N/A * Among the many things that can go wrong are:
0N/A * <ul>
0N/A * <li>The operating system program file was not found.
0N/A * <li>Access to the program file was denied.
0N/A * <li>The working directory does not exist.
0N/A * </ul>
0N/A *
0N/A * <p>In such cases an exception will be thrown. The exact nature
0N/A * of the exception is system-dependent, but it will always be a
0N/A * subclass of {@link IOException}.
0N/A *
0N/A * <p>Subsequent modifications to this process builder will not
25N/A * affect the returned {@link Process}.
0N/A *
25N/A * @return a new {@link Process} object for managing the subprocess
25N/A *
25N/A * @throws NullPointerException
25N/A * if an element of the command list is null
0N/A *
25N/A * @throws IndexOutOfBoundsException
25N/A * if the command is an empty list (has size {@code 0})
0N/A *
25N/A * @throws SecurityException
25N/A * if a security manager exists and
25N/A * <ul>
25N/A *
25N/A * <li>its
25N/A * {@link SecurityManager#checkExec checkExec}
25N/A * method doesn't allow creation of the subprocess, or
0N/A *
25N/A * <li>the standard input to the subprocess was
25N/A * {@linkplain #redirectInput redirected from a file}
25N/A * and the security manager's
25N/A * {@link SecurityManager#checkRead checkRead} method
25N/A * denies read access to the file, or
0N/A *
25N/A * <li>the standard output or standard error of the
25N/A * subprocess was
25N/A * {@linkplain #redirectOutput redirected to a file}
25N/A * and the security manager's
25N/A * {@link SecurityManager#checkWrite checkWrite} method
25N/A * denies write access to the file
0N/A *
25N/A * </ul>
25N/A *
25N/A * @throws IOException if an I/O error occurs
25N/A *
25N/A * @see Runtime#exec(String[], String[], java.io.File)
0N/A */
0N/A public Process start() throws IOException {
0N/A // Must convert to array first -- a malicious user-supplied
0N/A // list might try to circumvent the security check.
0N/A String[] cmdarray = command.toArray(new String[command.size()]);
2265N/A cmdarray = cmdarray.clone();
2265N/A
0N/A for (String arg : cmdarray)
0N/A if (arg == null)
0N/A throw new NullPointerException();
0N/A // Throws IndexOutOfBoundsException if command is empty
0N/A String prog = cmdarray[0];
0N/A
0N/A SecurityManager security = System.getSecurityManager();
5975N/A if (security != null) {
0N/A security.checkExec(prog);
5975N/A }
0N/A
0N/A String dir = directory == null ? null : directory.toString();
0N/A
0N/A try {
0N/A return ProcessImpl.start(cmdarray,
0N/A environment,
0N/A dir,
25N/A redirects,
0N/A redirectErrorStream);
6391N/A } catch (IOException | IllegalArgumentException e) {
5975N/A String exceptionInfo = ": " + e.getMessage();
5975N/A Throwable cause = e;
6391N/A if ((e instanceof IOException) && security != null) {
5975N/A // Can not disclose the fail reason for read-protected files.
5975N/A try {
5975N/A security.checkRead(prog);
5975N/A } catch (AccessControlException ace) {
5975N/A exceptionInfo = "";
5975N/A cause = ace;
5975N/A }
5975N/A }
0N/A // It's much easier for us to create a high-quality error
0N/A // message than the low-level C code which found the problem.
0N/A throw new IOException(
0N/A "Cannot run program \"" + prog + "\""
0N/A + (dir == null ? "" : " (in directory \"" + dir + "\")")
5975N/A + exceptionInfo,
5975N/A cause);
0N/A }
0N/A }
0N/A}