From c2de5bf5b191b37b2f8ab260a12d370db38ee4fc Mon Sep 17 00:00:00 2001 From: Adam Megacz Date: Sun, 10 Jan 2010 19:02:38 -0800 Subject: [PATCH] add ExecProcess from Electric --- src/com/sun/electric/tool/io/ExecProcess.java | 160 +++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/com/sun/electric/tool/io/ExecProcess.java diff --git a/src/com/sun/electric/tool/io/ExecProcess.java b/src/com/sun/electric/tool/io/ExecProcess.java new file mode 100644 index 0000000..18cd885 --- /dev/null +++ b/src/com/sun/electric/tool/io/ExecProcess.java @@ -0,0 +1,160 @@ +package com.sun.electric.tool.io; + +import java.net.*; +import java.util.*; +import java.io.*; + +/** + * This class provides the same functionality as Runtime.exec(), but + * with extra safeguards and utilities. Includes the ability to + * execute a command on a remote machine via ssh, optionally rsync'ing + * the working directory to the remote machine before execution and + * back afterwards. + * + * This class should not depend on other Electric classes. + * + * @author megacz (heavily influenced by gainsley's ExecProcess) + */ +public class ExecProcess { + + /** an OutputStream that discards anything written to it */ + public static final OutputStream devNull = new OutputStream() { + public void write(int b) { } + public void write(byte[] b, int ofs, int len) { } + }; + + /** an InputStream that always returns EOF */ + public static final InputStream eofInputStream = new InputStream() { + public int read() { return -1; } + public int read(byte[] buf, int ofs, int len) { return -1; } + public long skip(long ofs) { return 0; } + public int available() { return 0; } + }; + + /** + * @param command the command to run (separated into argv[]) + * @param workingDirectory the working directory on the LOCAL machine + */ + public ExecProcess(String[] command, File workingDirectory) { + this.command = command; + + // Using java.io.tmpdir as the default working directory leads + // to far more predictable behavior than simply using the + // JVM's working directory. Electric already has a lot of + // bugs and quirks that result from doing that -- let's not + // add more! + if (workingDirectory==null) + workingDirectory = new File(System.getProperty("java.io.tmpdir")); + + this.workingDirectory = workingDirectory; + } + + /** + * @param host the hostname to run on + * @param user the username on the remote machine (or null to use + * whatever default ssh chooses) + * @param remoteWorkingDirectory the directory to work in on the remote machine + * @param syncBefore if true then "rsync --delete + * workingDirectory host:remoteWorkingDirectory" before + * invoking command. + * @param syncAfter if true and the command terminates with exit + * code zero, then "rsync --delete + * host:remoteWorkingDirectory workingDirectory" after + * invoking command. + */ + public synchronized void setRemote(String host, String user, + File remoteWorkingDirectory, + boolean syncBefore, boolean syncAfter) { + if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.setRemote() after ExecProcess.start()"); + throw new RuntimeException("not implemented"); + } + + /** undoes setRemote() */ + public synchronized void setLocal() { } + + public synchronized void redirectStdin(InputStream in) { + if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStdin() after ExecProcess.start()"); + this.redirectStdin = in; + } + + public synchronized void redirectStdout(OutputStream os) { + if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStdout() after ExecProcess.start()"); + this.redirectStdout = os; + } + + public synchronized void redirectStderr(OutputStream os) { + if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStderr() after ExecProcess.start()"); + this.redirectStderr = os; + } + + public synchronized void start() throws IOException { + if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.start() twice"); + proc = Runtime.getRuntime().exec(command, null, workingDirectory); + if (redirectStdin != null) new StreamCopier(redirectStdin, proc.getOutputStream()).start(); + if (redirectStdout != null) new StreamCopier(proc.getInputStream(), redirectStdout).start(); + if (redirectStderr != null) new StreamCopier(proc.getErrorStream(), redirectStderr).start(); + } + + public synchronized void destroy() throws IOException { + if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first"); + proc.destroy(); + } + + public int waitFor() throws IOException { + if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first"); + try { + return proc.waitFor(); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } + + public synchronized OutputStream getStdin() { + if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first"); + if (redirectStdin!=null) throw new RuntimeException("you cannot invoke getStdin() after redirectStdin()"); + return proc.getOutputStream(); + } + + public synchronized InputStream getStdout() { + if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first"); + if (redirectStdout!=null) throw new RuntimeException("you cannot invoke getStdout() after redirectStdout()"); + return proc.getInputStream(); + } + + public synchronized InputStream getStderr() { + if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first"); + if (redirectStderr!=null) throw new RuntimeException("you cannot invoke getStderr() after redirectStderr()"); + return proc.getErrorStream(); + } + + private Process proc; + private InputStream redirectStdin; + private OutputStream redirectStdout; + private OutputStream redirectStderr; + private String[] command; + private File workingDirectory; + + /** + * Copies from an InputStream to an OutputStream; used to implement redirectXXX(). + */ + private static class StreamCopier extends Thread { + private final byte[] buf = new byte[16 * 1024]; + private final InputStream is; + private final OutputStream os; + public StreamCopier(InputStream is, OutputStream os) { + setDaemon(true); + this.is = is; + this.os = os; + } + public void run() { + try { + while(true) { + int numread = is.read(buf, 0, buf.length); + if (numread==-1) break; + os.write(buf, 0, numread); + } + } catch (Exception e) { throw new RuntimeException(e); } + } + } + +} -- 1.7.10.4