1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.exec;
18
19 import java.io.Closeable;
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.Map;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.ThreadFactory;
25 import java.util.function.Supplier;
26
27 import org.apache.commons.exec.launcher.CommandLauncher;
28 import org.apache.commons.exec.launcher.CommandLauncherFactory;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class DefaultExecutor implements Executor {
50
51
52
53
54
55
56
57 public static class Builder<T extends Builder<T>> implements Supplier<DefaultExecutor> {
58
59 private ThreadFactory threadFactory;
60 private ExecuteStreamHandler executeStreamHandler;
61 private File workingDirectory;
62
63 @SuppressWarnings("unchecked")
64 T asThis() {
65 return (T) this;
66 }
67
68
69
70
71
72
73 @Override
74 public DefaultExecutor get() {
75 return new DefaultExecutor(threadFactory, executeStreamHandler, workingDirectory);
76 }
77
78 ExecuteStreamHandler getExecuteStreamHandler() {
79 return executeStreamHandler;
80 }
81
82 ThreadFactory getThreadFactory() {
83 return threadFactory;
84 }
85
86 File getWorkingDirectory() {
87 return workingDirectory;
88 }
89
90
91
92
93
94
95
96 public T setExecuteStreamHandler(final ExecuteStreamHandler executeStreamHandler) {
97 this.executeStreamHandler = executeStreamHandler;
98 return asThis();
99 }
100
101
102
103
104
105
106
107 public T setThreadFactory(final ThreadFactory threadFactory) {
108 this.threadFactory = threadFactory;
109 return asThis();
110 }
111
112
113
114
115
116
117
118 public T setWorkingDirectory(final File workingDirectory) {
119 this.workingDirectory = workingDirectory;
120 return asThis();
121 }
122
123 }
124
125
126
127
128
129
130
131 public static Builder<?> builder() {
132 return new Builder<>();
133 }
134
135
136 private ExecuteStreamHandler executeStreamHandler;
137
138
139 private File workingDirectory;
140
141
142 private ExecuteWatchdog watchdog;
143
144
145 private int[] exitValues;
146
147
148 private final CommandLauncher launcher;
149
150
151 private ProcessDestroyer processDestroyer;
152
153
154 private Thread executorThread;
155
156
157 private IOException exceptionCaught;
158
159
160
161
162 private final ThreadFactory threadFactory;
163
164
165
166
167
168
169
170
171
172 @Deprecated
173 public DefaultExecutor() {
174 this(Executors.defaultThreadFactory(), new PumpStreamHandler(), new File("."));
175 }
176
177 DefaultExecutor(final ThreadFactory threadFactory, final ExecuteStreamHandler executeStreamHandler, final File workingDirectory) {
178 this.threadFactory = threadFactory != null ? threadFactory : Executors.defaultThreadFactory();
179 this.executeStreamHandler = executeStreamHandler != null ? executeStreamHandler : new PumpStreamHandler();
180 this.workingDirectory = workingDirectory != null ? workingDirectory : new File(".");
181 this.launcher = CommandLauncherFactory.createVMLauncher();
182 this.exitValues = new int[0];
183 }
184
185 private void checkWorkingDirectory() throws IOException {
186 checkWorkingDirectory(workingDirectory);
187 }
188
189 private void checkWorkingDirectory(final File directory) throws IOException {
190 if (directory != null && !directory.exists()) {
191 throw new IOException(directory + " doesn't exist.");
192 }
193 }
194
195
196
197
198
199
200 private void closeCatch(final Closeable closeable) {
201 try {
202 closeable.close();
203 } catch (final IOException e) {
204 setExceptionCaught(e);
205 }
206 }
207
208
209
210
211
212
213 @SuppressWarnings("resource")
214 private void closeProcessStreams(final Process process) {
215 closeCatch(process.getInputStream());
216 closeCatch(process.getOutputStream());
217 closeCatch(process.getErrorStream());
218 }
219
220
221
222
223
224
225
226
227 protected Thread createThread(final Runnable runnable, final String name) {
228 return ThreadUtil.newThread(threadFactory, runnable, name, false);
229 }
230
231
232
233
234 @Override
235 public int execute(final CommandLine command) throws ExecuteException, IOException {
236 return execute(command, (Map<String, String>) null);
237 }
238
239
240
241
242 @Override
243 public void execute(final CommandLine command, final ExecuteResultHandler handler) throws ExecuteException, IOException {
244 execute(command, null, handler);
245 }
246
247
248
249
250 @Override
251 public int execute(final CommandLine command, final Map<String, String> environment) throws ExecuteException, IOException {
252 checkWorkingDirectory();
253 return executeInternal(command, environment, workingDirectory, executeStreamHandler);
254 }
255
256
257
258
259 @Override
260 public void execute(final CommandLine command, final Map<String, String> environment, final ExecuteResultHandler handler)
261 throws ExecuteException, IOException {
262 checkWorkingDirectory();
263 if (watchdog != null) {
264 watchdog.setProcessNotStarted();
265 }
266 executorThread = createThread(() -> {
267 int exitValue = Executor.INVALID_EXITVALUE;
268 try {
269 exitValue = executeInternal(command, environment, workingDirectory, executeStreamHandler);
270 handler.onProcessComplete(exitValue);
271 } catch (final ExecuteException e) {
272 handler.onProcessFailed(e);
273 } catch (final Exception e) {
274 handler.onProcessFailed(new ExecuteException("Execution failed", exitValue, e));
275 }
276 }, "CommonsExecDefaultExecutor");
277 getExecutorThread().start();
278 }
279
280
281
282
283
284
285
286
287
288
289
290 private int executeInternal(final CommandLine command, final Map<String, String> environment, final File workingDirectory,
291 final ExecuteStreamHandler streams) throws IOException {
292 final Process process;
293 exceptionCaught = null;
294 try {
295 process = launch(command, environment, workingDirectory);
296 } catch (final IOException e) {
297 if (watchdog != null) {
298 watchdog.failedToStart(e);
299 }
300 throw e;
301 }
302 try {
303 setStreams(streams, process);
304 } catch (final IOException e) {
305 process.destroy();
306 if (watchdog != null) {
307 watchdog.failedToStart(e);
308 }
309 throw e;
310 }
311 streams.start();
312 try {
313
314 if (getProcessDestroyer() != null) {
315 getProcessDestroyer().add(process);
316 }
317
318 if (watchdog != null) {
319 watchdog.start(process);
320 }
321 int exitValue = Executor.INVALID_EXITVALUE;
322 try {
323 exitValue = process.waitFor();
324 } catch (final InterruptedException e) {
325 process.destroy();
326 } finally {
327
328
329
330
331 Thread.interrupted();
332 }
333 if (watchdog != null) {
334 watchdog.stop();
335 }
336 try {
337 streams.stop();
338 } catch (final IOException e) {
339 setExceptionCaught(e);
340 }
341 closeProcessStreams(process);
342 if (getExceptionCaught() != null) {
343 throw getExceptionCaught();
344 }
345 if (watchdog != null) {
346 try {
347 watchdog.checkException();
348 } catch (final IOException e) {
349 throw e;
350 } catch (final Exception e) {
351 throw new IOException(e);
352 }
353 }
354 if (isFailure(exitValue)) {
355 throw new ExecuteException("Process exited with an error: " + exitValue, exitValue);
356 }
357 return exitValue;
358 } finally {
359
360 if (getProcessDestroyer() != null) {
361 getProcessDestroyer().remove(process);
362 }
363 }
364 }
365
366
367
368
369
370
371 private IOException getExceptionCaught() {
372 return exceptionCaught;
373 }
374
375
376
377
378
379
380 protected Thread getExecutorThread() {
381 return executorThread;
382 }
383
384
385
386
387 @Override
388 public ProcessDestroyer getProcessDestroyer() {
389 return processDestroyer;
390 }
391
392
393
394
395 @Override
396 public ExecuteStreamHandler getStreamHandler() {
397 return executeStreamHandler;
398 }
399
400
401
402
403
404
405 ThreadFactory getThreadFactory() {
406 return threadFactory;
407 }
408
409
410
411
412 @Override
413 public ExecuteWatchdog getWatchdog() {
414 return watchdog;
415 }
416
417
418
419
420 @Override
421 public File getWorkingDirectory() {
422 return workingDirectory;
423 }
424
425
426 @Override
427 public boolean isFailure(final int exitValue) {
428 if (exitValues == null) {
429 return false;
430 }
431 if (exitValues.length == 0) {
432 return launcher.isFailure(exitValue);
433 }
434 for (final int exitValue2 : exitValues) {
435 if (exitValue2 == exitValue) {
436 return false;
437 }
438 }
439 return true;
440 }
441
442
443
444
445
446
447
448
449
450
451 protected Process launch(final CommandLine command, final Map<String, String> env, final File workingDirectory) throws IOException {
452 if (launcher == null) {
453 throw new IllegalStateException("CommandLauncher can not be null");
454 }
455 checkWorkingDirectory(workingDirectory);
456 return launcher.exec(command, env, workingDirectory);
457 }
458
459
460
461
462
463
464 private void setExceptionCaught(final IOException e) {
465 if (exceptionCaught == null) {
466 exceptionCaught = e;
467 }
468 }
469
470
471 @Override
472 public void setExitValue(final int value) {
473 setExitValues(new int[] { value });
474 }
475
476
477 @Override
478 public void setExitValues(final int[] values) {
479 exitValues = values == null ? null : (int[]) values.clone();
480 }
481
482
483
484
485 @Override
486 public void setProcessDestroyer(final ProcessDestroyer processDestroyer) {
487 this.processDestroyer = processDestroyer;
488 }
489
490
491
492
493 @Override
494 public void setStreamHandler(final ExecuteStreamHandler streamHandler) {
495 this.executeStreamHandler = streamHandler;
496 }
497
498 @SuppressWarnings("resource")
499 private void setStreams(final ExecuteStreamHandler streams, final Process process) throws IOException {
500 streams.setProcessInputStream(process.getOutputStream());
501 streams.setProcessOutputStream(process.getInputStream());
502 streams.setProcessErrorStream(process.getErrorStream());
503 }
504
505
506
507
508 @Override
509 public void setWatchdog(final ExecuteWatchdog watchdog) {
510 this.watchdog = watchdog;
511 }
512
513
514
515
516
517
518
519 @Deprecated
520 @Override
521 public void setWorkingDirectory(final File workingDirectory) {
522 this.workingDirectory = workingDirectory;
523 }
524
525 }