ExecHelper.java Source code

Java tutorial

Introduction

Here is the source code for ExecHelper.java

Source

/*
 * Convenience methods for executing non-Java processes.
 * Copyright (C) 2005 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 */

import java.io.*;

/**
 * Convenience methods for executing non-Java processes.
 * More information about this class is available from <a target="_top" href=
 * "http://ostermiller.org/utils/ExecHelper.html">ostermiller.org</a>.
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.06.00
 */
public final class ExecHelper {

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * Output from the process is expected to be text in the system's default character set.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray), null);
    }

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * Output from the process is expected to be text in the system's default character set.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @param envp array of strings, each element of which has environment variable settings in format name=value.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray, String[] envp) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray, envp), null);
    }

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * Output from the process is expected to be text in the system's default character set.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @param envp array of strings, each element of which has environment variable settings in format name=value.
     * @param dir the working directory of the subprocess, or null if the subprocess should inherit the working directory of the current process.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray, String[] envp, File dir) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray, envp, dir), null);
    }

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @param charset Output from the executed command is expected to be in this character set.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray, String charset) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray), charset);
    }

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @param envp array of strings, each element of which has environment variable settings in format name=value.
     * @param charset Output from the executed command is expected to be in this character set.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray, String[] envp, String charset) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray, envp), charset);
    }

    /**
     * Executes the specified command and arguments in a separate process, and waits for the
     * process to finish.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param cmdarray array containing the command to call and its arguments.
     * @param envp array of strings, each element of which has environment variable settings in format name=value.
     * @param dir the working directory of the subprocess, or null if the subprocess should inherit the working directory of the current process.
     * @param charset Output from the executed command is expected to be in this character set.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper exec(String[] cmdarray, String[] envp, File dir, String charset) throws IOException {
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray, envp, dir), charset);
    }

    /**
     * Executes the specified command using a shell.  On windows uses cmd.exe or command.exe.
     * On other platforms it uses /bin/sh.
     * <p>
     * A shell should be used to execute commands when features such as file redirection, pipes,
     * argument parsing are desired.
     * <p>
     * Output from the process is expected to be text in the system's default character set.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param command String containing a command to be parsed by the shell and executed.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if command is null
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper execUsingShell(String command) throws IOException {
        return execUsingShell(command, null);
    }

    /**
     * Executes the specified command using a shell.  On windows uses cmd.exe or command.exe.
     * On other platforms it uses /bin/sh.
     * <p>
     * A shell should be used to execute commands when features such as file redirection, pipes,
     * argument parsing are desired.
     * <p>
     * No input is passed to the process on STDIN.
     *
     * @param command String containing a command to be parsed by the shell and executed.
     * @param charset Output from the executed command is expected to be in this character set.
     * @return The results of the execution in an ExecHelper object.
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if command is null
     *
     * @since ostermillerutils 1.06.00
     */
    public static ExecHelper execUsingShell(String command, String charset) throws IOException {
        if (command == null)
            throw new NullPointerException();
        String[] cmdarray;
        String os = System.getProperty("os.name");
        if (os.equals("Windows 95") || os.equals("Windows 98") || os.equals("Windows ME")) {
            cmdarray = new String[] { "command.exe", "/C", command };
        } else if (os.startsWith("Windows")) {
            cmdarray = new String[] { "cmd.exe", "/C", command };
        } else {
            cmdarray = new String[] { "/bin/sh", "-c", command };
        }
        return new ExecHelper(Runtime.getRuntime().exec(cmdarray), charset);
    }

    /**
     * Take a process, record its standard error and standard out streams, wait for it to finish
     *
     * @param process process to watch
     * @throws SecurityException if a security manager exists and its checkExec method doesn't allow creation of a subprocess.
     * @throws IOException - if an I/O error occurs
     * @throws NullPointerException - if cmdarray is null
     * @throws IndexOutOfBoundsException - if cmdarray is an empty array (has length 0).
     *
     * @since ostermillerutils 1.06.00
     */
    private ExecHelper(Process process, String charset) throws IOException {
        StringBuffer output = new StringBuffer();
        StringBuffer error = new StringBuffer();

        Reader stdout;
        Reader stderr;

        if (charset == null) {
            // This is one time that the system charset is appropriate,
            // don't specify a character set.
            stdout = new InputStreamReader(process.getInputStream());
            stderr = new InputStreamReader(process.getErrorStream());
        } else {
            stdout = new InputStreamReader(process.getInputStream(), charset);
            stderr = new InputStreamReader(process.getErrorStream(), charset);
        }
        char[] buffer = new char[1024];

        boolean done = false;
        boolean stdoutclosed = false;
        boolean stderrclosed = false;
        while (!done) {
            boolean readSomething = false;
            // read from the process's standard output
            if (!stdoutclosed && stdout.ready()) {
                readSomething = true;
                int read = stdout.read(buffer, 0, buffer.length);
                if (read < 0) {
                    readSomething = true;
                    stdoutclosed = true;
                } else if (read > 0) {
                    readSomething = true;
                    output.append(buffer, 0, read);
                }
            }
            // read from the process's standard error
            if (!stderrclosed && stderr.ready()) {
                int read = stderr.read(buffer, 0, buffer.length);
                if (read < 0) {
                    readSomething = true;
                    stderrclosed = true;
                } else if (read > 0) {
                    readSomething = true;
                    error.append(buffer, 0, read);
                }
            }
            // Check the exit status only we haven't read anything,
            // if something has been read, the process is obviously not dead yet.
            if (!readSomething) {
                try {
                    this.status = process.exitValue();
                    done = true;
                } catch (IllegalThreadStateException itx) {
                    // Exit status not ready yet.
                    // Give the process a little breathing room.
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ix) {
                        process.destroy();
                        throw new IOException("Interrupted - processes killed");
                    }
                }
            }
        }

        this.output = output.toString();
        this.error = error.toString();
    }

    /**
     * The output of the job that ran.
     *
     * @since ostermillerutils 1.06.00
     */
    private String output;

    /**
     * Get the output of the job that ran.
     *
     * @return Everything the executed process wrote to its standard output as a String.
     *
     * @since ostermillerutils 1.06.00
     */
    public String getOutput() {
        return output;
    }

    /**
     * The error output of the job that ran.
     *
     * @since ostermillerutils 1.06.00
     */
    private String error;

    /**
     * Get the error output of the job that ran.
     *
     * @return Everything the executed process wrote to its standard error as a String.
     *
     * @since ostermillerutils 1.06.00
     */
    public String getError() {
        return error;
    }

    /**
     * The status of the job that ran.
     *
     * @since ostermillerutils 1.06.00
     */
    private int status;

    /**
     * Get the status of the job that ran.
     *
     * @return exit status of the executed process, by convention, the value 0 indicates normal termination.
     *
     * @since ostermillerutils 1.06.00
     */
    public int getStatus() {
        return status;
    }
}