1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.commons.exec;
21
22 import java.time.Duration;
23 import java.time.Instant;
24
25 /**
26 * A default implementation of 'ExecuteResultHandler' used for asynchronous process handling.
27 */
28 public class DefaultExecuteResultHandler implements ExecuteResultHandler {
29
30 /** The interval polling the result. */
31 private static final int SLEEP_TIME_MS = 50;
32
33 /** Keep track if the process is still running. */
34 private volatile boolean hasResult;
35
36 /** The exit value of the finished process. */
37 private volatile int exitValue;
38
39 /** Any offending exception. */
40 private volatile ExecuteException exception;
41
42 /**
43 * Constructs a new instance.
44 */
45 public DefaultExecuteResultHandler() {
46 this.hasResult = false;
47 this.exitValue = Executor.INVALID_EXITVALUE;
48 }
49
50 /**
51 * Gets the {@code exception} causing the process execution to fail.
52 *
53 * @return the exception.
54 * @throws IllegalStateException if the process has not exited yet.
55 */
56 public ExecuteException getException() {
57 if (!hasResult) {
58 throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
59 }
60 return exception;
61 }
62
63 /**
64 * Gets the {@code exitValue} of the process.
65 *
66 * @return the exitValue.
67 * @throws IllegalStateException if the process has not exited yet.
68 */
69 public int getExitValue() {
70 if (!hasResult) {
71 throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
72 }
73 return exitValue;
74 }
75
76 /**
77 * Tests whether the process exited and a result is available, i.e. exitCode or exception?
78 *
79 * @return true whether a result of the execution is available.
80 */
81 public boolean hasResult() {
82 return hasResult;
83 }
84
85 /**
86 * @see org.apache.commons.exec.ExecuteResultHandler#onProcessComplete(int)
87 */
88 @Override
89 public void onProcessComplete(final int exitValue) {
90 this.exitValue = exitValue;
91 this.exception = null;
92 this.hasResult = true;
93 }
94
95 /**
96 * @see org.apache.commons.exec.ExecuteResultHandler#onProcessFailed(org.apache.commons.exec.ExecuteException)
97 */
98 @Override
99 public void onProcessFailed(final ExecuteException e) {
100 this.exitValue = e.getExitValue();
101 this.exception = e;
102 this.hasResult = true;
103 }
104
105 /**
106 * Causes the current thread to wait, if necessary, until the process has terminated. This method returns immediately if the process has already terminated.
107 * If the process has not yet terminated, the calling thread will be blocked until the process exits.
108 *
109 * @throws InterruptedException if the current thread is {@linkplain Thread#interrupt() interrupted} by another thread while it is waiting, then the wait is
110 * ended and an {@link InterruptedException} is thrown.
111 */
112 public void waitFor() throws InterruptedException {
113 while (!hasResult()) {
114 Thread.sleep(SLEEP_TIME_MS);
115 }
116 }
117
118 /**
119 * Causes the current thread to wait, if necessary, until the process has terminated. This method returns immediately if the process has already terminated.
120 * If the process has not yet terminated, the calling thread will be blocked until the process exits.
121 *
122 * @param timeout the maximum time to wait.
123 * @throws InterruptedException if the current thread is {@linkplain Thread#interrupt() interrupted} by another thread while it is waiting, then the wait is
124 * ended and an {@link InterruptedException} is thrown.
125 * @since 1.4.0
126 */
127 public void waitFor(final Duration timeout) throws InterruptedException {
128 final Instant until = Instant.now().plus(timeout);
129 while (!hasResult() && Instant.now().isBefore(until)) {
130 Thread.sleep(SLEEP_TIME_MS);
131 }
132 }
133
134 /**
135 * Causes the current thread to wait, if necessary, until the process has terminated. This method returns immediately if the process has already terminated.
136 * If the process has not yet terminated, the calling thread will be blocked until the process exits.
137 *
138 * @param timeoutMillis the maximum time to wait in milliseconds.
139 * @throws InterruptedException if the current thread is {@linkplain Thread#interrupt() interrupted} by another thread while it is waiting, then the wait is
140 * ended and an {@link InterruptedException} is thrown.
141 * @deprecated Use {@link #waitFor(Duration)}.
142 */
143 @Deprecated
144 public void waitFor(final long timeoutMillis) throws InterruptedException {
145 final long untilMillis = System.currentTimeMillis() + timeoutMillis;
146 while (!hasResult() && System.currentTimeMillis() < untilMillis) {
147 Thread.sleep(SLEEP_TIME_MS);
148 }
149 }
150
151 }