001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.exec; 019 020import java.time.Duration; 021import java.time.Instant; 022 023/** 024 * A default implementation of 'ExecuteResultHandler' used for asynchronous process handling. 025 */ 026public class DefaultExecuteResultHandler implements ExecuteResultHandler { 027 028 /** The interval polling the result. */ 029 private static final int SLEEP_TIME_MS = 50; 030 031 /** Keep track if the process is still running. */ 032 private volatile boolean hasResult; 033 034 /** The exit value of the finished process. */ 035 private volatile int exitValue; 036 037 /** Any offending exception. */ 038 private volatile ExecuteException exception; 039 040 /** 041 * Constructs a new instance. 042 */ 043 public DefaultExecuteResultHandler() { 044 this.hasResult = false; 045 this.exitValue = Executor.INVALID_EXITVALUE; 046 } 047 048 /** 049 * Gets the {@code exception} causing the process execution to fail. 050 * 051 * @return the exception. 052 * @throws IllegalStateException if the process has not exited yet. 053 */ 054 public ExecuteException getException() { 055 if (!hasResult) { 056 throw new IllegalStateException("The process has not exited yet therefore no result is available ..."); 057 } 058 return exception; 059 } 060 061 /** 062 * Gets the {@code exitValue} of the process. 063 * 064 * @return the exitValue. 065 * @throws IllegalStateException if the process has not exited yet. 066 */ 067 public int getExitValue() { 068 if (!hasResult) { 069 throw new IllegalStateException("The process has not exited yet therefore no result is available ..."); 070 } 071 return exitValue; 072 } 073 074 /** 075 * Tests whether the process exited and a result is available, i.e. exitCode or exception? 076 * 077 * @return true whether a result of the execution is available. 078 */ 079 public boolean hasResult() { 080 return hasResult; 081 } 082 083 /** 084 * @see org.apache.commons.exec.ExecuteResultHandler#onProcessComplete(int) 085 */ 086 @Override 087 public void onProcessComplete(final int exitValue) { 088 this.exitValue = exitValue; 089 this.exception = null; 090 this.hasResult = true; 091 } 092 093 /** 094 * @see org.apache.commons.exec.ExecuteResultHandler#onProcessFailed(org.apache.commons.exec.ExecuteException) 095 */ 096 @Override 097 public void onProcessFailed(final ExecuteException e) { 098 this.exitValue = e.getExitValue(); 099 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}