com.hfg.util.Executor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.util;
import java.io.*;
import java.util.List;
import java.util.ArrayList;
//------------------------------------------------------------------------------
/**
More robust wrapper for Process.exec().
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
// TODO:
// - Make the internal threads reusable
public class Executor
{
//***************************************************************************
// PRIVATE FIELDS
//***************************************************************************
private String mCmd;
private List mEnvVars;
private File mWorkingDir;
private String mSTDINString;
private InputStream mSTDINStream;
private File mShell = new File("/bin/csh");
private boolean mRunning;
private String mSTDOUT;
private String mSTDERR;
private int mExitValue;
private long mExecutionTime;
private static int BUFFER_SIZE = 8192;
//***************************************************************************
// CONSTRUCTORS
//***************************************************************************
//---------------------------------------------------------------------------
/**
Default constructor.
*/
public Executor()
{
}
//---------------------------------------------------------------------------
/**
Constructor specifying the command to execute.
*/
public Executor(String inCommand)
{
this();
setCommand(inCommand);
}
//---------------------------------------------------------------------------
/**
Constructor specifying the command to execute.
*/
public Executor(String[] inCommand)
{
this(StringUtil.join(inCommand));
}
//***************************************************************************
// PUBLIC METHODS
//***************************************************************************
//---------------------------------------------------------------------------
/**
Sets the command to execute.
*/
public void setCommand(String inCommand)
{
mCmd = inCommand;
}
//---------------------------------------------------------------------------
/**
Returns the command to execute.
*/
public String getCommand()
{
return mCmd;
}
//---------------------------------------------------------------------------
/**
Clears the list of environment variables to be set when executing the command.
IMPORTANT! If the environment variables are cleared it does NOT mean
that no environment variables are present, but rather that the parent process'
environment variables are inherited.
*/
public void clearEnvVars()
{
mEnvVars = null;
}
//---------------------------------------------------------------------------
/**
Adds the specified environment variable to the list to be set when executing
the command.
IMPORTANT! If environment variables are specified they are specified
IN PLACE OF the parent process' environment variables and NOT in addition to
them.
*/
public void addEnvVar(String inName, String inValue)
{
if (null == inName)
{
throw new ExecutorException("Environment variable names cannot be null!");
}
if (null == mEnvVars) mEnvVars = new ArrayList(5);
mEnvVars.add(inName + (inValue != null ? "=" + inValue : ""));
}
//---------------------------------------------------------------------------
/**
Sets the shell to be used for command execution. The default is '/bin/csh'.
To run the command without a <shell> -c '<cmd>' construct, set the shell
to null.
*/
public void setShell(File inValue)
{
if (inValue != null
&& !inValue.exists())
{
throw new ExecutorException("The shell '" + inValue + "' does not exist!");
}
mShell = inValue;
}
//---------------------------------------------------------------------------
/**
Returns the shell to be used for command execution.
*/
public File getShell()
{
return mShell;
}
//---------------------------------------------------------------------------
/**
Sets the working directory for command execution.
*/
public void setWorkingDir(File inValue)
{
mWorkingDir = inValue;
}
//---------------------------------------------------------------------------
/**
Returns the working directory for command execution.
*/
public File getWorkingDir()
{
return mWorkingDir;
}
//---------------------------------------------------------------------------
/**
Sets the STDIN content for the command to be executed.
*/
public void setSTDIN(String inValue)
{
mSTDINString = inValue;
mSTDINStream = null;
}
//---------------------------------------------------------------------------
/**
Sets the STDIN content for the command to be executed.
*/
public void setSTDIN(InputStream inValue)
{
mSTDINString = null;
mSTDINStream = inValue;
}
//---------------------------------------------------------------------------
/**
Returns the STDOUT output produced by the executed command.
*/
public String getSTDOUT()
{
return mSTDOUT;
}
//---------------------------------------------------------------------------
/**
Returns the STDERR output produced by the executed command.
*/
public String getSTDERR()
{
return mSTDERR;
}
//---------------------------------------------------------------------------
/**
Returns the execution time in milliseconds of executed command. This is wall
time, not CPU time.
*/
public long getExecutionTimeMillis()
{
return mExecutionTime;
}
//---------------------------------------------------------------------------
/**
Executes the command.
@return the exit value of the executed command.
@throws ExecutorException
*/
public int exec()
throws ExecutorException
{
setup();
mRunning = true;
long startTime = System.currentTimeMillis();
Job job = new Job();
Thread thread = new Thread(job);
thread.start();
while (mRunning)
{
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
}
}
mExecutionTime = System.currentTimeMillis() - startTime;
return mExitValue;
}
//***************************************************************************
// PRIVATE METHODS
//***************************************************************************
//---------------------------------------------------------------------------
private void setup()
{
mSTDOUT = null;
mSTDERR = null;
}
//---------------------------------------------------------------------------
private String[] getCmdAsArray()
{
String[] cmd = {mShell.toString(),
"-f", // The shell ignores the ~/.cshrc (or appropriate
// login script for the shell).
"-c", getCommand()};
return cmd;
}
//---------------------------------------------------------------------------
private String[] getEnvVarsAsArray()
{
String[] env = null;
if (mEnvVars != null)
{
env = new String[mEnvVars.size()];
for (int i = 0; i < mEnvVars.size(); i++)
{
env[i] = (String) mEnvVars.get(i);
}
}
return env;
}
//***************************************************************************
// INNER CLASS
//***************************************************************************
private class Job implements Runnable
{
//------------------------------------------------------------------------
public void run()
{
try
{
Process process = getProcess();
// Set STDIN if specified
setSTDIN(process);
// Use separate threads to read from STDOUT and STDERR to avoid
// potential blocking problems.
JobOutput stdout = new JobOutput(process.getInputStream());
Thread stdoutThread = new Thread(stdout);
stdoutThread.start();
JobOutput stderr = new JobOutput(process.getErrorStream());
Thread stderrThread = new Thread(stderr);
stderrThread.start();
process.waitFor();
while ( ! stdout.isDone()
|| ! stderr.isDone())
{
Thread.sleep(25);
}
mSTDOUT = stdout.getContent();
mSTDERR = stderr.getContent();
mExitValue = process.exitValue();
}
catch (Throwable e)
{
throw new ExecutorException("Command " + StringUtil.singleQuote(getCommand())
+ " produced an exception!", e);
}
finally
{
mRunning = false;
}
}
//------------------------------------------------------------------------
private Process getProcess()
throws Exception
{
Process process = null;
if (null == mShell)
{
process = Runtime.getRuntime().exec(getCommand(),
getEnvVarsAsArray(),
getWorkingDir());
}
else
{
process = Runtime.getRuntime().exec(getCmdAsArray(),
getEnvVarsAsArray(),
getWorkingDir());
}
return process;
}
//------------------------------------------------------------------------
private void setSTDIN(Process inProcess)
throws IOException
{
if (mSTDINString != null)
{
PrintWriter writer = new PrintWriter(inProcess.getOutputStream());
writer.print(mSTDINString);
writer.close();
}
else if (mSTDINStream != null)
{
PrintWriter writer = new PrintWriter(inProcess.getOutputStream());
InputStreamReader reader = new InputStreamReader(mSTDINStream);
char[] buffer = new char[BUFFER_SIZE];
int charsRead = 0;
while ((charsRead = reader.read(buffer, 0, BUFFER_SIZE)) != -1)
{
writer.write(buffer, 0, charsRead);
}
writer.close();
}
}
}
//***************************************************************************
// INNER CLASS
//***************************************************************************
// Generic class to handle reading the STDOUT and STDERR streams from the process.
private class JobOutput implements Runnable
{
private InputStream mInputStream;
private StringBuffer mContent = new StringBuffer();
private boolean mDone = false;
//************************************************************************
// CONSTRUCTORS
//************************************************************************
//------------------------------------------------------------------------
public JobOutput(InputStream inProcessStream)
{
mInputStream = inProcessStream;
}
//************************************************************************
// PUBLIC METHODS
//************************************************************************
//------------------------------------------------------------------------
public void run()
{
try
{
InputStreamReader reader = new InputStreamReader(mInputStream);
char[] buffer = new char[BUFFER_SIZE];
int charsRead = 0;
while ((charsRead = reader.read(buffer, 0, BUFFER_SIZE)) != -1)
{
mContent.append(buffer, 0, charsRead);
}
}
catch (Throwable e)
{
throw new ExecutorException(e);
}
finally
{
mDone = true;
}
}
//------------------------------------------------------------------------
public boolean isDone()
{
return mDone;
}
//------------------------------------------------------------------------
public String getContent()
{
return mContent.toString();
}
}
}