1 package auxtestlib;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Timer;
10 import java.util.TimerTask;
11
12
13
14
15 public class CommandRunner {
16
17
18
19 public static class CommandOutput {
20
21
22
23 public String output;
24
25
26
27
28 public String error;
29
30
31
32
33 public byte outputBytes[];
34
35
36
37
38 public byte errorBytes[];
39
40
41
42
43 public int exitCode;
44
45
46
47
48 public boolean timedOut;
49 }
50
51
52
53
54 public CommandRunner() {
55
56
57
58 }
59
60
61
62
63
64
65
66
67
68
69
70 public ProcessInterface run_command_async(String[] cmds, File directory,
71 int limit) throws IOException {
72 if (cmds == null) {
73 throw new IllegalArgumentException("cmds == null");
74 }
75
76 if (directory == null) {
77 throw new IllegalArgumentException("directory == null");
78 }
79
80 if (limit <= 0) {
81 throw new IllegalArgumentException("limit <= 0");
82 }
83
84 ProcessBuilder pb = new ProcessBuilder(cmds);
85 pb.directory(directory);
86 Process p = pb.start();
87 return ProcessInterface.makeProcessInterface(p, limit);
88 }
89
90
91
92
93
94
95
96
97
98 @Deprecated
99 public ProcessInterface runCommandAsync(String[] cmds, File directory,
100 int limit) throws IOException {
101 return run_command_async(cmds, directory, limit);
102 }
103
104
105
106
107
108
109
110
111
112
113
114 public CommandOutput run_command(String cmds[], File directory, int limit)
115 throws IOException {
116 ProcessInterface pi = runCommandAsync(cmds, directory, limit);
117
118
119
120
121 while (pi.isRunning()) {
122 try {
123 Thread.sleep(ProcessInterface.PROCESS_POLLING);
124 } catch (InterruptedException e) {
125
126
127
128 }
129 }
130
131 return pi.getOutput();
132 }
133
134
135
136
137
138
139
140
141
142 @Deprecated
143 public CommandOutput runCommand(String cmds[], File directory, int limit)
144 throws IOException {
145 return run_command(cmds, directory, limit);
146 }
147
148
149
150
151
152 static class Capturer extends Thread {
153
154
155
156 private final InputStream m_input_stream;
157
158
159
160
161 private final StringBuffer m_result;
162
163
164
165
166 private final ByteArrayOutputStream m_result_bytes;
167
168
169
170
171
172
173 Capturer(InputStream is) {
174 assert is != null;
175
176 m_input_stream = is;
177 m_result = new StringBuffer();
178 m_result_bytes = new ByteArrayOutputStream();
179 start();
180 }
181
182 @Override
183 public void run() {
184 int read;
185 try {
186 while ((read = m_input_stream.read()) != -1) {
187 synchronized (this) {
188 m_result_bytes.write(read);
189 m_result.append((char) read);
190 }
191 }
192 } catch (IOException e) {
193
194
195
196 }
197 }
198
199
200
201
202
203
204 synchronized String text() {
205 return m_result.toString();
206 }
207
208
209
210
211
212
213 synchronized byte[] bytes() {
214 return m_result_bytes.toByteArray();
215 }
216 }
217
218
219
220
221 public static class ProcessInterface {
222
223
224
225
226 private static final int PROCESS_POLLING = 200;
227
228
229
230
231 private final Process m_process;
232
233
234
235
236 private boolean m_running;
237
238
239
240
241 private int m_exit_code;
242
243
244
245
246 private final Capturer m_out;
247
248
249
250
251 private final Capturer m_err;
252
253
254
255
256 private boolean m_timed_out;
257
258
259
260
261 private final List<ProcessInterfaceListener> m_listeners;
262
263
264
265
266
267
268
269
270
271
272 private static ProcessInterface makeProcessInterface(Process process,
273 int limit) {
274 return new ProcessInterface(process, limit);
275 }
276
277
278
279
280
281
282
283
284 private ProcessInterface(Process process, int limit) {
285 assert process != null;
286
287 this.m_process = process;
288 m_running = true;
289 m_exit_code = 0;
290 m_out = new Capturer(process.getInputStream());
291 m_err = new Capturer(process.getErrorStream());
292 m_listeners = new ArrayList<>();
293 m_timed_out = false;
294
295 Timer timer = new Timer();
296 timer.schedule(new TimerTask() {
297 @Override
298 public void run() {
299 synchronized (ProcessInterface.this) {
300 update_state();
301 if (!m_running) {
302 cancel();
303 }
304 }
305 }
306 }, PROCESS_POLLING, PROCESS_POLLING);
307
308 final TimerTask timeout_task = new TimerTask() {
309 @Override
310 public void run() {
311 synchronized (this) {
312 if (killProcess()) {
313 m_timed_out = true;
314 }
315 }
316 }
317 };
318
319 addProcessInterfaceListener(new ProcessInterfaceListener() {
320 @Override
321 public void processFinished(ProcessInterface process) {
322 timeout_task.cancel();
323 }
324 });
325
326 timer.schedule(timeout_task, limit * 1000);
327 }
328
329
330
331
332
333 public synchronized void add_process_interface_listener(
334 ProcessInterfaceListener listener) {
335 if (listener == null) {
336 throw new IllegalArgumentException("listener == null");
337 }
338
339 m_listeners.add(listener);
340 }
341
342
343
344
345
346
347 @Deprecated
348 public synchronized void addProcessInterfaceListener(
349 ProcessInterfaceListener listener) {
350 add_process_interface_listener(listener);
351 }
352
353
354
355
356
357 public synchronized void remove_process_interface_listener(
358 ProcessInterfaceListener listener) {
359 if (listener == null) {
360 throw new IllegalArgumentException("listener == null");
361 }
362
363 m_listeners.remove(listener);
364 }
365
366
367
368
369
370
371 @Deprecated
372 public synchronized void removeProcessInterfaceListener(
373 ProcessInterfaceListener listener) {
374 remove_process_interface_listener(listener);
375 }
376
377
378
379
380
381
382
383 private synchronized void update_state() {
384 if (!m_running) {
385 return;
386 }
387
388 try {
389 m_exit_code = m_process.exitValue();
390 m_running = false;
391 for (ProcessInterfaceListener l : new ArrayList<>(
392 m_listeners)) {
393 l.processFinished(this);
394 }
395 } catch (IllegalThreadStateException e) {
396
397
398
399 }
400 }
401
402
403
404
405
406
407
408 public synchronized boolean killProcess() {
409 if (!m_running) {
410 return false;
411 }
412
413 m_process.destroy();
414 while (true) {
415 try {
416 m_process.exitValue();
417 break;
418 } catch (IllegalThreadStateException e) {
419
420
421
422 }
423 }
424
425 update_state();
426 assert !m_running;
427 return true;
428 }
429
430
431
432
433
434
435 public synchronized boolean isRunning() {
436 return m_running;
437 }
438
439
440
441
442
443
444
445
446
447 public synchronized CommandOutput getOutput() {
448 if (m_running) {
449 throw new IllegalStateException("Process still running.");
450 }
451
452 CommandOutput co = new CommandOutput();
453
454 co.exitCode = m_exit_code;
455 co.output = m_out.text();
456 co.error = m_err.text();
457 co.outputBytes = m_out.bytes();
458 co.errorBytes = m_err.bytes();
459 co.timedOut = m_timed_out;
460 return co;
461 }
462
463
464
465
466
467
468 public synchronized String getOutputText() {
469 return m_out.text();
470 }
471
472
473
474
475
476
477 public synchronized String getErrorText() {
478 return m_err.text();
479 }
480 }
481
482
483
484
485 interface ProcessInterfaceListener {
486
487
488
489
490
491 void processFinished(ProcessInterface process);
492 }
493 }