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