/*
* 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 * "http://ostermiller.org/utils/ExecHelper.html">ostermiller.org.
*
* @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.
*
* Output from the process is expected to be text in the system's default character set.
*
* 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.
*
* Output from the process is expected to be text in the system's default character set.
*
* 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.
*
* Output from the process is expected to be text in the system's default character set.
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* A shell should be used to execute commands when features such as file redirection, pipes,
* argument parsing are desired.
*
* Output from the process is expected to be text in the system's default character set.
*
* 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.
*
* A shell should be used to execute commands when features such as file redirection, pipes,
* argument parsing are desired.
*
* 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;
}
}