package org.syrup.functions;
import org.syrup.Context;
import org.syrup.Function;
import org.syrup.Result;
import org.syrup.helpers.DataImpl;
import org.syrup.helpers.ResultImpl;
import org.syrup.helpers.Writer;
import org.syrup.helpers.Utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.logging.Logger;
/**
* Executes a shell command provided by the first input. The second (optional)
* input provides the stdin. The first output connects to stdout and the second
* output to stderr. If the second input is not given the first (command) input
* is consumed. The second input is always consumed.
*
* @author Robbert van Dalen
*/
public class Shell extends Thread implements Function
{
static final String COPYRIGHT = "Copyright 2005 Robbert van Dalen."
+ "At your option, you may copy, distribute, or make derivative works under "
+ "the terms of The Artistic License. This License may be found at "
+ "http://www.opensource.org/licenses/artistic-license.php. "
+ "THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.";
private final static Logger logger = Logger.getLogger("org.syrup.functions.Shell");
/*
* The shell process.
*/
private java.lang.Process p = null;
/**
*/
public Result execute(Context context)
{
boolean consume_command = true;
try
{
if (Utils.isFull(context.in_1_link()))
{
String command = new String(context.in_1_link().content().bytes());
try
{
// The following clutch is needed with JVM 1.4.2 and lower.
// For JVM 1.5 and higher it can be implemented much better.
String[] i = null;
String os = System.getProperty("os.name");
if (os.toLowerCase().indexOf("windows") >= 0)
{
String[] k =
{
"cmd.exe", "/C", command
};
i = k;
}
else
{
String[] k =
{
"sh", "-c", command
};
i = k;
}
/*
* Start the shell. Add shutdown hook to make sure that
* after JVM shutdown, the shell is also terminated.
*/
p = Runtime.getRuntime().exec(i);
Runtime.getRuntime().addShutdownHook(this);
}
catch (Error e)
{
if (p != null)
{
p.destroy();
}
throw new Exception("Shell died exceptionally");
}
ByteArrayInputStream input = new ByteArrayInputStream(new byte[0]);
;
/*
* Create the shell's stdin buffer if the first input != null.
*/
if (Utils.isFull(context.in_2_link()))
{
consume_command = false;
input = new ByteArrayInputStream(context.in_2_link().content().bytes());
}
/*
* Create the shell's stdout and stderr buffers.
*/
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream error = new ByteArrayOutputStream();
/*
* Connect each stream (stdin, stdout, stderr) to the buffers.
*/
Writer w1 = new Writer(input, p.getOutputStream());
Writer w2 = new Writer(p.getInputStream(), output);
Writer w3 = new Writer(p.getErrorStream(), error);
/*
* Wait for all Writers to terminate.
*/
p.waitFor();
w1.join();
w2.join();
w3.join();
if (error.size() > 0
&& output.size() == 0)
{
/*
* This problably indicates an error.
*/
return new ResultImpl(context, consume_command, true, null, new DataImpl(error.toByteArray()));
}
else
{
if (error.size() > 0)
{
/*
* This indicates stdout + stderr Not clear how to
* handle this if it does indicate a real problem.
*/
return new ResultImpl(context, consume_command, true, new DataImpl(output.toByteArray()), new DataImpl(error.toByteArray()));
}
else
{
/*
* This indicates stdout without stderr.
*/
if (output.size() > 0)
{
return new ResultImpl(context, consume_command, true, new DataImpl(output.toByteArray()), null);
}
else
{
return new ResultImpl(context, consume_command, true, null, null);
}
}
}
}
else
{
throw new Exception("first input is mandatory");
}
}
catch (Throwable e1)
{
return new ResultImpl(context, true, true, null, org.syrup.helpers.Utils.manageError(logger, e1, "Shell error"));
}
finally
{
// Remove shutdown hook after execution.
Runtime.getRuntime().removeShutdownHook(this);
}
}
/**
* Executed when the JVM is in shutdown.
*/
public void run()
{
// Destroy shell.
p.destroy();
}
}