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