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 */
018
019package org.apache.commons.exec;
020
021/**
022 * A default implementation of 'ExecuteResultHandler' used for asynchronous
023 * process handling.
024 *
025 * @version $Id: DefaultExecuteResultHandler.java 1636057 2014-11-01 21:14:00Z ggregory $
026 */
027public class DefaultExecuteResultHandler implements ExecuteResultHandler {
028
029    /** the interval polling the result */
030    private static final int SLEEP_TIME_MS = 50;
031
032    /** Keep track if the process is still running */
033    private volatile boolean hasResult;
034
035    /** The exit value of the finished process */
036    private volatile int exitValue;
037
038    /** Any offending exception */
039    private volatile ExecuteException exception;
040
041    /**
042     * Constructor.
043     */
044    public DefaultExecuteResultHandler() {
045        this.hasResult = false;
046        this.exitValue = Executor.INVALID_EXITVALUE;
047    }
048
049    /**
050     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessComplete(int)
051     */
052    public void onProcessComplete(final int exitValue) {
053        this.exitValue = exitValue;
054        this.exception = null;
055        this.hasResult = true;
056    }
057
058    /**
059     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessFailed(org.apache.commons.exec.ExecuteException)
060     */
061    public void onProcessFailed(final ExecuteException e) {
062        this.exitValue = e.getExitValue();            
063        this.exception = e;
064        this.hasResult = true;
065    }
066
067    /**
068     * Get the {@code exception} causing the process execution to fail.
069     *
070     * @return Returns the exception.
071     * @throws IllegalStateException if the process has not exited yet
072     */
073    public ExecuteException getException() {
074
075        if (!hasResult) {
076            throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
077        }
078
079        return exception;
080    }
081
082    /**
083     * Get the {@code exitValue} of the process.
084     *
085     * @return Returns the exitValue.
086     * @throws IllegalStateException if the process has not exited yet
087     */
088    public int getExitValue() {
089
090        if (!hasResult) {
091            throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
092        }
093
094        return exitValue;
095    }
096
097    /**
098     * Has the process exited and a result is available, i.e. exitCode or exception?
099     *
100     * @return true if a result of the execution is available
101     */
102    public boolean hasResult() {
103        return hasResult;
104    }
105
106    /**
107     * Causes the current thread to wait, if necessary, until the
108     * process has terminated. This method returns immediately if
109     * the process has already terminated. If the process has
110     * not yet terminated, the calling thread will be blocked until the
111     * process exits.
112     *
113     * @exception  InterruptedException if the current thread is
114     *             {@linkplain Thread#interrupt() interrupted} by another
115     *             thread while it is waiting, then the wait is ended and
116     *             an {@link InterruptedException} is thrown.
117     */
118    public void waitFor() throws InterruptedException {
119
120        while (!hasResult()) {
121            Thread.sleep(SLEEP_TIME_MS);
122        }
123    }
124
125    /**
126     * Causes the current thread to wait, if necessary, until the
127     * process has terminated. This method returns immediately if
128     * the process has already terminated. If the process has
129     * not yet terminated, the calling thread will be blocked until the
130     * process exits.
131     *
132     * @param timeout the maximum time to wait in milliseconds
133     * @exception  InterruptedException if the current thread is
134     *             {@linkplain Thread#interrupt() interrupted} by another
135     *             thread while it is waiting, then the wait is ended and
136     *             an {@link InterruptedException} is thrown.
137     */
138    public void waitFor(final long timeout) throws InterruptedException {
139
140        final long until = System.currentTimeMillis() + timeout;
141
142        while (!hasResult() && System.currentTimeMillis() < until) {
143            Thread.sleep(SLEEP_TIME_MS);
144        }
145    }
146}