08 java.lang.ProcessBuilder

来源:互联网 发布:hql与sql exists in 编辑:程序博客网 时间:2024/06/10 19:14

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, 所以请大家指出!

0 0