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