1 package com.randomnoun.common;
2
3
4
5
6
7 import java.io.ByteArrayOutputStream;
8 import java.io.File;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.util.Map;
13
14
15
16
17
18 public class ProcessUtil {
19
20
21 private int maxOutputChars = 8000;
22
23 public static int NO_MAX_OUTPUT_CHARS = 0;
24
25 public void setMaxOutputChars(int maxOutputChars) {
26 this.maxOutputChars = maxOutputChars;
27 }
28
29
30
31
32 public class ProcessException extends Exception {
33
34
35 private static final long serialVersionUID = -6301630237335589674L;
36
37 private String command;
38 private int exitCode;
39 private String stdout;
40 private String stderr;
41 private String exitCause;
42
43
44
45
46
47
48
49
50
51 public ProcessException(String command, String exitCause, int exitCode, String stdout, String stderr) {
52 super("Error executing '" + command + "'" + ", cause='" + exitCause + "'");
53
54 this.command = command;
55 this.exitCode = exitCode;
56 this.exitCause = exitCause;
57 this.stdout = stdout;
58 this.stderr = stderr;
59 }
60 public String getStdout() { return stdout; }
61 public String getStderr() { return stderr; }
62 public String getCommand() { return command; }
63 public int getExitCode() { return exitCode; }
64 public String getExitCause() { return exitCause; }
65 public String getMessage() {
66
67 if (maxOutputChars>0) { stdout = Text.getDisplayString("stdout", stdout, 8000); }
68 if (maxOutputChars>0) { stderr = Text.getDisplayString("stderr", stderr, 8000); }
69 return super.getMessage() +
70 "; " +
71 (stdout==null ? "" : ", stdout='" + stdout + "'\n") +
72 (stderr==null ? "" : ", stderr='" + stderr + "'\n") +
73 "exitCode=" + exitCode;
74 }
75 }
76
77
78
79 public String exec(String[] command) throws ProcessException {
80 return exec(command, -1, null, null, null);
81 }
82
83 public String exec(String[] command, Map<String, String> env) throws ProcessException {
84 return exec(command, -1, null, env, null);
85 }
86
87 public String exec(String[] command, long timeout, InputStream stdin, Map<String, String> envMap, File dir) throws ProcessException {
88 Process process;
89 try {
90 ProcessBuilder pb = new ProcessBuilder(command);
91 if (dir != null) {
92 pb.directory(dir);
93 }
94 if (envMap != null) {
95 pb.environment().clear();
96 pb.environment().putAll(envMap);
97 }
98 process = pb.start();
99 } catch (IOException ioe) {
100 throw (ProcessException) new ProcessException(Text.join(command, " "), "IOException", 0, "", "").initCause(ioe);
101 }
102 InputStream stdout = process.getInputStream();
103 InputStream stderr = process.getErrorStream();
104 OutputStream processStdin = process.getOutputStream();
105 ByteArrayOutputStream stdoutByteArrayStream = new ByteArrayOutputStream();
106 ByteArrayOutputStream stderrByteArrayStream = new ByteArrayOutputStream();
107 OutputStream stdoutStream = stdoutByteArrayStream;
108 OutputStream stderrStream = stderrByteArrayStream;
109
110
111
112
113 Thread copyStdoutThread = StreamUtil.copyThread(stdout, stdoutStream, 1024);
114 Thread copyStderrThread = StreamUtil.copyThread(stderr, stderrStream, 1024);
115 Thread copyStdinThread = null;
116 if (stdin!=null) {
117 copyStdinThread = StreamUtil.copyAndCloseThread(stdin, processStdin, 1024);
118 copyStdinThread.start();
119 }
120
121 copyStdoutThread.start();
122 copyStderrThread.start();
123 int exitCode = -1;
124 boolean throwException = false;
125 long interval = 100;
126 String cause = "";
127
128 try {
129
130 long timeWaiting = 0;
131 boolean processFinished = false;
132 while ((timeout == -1 || timeWaiting < timeout) && !processFinished) {
133 processFinished = true;
134 Thread.sleep(interval);
135 try {
136 exitCode = process.exitValue();
137 } catch (IllegalThreadStateException e) {
138
139 processFinished = false;
140 }
141 timeWaiting += interval;
142 }
143
144 if (processFinished) {
145
146 copyStdoutThread.join();
147 copyStderrThread.join();
148 if (copyStdinThread != null) {
149 copyStdinThread.interrupt();
150 }
151 if (exitCode != 0) {
152 cause = "non-0 exitCode";
153 throwException = true;
154 }
155 } else {
156 cause = "timeout";
157 throwException = true;
158 process.destroy();
159 if (copyStdinThread != null) {
160 copyStdinThread.interrupt();
161 }
162 copyStderrThread.interrupt();
163 copyStdoutThread.interrupt();
164 }
165
166 } catch (InterruptedException ie) {
167 cause = "InterruptedException";
168 throwException = true;
169 process.destroy();
170 if (copyStdinThread!=null) {
171 copyStdinThread.interrupt();
172 }
173 copyStderrThread.interrupt();
174 copyStdoutThread.interrupt();
175 }
176
177 if (throwException) {
178 throw new ProcessException(
179 Text.join(command, " "), cause, exitCode,
180 stdoutByteArrayStream.toString(), stderrByteArrayStream.toString());
181 }
182 return stdoutByteArrayStream.toString();
183 }
184
185
186
187
188 }