1 package com.sun.electric.tool.io;
8 * This class provides the same functionality as Runtime.exec(), but
9 * with extra safeguards and utilities. Includes the ability to
10 * execute a command on a remote machine via ssh, optionally rsync'ing
11 * the working directory to the remote machine before execution and
14 * This class should not depend on other Electric classes.
16 * @author megacz (heavily influenced by gainsley's ExecProcess)
18 public class ExecProcess {
20 /** an OutputStream that discards anything written to it */
21 public static final OutputStream devNull = new OutputStream() {
22 public void write(int b) { }
23 public void write(byte[] b, int ofs, int len) { }
26 /** an InputStream that always returns EOF */
27 public static final InputStream eofInputStream = new InputStream() {
28 public int read() { return -1; }
29 public int read(byte[] buf, int ofs, int len) { return -1; }
30 public long skip(long ofs) { return 0; }
31 public int available() { return 0; }
35 * @param command the command to run (separated into argv[])
36 * @param workingDirectory the working directory on the LOCAL machine
38 public ExecProcess(String[] command, File workingDirectory) {
39 this.command = command;
41 // Using java.io.tmpdir as the default working directory leads
42 // to far more predictable behavior than simply using the
43 // JVM's working directory. Electric already has a lot of
44 // bugs and quirks that result from doing that -- let's not
46 if (workingDirectory==null)
47 workingDirectory = new File(System.getProperty("java.io.tmpdir"));
49 this.workingDirectory = workingDirectory;
53 * @param host the hostname to run on
54 * @param user the username on the remote machine (or null to use
55 * whatever default ssh chooses)
56 * @param remoteWorkingDirectory the directory to work in on the remote machine
57 * @param syncBefore if true then "rsync --delete
58 * workingDirectory host:remoteWorkingDirectory" before
60 * @param syncAfter if true and the command terminates with exit
61 * code zero, then "rsync --delete
62 * host:remoteWorkingDirectory workingDirectory" after
65 public synchronized void setRemote(String host, String user,
66 File remoteWorkingDirectory,
67 boolean syncBefore, boolean syncAfter) {
68 if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.setRemote() after ExecProcess.start()");
69 throw new RuntimeException("not implemented");
72 /** undoes setRemote() */
73 public synchronized void setLocal() { }
75 public synchronized void redirectStdin(InputStream in) {
76 if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStdin() after ExecProcess.start()");
77 this.redirectStdin = in;
80 public synchronized void redirectStdout(OutputStream os) {
81 if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStdout() after ExecProcess.start()");
82 this.redirectStdout = os;
85 public synchronized void redirectStderr(OutputStream os) {
86 if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.redirectStderr() after ExecProcess.start()");
87 this.redirectStderr = os;
90 public synchronized void start() throws IOException {
91 if (proc!=null) throw new RuntimeException("you cannot invoke ExecProcess.start() twice");
92 proc = Runtime.getRuntime().exec(command, null, workingDirectory);
93 if (redirectStdin != null) new StreamCopier(redirectStdin, proc.getOutputStream()).start();
94 if (redirectStdout != null) new StreamCopier(proc.getInputStream(), redirectStdout).start();
95 if (redirectStderr != null) new StreamCopier(proc.getErrorStream(), redirectStderr).start();
98 public synchronized void destroy() throws IOException {
99 if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first");
103 public int waitFor() throws IOException {
104 if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first");
106 return proc.waitFor();
107 } catch (InterruptedException ie) {
108 throw new RuntimeException(ie);
112 public synchronized OutputStream getStdin() {
113 if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first");
114 if (redirectStdin!=null) throw new RuntimeException("you cannot invoke getStdin() after redirectStdin()");
115 return proc.getOutputStream();
118 public synchronized InputStream getStdout() {
119 if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first");
120 if (redirectStdout!=null) throw new RuntimeException("you cannot invoke getStdout() after redirectStdout()");
121 return proc.getInputStream();
124 public synchronized InputStream getStderr() {
125 if (proc==null) throw new RuntimeException("you must invoke ExecProcess.start() first");
126 if (redirectStderr!=null) throw new RuntimeException("you cannot invoke getStderr() after redirectStderr()");
127 return proc.getErrorStream();
130 private Process proc;
131 private InputStream redirectStdin;
132 private OutputStream redirectStdout;
133 private OutputStream redirectStderr;
134 private String[] command;
135 private File workingDirectory;
138 * Copies from an InputStream to an OutputStream; used to implement redirectXXX().
140 private static class StreamCopier extends Thread {
141 private final byte[] buf = new byte[16 * 1024];
142 private final InputStream is;
143 private final OutputStream os;
144 public StreamCopier(InputStream is, OutputStream os) {
152 int numread = is.read(buf, 0, buf.length);
153 if (numread==-1) break;
154 os.write(buf, 0, numread);
156 } catch (Exception e) { throw new RuntimeException(e); }