ProcessBuilder[final]
2015.01.19
By 970655147
这个类主要适用于启动当前操作系统的进程, 有些时候很有用
比如 : 我们想使用一个程序来帮我们使用指定的程序打开指定的文件, 那么我们就可以使用这个
start ->
声明
, 大家可以看看注释
/**
* This class is used to create operating system processes.
*
* <p>Each {@code ProcessBuilder} instance manages a collection
* of process attributes. The {@link #start()} method creates a new
* {@link Process} instance with those attributes. The {@link
* #start()} method can be invoked repeatedly from the same instance
* to create new subprocesses with identical or related attributes.
*
* <p>Each process builder manages these process attributes:
*
* <ul>
*
* <li>a <i>command</i>, a list of strings which signifies the
* external program file to be invoked and its arguments, if any.
* Which string lists represent a valid operating system command is
* system-dependent. For example, it is common for each conceptual
* argument to be an element in this list, but there are operating
* systems where programs are expected to tokenize command line
* strings themselves - on such a system a Java implementation might
* require commands to contain exactly two elements.
*
* <li>an <i>environment</i>, which is a system-dependent mapping from
* <i>variables</i> to <i>values</i>. The initial value is a copy of
* the environment of the current process (see {@link System#getenv()}).
*
* <li>a <i>working directory</i>. The default value is the current
* working directory of the current process, usually the directory
* named by the system property {@code user.dir}.
*
* <li><a name="redirect-input">a source of <i>standard input</i>.
* By default, the subprocess reads input from a pipe. Java code
* can access this pipe via the output stream returned by
* {@link Process#getOutputStream()}. However, standard input may
* be redirected to another source using
* {@link #redirectInput(Redirect) redirectInput}.
* In this case, {@link Process#getOutputStream()} will return a
* <i>null output stream</i>, for which:
*
* <ul>
* <li>the {@link OutputStream#write(int) write} methods always
* throw {@code IOException}
* <li>the {@link OutputStream#close() close} method does nothing
* </ul>
*
* <li><a name="redirect-output">a destination for <i>standard output</i>
* and <i>standard error</i>. By default, the subprocess writes standard
* output and standard error to pipes. Java code can access these pipes
* via the input streams returned by {@link Process#getInputStream()} and
* {@link Process#getErrorStream()}. However, standard output and
* standard error may be redirected to other destinations using
* {@link #redirectOutput(Redirect) redirectOutput} and
* {@link #redirectError(Redirect) redirectError}.
* In this case, {@link Process#getInputStream()} and/or
* {@link Process#getErrorStream()} will return a <i>null input
* stream</i>, for which:
*
* <ul>
* <li>the {@link InputStream#read() read} methods always return
* {@code -1}
* <li>the {@link InputStream#available() available} method always returns
* {@code 0}
* <li>the {@link InputStream#close() close} method does nothing
* </ul>
*
* <li>a <i>redirectErrorStream</i> property. Initially, this property
* is {@code false}, meaning that the standard output and error
* output of a subprocess are sent to two separate streams, which can
* be accessed using the {@link Process#getInputStream()} and {@link
* Process#getErrorStream()} methods.
*
* <p>If the value is set to {@code true}, then:
*
* <ul>
* <li>standard error is merged with the standard output and always sent
* to the same destination (this makes it easier to correlate error
* messages with the corresponding output)
* <li>the common destination of standard error and standard output can be
* redirected using
* {@link #redirectOutput(Redirect) redirectOutput}
* <li>any redirection set by the
* {@link #redirectError(Redirect) redirectError}
* method is ignored when creating a subprocess
* <li>the stream returned from {@link Process#getErrorStream()} will
* always be a <a href="#redirect-output">null input stream</a>
* </ul>
*
* </ul>
*
* <p>Modifying a process builder's attributes will affect processes
* subsequently started by that object's {@link #start()} method, but
* will never affect previously started processes or the Java process
* itself.
*
* <p>Most error checking is performed by the {@link #start()} method.
* It is possible to modify the state of an object so that {@link
* #start()} will fail. For example, setting the command attribute to
* an empty list will not throw an exception unless {@link #start()}
* is invoked.
*
* <p><strong>Note that this class is not synchronized.</strong>
* If multiple threads access a {@code ProcessBuilder} instance
* concurrently, and at least one of the threads modifies one of the
* attributes structurally, it <i>must</i> be synchronized externally.
*
* <p>Starting a new process which uses the default working directory
* and environment is easy:
*
* <pre> {@code
* Process p = new ProcessBuilder("myCommand", "myArg").start();
* }</pre>
*
* <p>Here is an example that starts a process with a modified working
* directory and environment, and redirects standard output and error
* to be appended to a log file:
*
* <pre> {@code
* ProcessBuilder pb =
* new ProcessBuilder("myCommand", "myArg1", "myArg2");
* Map<String, String> env = pb.environment();
* env.put("VAR1", "myValue");
* env.remove("OTHERVAR");
* env.put("VAR2", env.get("VAR1") + "suffix");
* pb.directory(new File("myDir"));
* File log = new File("log");
* pb.redirectErrorStream(true);
* pb.redirectOutput(Redirect.appendTo(log));
* Process p = pb.start();
* assert pb.redirectInput() == Redirect.PIPE;
* assert pb.redirectOutput().file() == log;
* assert p.getInputStream().read() == -1;
* }</pre>
*
* <p>To start a process with an explicit set of environment
* variables, first call {@link java.util.Map#clear() Map.clear()}
* before adding environment variables.
*
* @author Martin Buchholz
* @since 1.5
*/
public final class ProcessBuilder
ProcessBuilder. 属性
// command 为需要运行的程序以及参数 directory为程序所在目录
// environment 为环境变量
// redirectErrorStream=false将子进程的标准输出和错误输出被发送给两个独立的流errorStream
// redirects为重定向输入/输出/错误流到指定的文件
private List<String> command;
private File directory;
private Map<String,String> environment;
private boolean redirectErrorStream;
private Redirect[] redirects;
ProcessBuilder. command()
// 设置command, 获取command
public ProcessBuilder command(List<String> command) {
if (command == null)
throw new NullPointerException();
this.command = command;
return this;
}
public ProcessBuilder command(String... command) {
this.command = new ArrayList<>(command.length);
for (String arg : command)
this.command.add(arg);
return this;
}
public List<String> command() {
return command;
}
ProcessBuilder. environment()
// 设置environment, 获取environment
public Map<String,String> environment() {
SecurityManager security = System.getSecurityManager();
// checkPermission
if (security != null)
security.checkPermission(new RuntimePermission("getenv.*"));
// 获取系统环境变量如果environment是null
if (environment == null)
environment = ProcessEnvironment.environment();
// assert 确保environment!=null
assert environment != null;
return environment;
}
ProcessBuilder environment(String[] envp)
// Only for use by Runtime.exec(...envp...)
ProcessBuilder environment(String[] envp) {
assert environment == null;
if (envp != null) {
// 获取一个长为envp.length的Map<String, String>
environment = ProcessEnvironment.emptyEnvironment(envp.length);
assert environment != null;
for (String envstring : envp) {
// Before 1.5, we blindly passed invalid envstrings
// to the child process.
// We would like to throw an exception, but do not,
// for compatibility with old broken code.
// Silently discard any trailing junk.
// 空格的index
if (envstring.indexOf((int) '\u0000') != -1)
envstring = envstring.replaceFirst("\u0000.*", "");
int eqlsign =
envstring.indexOf('=', ProcessEnvironment.MIN_NAME_LENGTH);
// Silently ignore envstrings lacking the required `='.
// 如果每个envString存在”=” 将kvPair放入environment中
if (eqlsign != -1)
environment.put(envstring.substring(0,eqlsign),
envstring.substring(eqlsign+1));
}
}
return this;
}
ProcessEnvironment. emptyEnvironment(int capacity)
// Only for use by ProcessBuilder.environment(String[] envp)
static Map<String,String> emptyEnvironment(int capacity) {
return new ProcessEnvironment(capacity);
}
final class ProcessEnvironment extends HashMap<String,String>
ProcessBuilder. directory()
// 设置environment, 获取environment
public File directory() {
return directory;
}
public ProcessBuilder directory(File directory) {
this.directory = directory;
return this;
}
NullInputStream & NullOutputStream [InnerClass]
// 如果没有配置输入/ 输出/ 错误 的载体, 则使用NullInputStream/ NullOutputStream. INSTANCE 作为载体[详见ProcessImpl]
/**
* Implements a <a href="#redirect-output">null input stream</a>.
*/
static class NullInputStream extends InputStream {
static final NullInputStream INSTANCE = new NullInputStream();
private NullInputStream() {}
public int read() { return -1; }
public int available() { return 0; }
}
static class NullOutputStream extends OutputStream {
static final NullOutputStream INSTANCE = new NullOutputStream();
private NullOutputStream() {}
public void write(int b) throws IOException {
throw new IOException("Stream closed");
}
}
Redirect[InnerClass]
// 用于标识重定向在"载体"
public static abstract class Redirect {
/**
* The type of a {@link Redirect}.
// 只有PIPE, INHERIT, READ, WRITE, APPEND 五种type
*/
public enum Type {
/**
* The type of {@link Redirect#PIPE Redirect.PIPE}.
*/
PIPE,
/**
* The type of {@link Redirect#INHERIT Redirect.INHERIT}.
*/
INHERIT,
/**
* The type of redirects returned from
* {@link Redirect#from Redirect.from(File)}.
*/
READ,
/**
* The type of redirects returned from
* {@link Redirect#to Redirect.to(File)}.
*/
WRITE,
/**
* The type of redirects returned from
* {@link Redirect#appendTo Redirect.appendTo(File)}.
*/
APPEND
};
/**
* Returns the type of this {@code Redirect}.
* @return the type of this {@code Redirect}
*/
public abstract Type type();
/**
* Indicates that subprocess I/O will be connected to the
* current Java process over a pipe.
*
* This is the default handling of subprocess standard I/O.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.PIPE.file() == null &&
* Redirect.PIPE.type() == Redirect.Type.PIPE
* }</pre>
*/
// PIPE INSTANCE
public static final Redirect PIPE = new Redirect() {
public Type type() { return Type.PIPE; }
public String toString() { return type().toString(); }};
/**
* Indicates that subprocess I/O source or destination will be the
* same as those of the current process. This is the normal
* behavior of most operating system command interpreters (shells).
*
* <p>It will always be true that
* <pre> {@code
* Redirect.INHERIT.file() == null &&
* Redirect.INHERIT.type() == Redirect.Type.INHERIT
* }</pre>
*/
// PIPE INSTANCE
public static final Redirect INHERIT = new Redirect() {
public Type type() { return Type.INHERIT; }
public String toString() { return type().toString(); }};
/**
* Returns the {@link File} source or destination associated
* with this redirect, or {@code null} if there is no such file.
*
* @return the file associated with this redirect,
* or {@code null} if there is no such file
*/
public File file() { return null; }
/**
* When redirected to a destination file, indicates if the output
* is to be written to the end of the file.
*/
boolean append() {
throw new UnsupportedOperationException();
}
/**
* Returns a redirect to read from the specified file.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.from(file).file() == file &&
* Redirect.from(file).type() == Redirect.Type.READ
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to read from the specified file
*/
// READ INSTANCE
public static Redirect from(final File file) {
if (file == null)
throw new NullPointerException();
return new Redirect() {
public Type type() { return Type.READ; }
public File file() { return file; }
public String toString() {
return "redirect to read from file \"" + file + "\"";
}
};
}
/**
* Returns a redirect to write to the specified file.
* If the specified file exists when the subprocess is started,
* its previous contents will be discarded.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.to(file).file() == file &&
* Redirect.to(file).type() == Redirect.Type.WRITE
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to write to the specified file
*/
// WRITE INSTANCE
public static Redirect to(final File file) {
if (file == null)
throw new NullPointerException();
return new Redirect() {
public Type type() { return Type.WRITE; }
public File file() { return file; }
public String toString() {
return "redirect to write to file \"" + file + "\"";
}
boolean append() { return false; }
};
}
/**
* Returns a redirect to append to the specified file.
* Each write operation first advances the position to the
* end of the file and then writes the requested data.
* Whether the advancement of the position and the writing
* of the data are done in a single atomic operation is
* system-dependent and therefore unspecified.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.appendTo(file).file() == file &&
* Redirect.appendTo(file).type() == Redirect.Type.APPEND
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to append to the specified file
*/
// APPEND INSTANCE
public static Redirect appendTo(final File file) {
if (file == null)
throw new NullPointerException();
return new Redirect() {
public Type type() { return Type.APPEND; }
public File file() { return file; }
public String toString() {
return "redirect to append to file \"" + file + "\"";
}
boolean append() { return true; }
};
}
/**
* Compares the specified object with this {@code Redirect} for
* equality. Returns {@code true} if and only if the two
* objects are identical or both objects are {@code Redirect}
* instances of the same type associated with non-null equal
* {@code File} instances.
*/
public boolean equals(Object obj) {
// 如果引用相等 直接返回true
if (obj == this)
return true;
// RTTI
if (! (obj instanceof Redirect))
return false;
// 先比较type 在比较file
Redirect r = (Redirect) obj;
if (r.type() != this.type())
return false;
assert this.file() != null;
return this.file().equals(r.file());
}
/**
* Returns a hash code value for this {@code Redirect}.
* @return a hash code value for this {@code Redirect}
*/
public int hashCode() {
File file = file();
if (file == null)
return super.hashCode();
else
return file.hashCode();
}
/**
* No public constructors. Clients must use predefined
* static {@code Redirect} instances or factory methods.
*/
//构造方法私有化
// 保证了除了本身提供的Redirect对象 和from, to方法构造Redirect外 不能另外构建对象
private Redirect() {}
}
ProcessBuilder. redirect Input/ Output/ Error(Redirect source)
public ProcessBuilder redirectInput(Redirect source) {
// 如果source的type是write或者append 抛出异常
// source的type 属于 read, pipe, inherit
if (source.type() == Redirect.Type.WRITE ||
source.type() == Redirect.Type.APPEND)
throw new IllegalArgumentException(
"Redirect invalid for reading: " + source);
// 重定向输入
redirects()[0] = source;
return this;
}
public ProcessBuilder redirectOutput(Redirect destination) {
if (destination.type() == Redirect.Type.READ)
throw new IllegalArgumentException(
"Redirect invalid for writing: " + destination);
redirects()[1] = destination;
return this;
}
public ProcessBuilder redirectError(Redirect destination) {
if (destination.type() == Redirect.Type.READ)
throw new IllegalArgumentException(
"Redirect invalid for writing: " + destination);
redirects()[2] = destination;
return this;
}
public ProcessBuilder redirectInput(File file) {
return redirectInput(Redirect.from(file));
}
public ProcessBuilder redirectOutput(File file) {
return redirectOutput(Redirect.to(file));
}
public ProcessBuilder redirectError(File file) {
return redirectError(Redirect.to(file));
}
private Redirect[] redirects() {
// 如果redirects没有初始化 则新建一个三个对象的Redirect数组
// 表示输入/输出/错误重定向
if (redirects == null)
redirects = new Redirect[] {
Redirect.PIPE, Redirect.PIPE, Redirect.PIPE
};
return redirects;
}
// 默认的输入/ 输出/ 错误 重定向为PIPE
public Redirect redirectInput() {
return (redirects == null) ? Redirect.PIPE : redirects[0];
}
public Redirect redirectOutput() {
return (redirects == null) ? Redirect.PIPE : redirects[1];
}
public Redirect redirectError() {
return (redirects == null) ? Redirect.PIPE : redirects[2];
}
ProcessBuilder. redirectErrorStream()
public boolean redirectErrorStream() {
return redirectErrorStream;
}
public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
this.redirectErrorStream = redirectErrorStream;
return this;
}
ProcessBuilder. inheritIO()
public ProcessBuilder inheritIO() {
// 将redirects都设置为Redirect.INHERIT
Arrays.fill(redirects(), Redirect.INHERIT);
return this;
}
ProcessBuilder. start()
// 这个是ProcessBuilder的核心业务方法, 也是我们使用到的最多的方法, 启动一个进程
public Process start() throws IOException {
// Must convert to array first -- a malicious user-supplied
// list might try to circumvent the security check.
String[] cmdarray = command.toArray(new String[command.size()]);
// toArray不是会新建一个数组吗 为什么还需要clone..?
cmdarray = cmdarray.clone();
// 校验是否存在null参数
for (String arg : cmdarray)
if (arg == null)
throw new NullPointerException();
// Throws IndexOutOfBoundsException if command is empty
// 第一个字符串为要启动的程序
String prog = cmdarray[0];
// security check
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExec(prog);
}
// 为什么要新建一个字符串?并发?
String dir = directory == null ? null : directory.toString();
try {
// 新建一个进程实例
return ProcessImpl.start(cmdarray,
environment,
dir,
redirects,
redirectErrorStream);
} catch (IOException | IllegalArgumentException e) {
String exceptionInfo = ": " + e.getMessage();
Throwable cause = e;
if ((e instanceof IOException) && security != null) {
// Can not disclose the fail reason for read-protected files.
// 不能公开读取受保护的文件 exceptionInfo=””
try {
security.checkRead(prog);
} catch (AccessControlException ace) {
exceptionInfo = "";
cause = ace;
}
}
// It's much easier for us to create a high-quality error
// message than the low-level C code which found the problem.
// 抛出异常
throw new IOException(
"Cannot run program \"" + prog + "\""
+ (dir == null ? "" : " (in directory \"" + dir + "\")")
+ exceptionInfo,
cause);
}
}
// System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[],
java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
throws IOException
{
// ...
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
FileInputStream f0 = null;
FileOutputStream f1 = null;
FileOutputStream f2 = null;
try {
long[] stdHandles;
if (redirects == null) {
stdHandles = new long[] { -1L, -1L, -1L };
} else {
stdHandles = new long[3];
// 如果输入type为pipe 设置输入标志位为-1L
// 如果是inherit 设置输入标志位为fdAccess.getHandle(FileDescriptor.in)
// 否则 f0设置为对应输入流重定向文件的FileInputStream,设置输入标志位为f0.getFD()
if (redirects[0] == Redirect.PIPE)
stdHandles[0] = -1L;
else if (redirects[0] == Redirect.INHERIT)
stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
else {
f0 = new FileInputStream(redirects[0].file());
stdHandles[0] = fdAccess.getHandle(f0.getFD());
}
if (redirects[1] == Redirect.PIPE)
stdHandles[1] = -1L;
else if (redirects[1] == Redirect.INHERIT)
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
else {
f1 = newFileOutputStream(redirects[1].file(),
redirects[1].append());
stdHandles[1] = fdAccess.getHandle(f1.getFD());
}
if (redirects[2] == Redirect.PIPE)
stdHandles[2] = -1L;
else if (redirects[2] == Redirect.INHERIT)
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
else {
f2 = newFileOutputStream(redirects[2].file(),
redirects[2].append());
stdHandles[2] = fdAccess.getHandle(f2.getFD());
}
}
// 新建一个进程实例
return new ProcessImpl(cmdarray, envblock, dir,
stdHandles, redirectErrorStream);
} finally {
// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
// 理论上来讲close()操作可能会抛出IOException
// 但是在这里可能不太可能会出现
try { if (f0 != null) f0.close(); }
finally {
try { if (f1 != null) f1.close(); }
finally { if (f2 != null) f2.close(); }
}
}
}
->
ok, ProcessBuilder 到此结束了, 这个类需要最核心的就是start方法了, 以及对于重定向的理解
资源下载 : http://download.csdn.net/detail/u011039332/9061385
注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!