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.Utils;
import org.syrup.helpers.Writer;
import org.syrup.services.BlobClient;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import javax.naming.InitialContext;
/**
* Executes a shell command, provided by the first input, using a BlobServer.
* The second input provides the stdin. If the second input is not given the
* first (command) input is consumed. The first output maps to stdout and the
* second output to stderr. Only the second input and first output are put
* through a BlobServer. This makes the processing of big inputs and outputs
* possible without overloading the backing store implementation of Syrup.
*
* @author Robbert van Dalen
*/
public class BShell 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.BShell");
private java.lang.Process p = null;
/**
*/
public Result execute(Context context)
{
boolean consume_command = true;
// [TODO: Refactor so that Shell and BShell share the duplicate code]
// See Shell for the implementation comments.
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("Process died exceptionally");
}
ByteArrayInputStream input = new ByteArrayInputStream(new byte[0]);
if (Utils.isFull(context.in_2_link()))
{
consume_command = false;
input = new ByteArrayInputStream(context.in_2_link().content().bytes());
}
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream error = new ByteArrayOutputStream();
BlobClientHandler w1 = new BlobClientHandler("get", input, p.getOutputStream());
BlobClientHandler w2 = new BlobClientHandler("post", p.getInputStream(), output);
Writer w3 = new Writer(p.getErrorStream(), error);
p.waitFor();
w1.join();
w2.join();
w3.join();
if (w1.getException() != null)
{
throw w1.getException();
}
if (w2.getException() != null)
{
throw w2.getException();
}
if (w3.getException() != null)
{
throw w3.getException();
}
if (error.size() > 0
&& output.size() == 0)
{
return new ResultImpl(context, consume_command, true, null, new DataImpl(error.toByteArray()));
}
else
{
if (error.size() > 0)
{
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, "BShell error"));
}
finally
{
Runtime.getRuntime().removeShutdownHook(this);
}
}
/**
* Main processing method for the BShell object
*/
public void run()
{
p.destroy();
}
/**
* Description of the Class
*
* @author Robbert van Dalen
*/
private class BlobClientHandler extends Thread
{
public final String command;
public final InputStream input;
public final OutputStream output;
public final BlobClient client;
private Exception exception;
/**
*/
public BlobClientHandler(String c, InputStream i, OutputStream o)
throws Exception
{
command = c;
client = (BlobClient) (new InitialContext().lookup("syrupBlobClient"));
input = i;
output = o;
this.start();
}
/**
* Main processing method for the BlobClientHandler object
*/
public void run()
{
try
{
if (command.equals("get"))
{
client.get(input, output);
}
else if (command.equals("post"))
{
client.post(input, output);
}
}
catch (Exception e)
{
exception = e;
}
finally
{
try
{
input.close();
output.close();
}
catch (Exception e)
{
exception = e;
}
}
}
/**
* Gets the exception attribute of the BlobClientHandler object
*
* @return The exception value
*/
public Exception getException()
{
return exception;
}
}
}