ExecHelper.java Source Code

  • ExecHelper Documentation and Examples
  • ExecHelper Javadoc
    /*
     * Convenience methods for executing non-Java processes.
     * Copyright (C) 2005-2010 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 LICENSE.txt for details.
     */
    
    package com.Ostermiller.util;
    
    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 sub process.
    	 * @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 sub process.
    	 * @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 sub process, or null if the sub process 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 sub process.
    	 * @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 sub process.
    	 * @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 sub process.
    	 * @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 sub process, or null if the sub process 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 sub process.
    	 * @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 sub process.
    	 * @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 sub process.
    	 * @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 sub process.
    	 * @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;
    	}
    }