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 * http://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 }