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:
019: package org.apache.tools.ant.taskdefs;
020:
021: import org.apache.tools.ant.BuildException;
022: import org.apache.tools.ant.util.TimeoutObserver;
023: import org.apache.tools.ant.util.Watchdog;
024:
025: /**
026: * Destroys a process running for too long.
027: * For example:
028: * <pre>
029: * ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
030: * Execute exec = new Execute(myloghandler, watchdog);
031: * exec.setCommandLine(mycmdline);
032: * int exitvalue = exec.execute();
033: * if (Execute.isFailure(exitvalue) && watchdog.killedProcess()) {
034: * // it was killed on purpose by the watchdog
035: * }
036: * </pre>
037:
038: * @see Execute
039: * @see org.apache.tools.ant.util.Watchdog
040: * @since Ant 1.2
041: */
042: public class ExecuteWatchdog implements TimeoutObserver {
043:
044: /** the process to execute and watch for duration */
045: private Process process;
046:
047: /** say whether or not the watchdog is currently monitoring a process */
048: private volatile boolean watch = false;
049:
050: /** exception that might be thrown during the process execution */
051: private Exception caught = null;
052:
053: /** say whether or not the process was killed due to running overtime */
054: private volatile boolean killedProcess = false;
055:
056: /** will tell us whether timeout has occurred */
057: private Watchdog watchdog;
058:
059: /**
060: * Creates a new watchdog with a given timeout.
061: *
062: * @param timeout the timeout for the process in milliseconds.
063: * It must be greater than 0.
064: */
065: public ExecuteWatchdog(long timeout) {
066: watchdog = new Watchdog(timeout);
067: watchdog.addTimeoutObserver(this );
068: }
069:
070: /**
071: * @param timeout the timeout value to use in milliseconds.
072: * @see #ExecuteWatchdog(long)
073: * @deprecated since 1.5.x.
074: * Use constructor with a long type instead.
075: * (1.4.x compatibility)
076: */
077: public ExecuteWatchdog(int timeout) {
078: this ((long) timeout);
079: }
080:
081: /**
082: * Watches the given process and terminates it, if it runs for too long.
083: * All information from the previous run are reset.
084: * @param process the process to monitor. It cannot be <tt>null</tt>
085: * @throws IllegalStateException if a process is still being monitored.
086: */
087: public synchronized void start(Process process) {
088: if (process == null) {
089: throw new NullPointerException("process is null.");
090: }
091: if (this .process != null) {
092: throw new IllegalStateException("Already running.");
093: }
094: this .caught = null;
095: this .killedProcess = false;
096: this .watch = true;
097: this .process = process;
098: watchdog.start();
099: }
100:
101: /**
102: * Stops the watcher. It will notify all threads possibly waiting
103: * on this object.
104: */
105: public synchronized void stop() {
106: watchdog.stop();
107: cleanUp();
108: }
109:
110: /**
111: * Called after watchdog has finished.
112: * This can be called in the watchdog thread
113: * @param w the watchdog
114: */
115: public synchronized void timeoutOccured(Watchdog w) {
116: try {
117: try {
118: // We must check if the process was not stopped
119: // before being here
120: process.exitValue();
121: } catch (IllegalThreadStateException itse) {
122: // the process is not terminated, if this is really
123: // a timeout and not a manual stop then kill it.
124: if (watch) {
125: killedProcess = true;
126: process.destroy();
127: }
128: }
129: } catch (Exception e) {
130: caught = e;
131: } finally {
132: cleanUp();
133: }
134: }
135:
136: /**
137: * reset the monitor flag and the process.
138: */
139: protected synchronized void cleanUp() {
140: watch = false;
141: process = null;
142: }
143:
144: /**
145: * This method will rethrow the exception that was possibly caught during
146: * the run of the process. It will only remains valid once the process has
147: * been terminated either by 'error', timeout or manual intervention.
148: * Information will be discarded once a new process is ran.
149: * @throws BuildException a wrapped exception over the one that was
150: * silently swallowed and stored during the process run.
151: */
152: public synchronized void checkException() throws BuildException {
153: if (caught != null) {
154: throw new BuildException(
155: "Exception in ExecuteWatchdog.run: "
156: + caught.getMessage(), caught);
157: }
158: }
159:
160: /**
161: * Indicates whether or not the watchdog is still monitoring the process.
162: * @return <tt>true</tt> if the process is still running, otherwise
163: * <tt>false</tt>.
164: */
165: public boolean isWatching() {
166: return watch;
167: }
168:
169: /**
170: * Indicates whether the last process run was killed on timeout or not.
171: * @return <tt>true</tt> if the process was killed otherwise
172: * <tt>false</tt>.
173: */
174: public boolean killedProcess() {
175: return killedProcess;
176: }
177: }
|