package backtype.storm.utils; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.codehaus.plexus.util.cli.shell.CmdShell; import backtype.storm.task.TopologyContext; public class ShellProcess { public static Logger LOG = Logger.getLogger(ShellProcess.class); private DataOutputStream processIn; private BufferedReader processOut; private InputStream processErrorStream; private Process _subprocess; private String[] command; public ShellProcess(String[] command) { this.command = command; } public Number launch(Map conf, TopologyContext context) throws IOException { ProcessBuilder builder = new ProcessBuilder(command); builder.directory(new File(context.getCodeDir())); _subprocess = builder.start(); processIn = new DataOutputStream(_subprocess.getOutputStream()); processOut = new BufferedReader(new InputStreamReader( _subprocess.getInputStream())); processErrorStream = _subprocess.getErrorStream(); Map setupInfo = new HashMap(); setupInfo.put("pidDir", context.getPIDDir()); setupInfo.put("conf", conf); setupInfo.put("context", context); writeMessage(setupInfo); StringBuffer sb = new StringBuffer(); sb.append("Begin to run command:"); for (String cmd: command) { sb.append(cmd); } sb.append(setupInfo); LOG.info(sb.toString()); return (Number) readMessage().get("pid"); } public void destroy() { _subprocess.destroy(); } public void writeMessage(Object msg) throws IOException { writeString(Utils.to_json(msg)); } private void writeString(String str) throws IOException { byte[] strBytes = str.getBytes("UTF-8"); processIn.write(strBytes, 0, strBytes.length); processIn.writeBytes("\nend\n"); processIn.flush(); } public Map readMessage() throws IOException { String string = readString(); Map msg = (Map)Utils.from_json(string); if (msg != null) { return msg; } else { throw new IOException("unable to parse: " + string); } } public String getErrorsString() { if (processErrorStream != null) { try { return IOUtils.toString(processErrorStream); } catch (IOException e) { return "(Unable to capture error stream)"; } } else { return ""; } } public void drainErrorStream() { try { while (processErrorStream.available() > 0) { int bufferSize = processErrorStream.available(); byte[] errorReadingBuffer = new byte[bufferSize]; processErrorStream.read(errorReadingBuffer, 0, bufferSize); LOG.info("Got error from shell process: " + new String(errorReadingBuffer)); } } catch (Exception e) { } } private String readString() throws IOException { StringBuilder line = new StringBuilder(); // synchronized (processOut) { while (true) { String subline = processOut.readLine(); if (subline == null) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("Pipe to subprocess seems to be broken!"); if (line.length() == 0) { errorMessage.append(" No output read.\n"); } else { errorMessage.append(" Currently read output: " + line.toString() + "\n"); } errorMessage.append("Shell Process Exception:\n"); errorMessage.append(getErrorsString() + "\n"); throw new RuntimeException(errorMessage.toString()); } if (subline.equals("end")) { break; } if (line.length() != 0) { line.append("\n"); } line.append(subline); } // } return line.toString(); } }