/**
Copyright (C) 2012 Delcyon, Inc.
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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.delcyon.capo.util;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class CommandExecution extends Thread
{
private String command;
private String stdout = null;
private String stderr = null;
private int exitCode = -1;
private boolean isFinished = false;
private Exception exception = null;
private static final long MAX_RUNTIME = 250000l;
private Process process;
private Long timeout;
public CommandExecution(String command,Long timeout)
{
super(command);
this.command = command;
if (timeout == null || timeout == 0l)
{
this.timeout = MAX_RUNTIME;
}
else
{
this.timeout = timeout;
}
}
public CommandExecution(String command, String timeoutString)
{
super(command);
this.command = command;
if (timeoutString == null || timeoutString.trim().isEmpty() || timeoutString.matches("\\d+") == false)
{
this.timeout = MAX_RUNTIME;
}
else
{
Long timeout = Long.valueOf(timeoutString);
if (timeout == null || timeout == 0l)
{
this.timeout = MAX_RUNTIME;
}
else
{
this.timeout = timeout;
}
}
}
public void executeCommand() throws Exception
{
long startTime = System.currentTimeMillis();
start();
while(isFinished == false)
{
sleep(100);
if (System.currentTimeMillis() - startTime > timeout)
{
process.destroy();
throw new Exception("command timed out: '"+command+"'");
}
}
if (exception != null)
{
throw exception;
}
}
public void run()
{
try
{
String[] commandArray = {"/bin/sh","-c",command};
process = Runtime.getRuntime().exec(commandArray,null);
InputStream stdoutInputStream = process.getInputStream();
InputStream stderrInputStream = process.getErrorStream();
ByteArrayOutputStream errorByteArrayOutputStream = new ByteArrayOutputStream();
ByteArrayOutputStream outputByteArrayOutputStream = new ByteArrayOutputStream();
int errorValue = 0;
int outputValue = 0;
byte[] buffer = new byte[4096];
while (outputValue >= 0 && errorValue >= 0)
{
//see if we have anything to read from stdout
if (outputValue >= 0 && stdoutInputStream.available() > 0)
{
outputValue = stdoutInputStream.read(buffer);
outputByteArrayOutputStream.write(buffer,0,outputValue);
}
else
{
outputValue = 0;
}
//see if we have anything to read from stderr
if (errorValue >= 0 && stderrInputStream.available() > 0)
{
errorValue = stderrInputStream.read(buffer);
errorByteArrayOutputStream.write(buffer,0,errorValue);
}
else
{
errorValue = 0;
}
//we aren't closed, but we didn't read anything either
if (outputValue == 0 && errorValue == 0)
{
//this is arbitrary, but should keep us from running the cpu to hot
sleep(50);
try
{
process.exitValue();
//just because the process has finished doesn't mean we've gotten all of the data from the buffers yet
//so double check to make sure we don't having anything available
if (stderrInputStream.available() == 0 && stdoutInputStream.available() == 0)
{
break;
}
}
catch (IllegalThreadStateException e) {
//thread not finished
}
}
}
stderr = new String(errorByteArrayOutputStream.toByteArray()).trim();
stdout = new String(outputByteArrayOutputStream.toByteArray()).trim();
exitCode = process.waitFor();
stderrInputStream.close();
stdoutInputStream.close();
}
catch (Exception exception)
{
this.exception = new Exception("error running command: "+command,exception);
}
finally
{
process.destroy();
isFinished = true;
}
}
/**
* @return the stderr
*/
public String getStderr()
{
return stderr;
}
/**
* @return the stdout
*/
public String getStdout()
{
return stdout;
}
/**
* @return the exitCode
*/
public int getExitCode()
{
return exitCode;
}
/**
* @return
*/
public String getCommand()
{
return command;
}
}