1 /************************************************************************
2 * Copyright (c) 2000-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.util.watchdog;
19
20 import org.apache.excalibur.thread.ThreadPool;
21 import org.apache.avalon.framework.activity.Disposable;
22 import org.apache.avalon.framework.container.ContainerUtil;
23 import org.apache.avalon.framework.logger.AbstractLogEnabled;
24
25 /***
26 * This class represents an watchdog process that serves to
27 * monitor a situation and triggers an action after a certain time has
28 * passed. This implementation is deliberately inaccurate, trading
29 * accuracy for minimal impact on reset. This should be used when
30 * the time of the Watchdog trigger is not critical, and a high number
31 * of resets are expected.
32 *
33 */
34 public class InaccurateTimeoutWatchdog
35 extends AbstractLogEnabled
36 implements Watchdog, Runnable, Disposable {
37
38 /***
39 * Whether the watchdog is currently checking the trigger condition
40 */
41 private volatile boolean isChecking = false;
42
43 /***
44 * Whether the watchdog has been reset since the thread slept.
45 */
46 private volatile boolean isReset = false;
47
48
49 /***
50 * The number of milliseconds until the watchdog times out.
51 */
52 private final long timeout;
53
54 /***
55 * The last time the internal timer was reset, as measured in milliseconds since
56 * January 1, 1970 00:00:00.000 GMT.
57 */
58 private volatile long lastReset;
59
60 /***
61 * The WatchdogTarget whose execute() method will be called upon triggering
62 * of the condition.
63 */
64 private WatchdogTarget triggerTarget;
65
66 /***
67 * The thread that runs the watchdog.
68 */
69 private Thread watchdogThread;
70
71 /***
72 * The thread pool used to generate InaccurateTimeoutWatchdogs
73 */
74 private ThreadPool myThreadPool;
75
76 /***
77 * The sole constructor for the InaccurateTimeoutWatchdog
78 *
79 * @param timeout the time (in msec) that it will take the Watchdog to timeout
80 * @param target the WatchdogTarget to be executed when this Watchdog expires
81 * @param threadPool the thread pool used to generate threads for this implementation.
82 */
83 public InaccurateTimeoutWatchdog(long timeout, WatchdogTarget target, ThreadPool threadPool) {
84 if (target == null) {
85 throw new IllegalArgumentException("The WatchdogTarget for this TimeoutWatchdog cannot be null.");
86 }
87 if (threadPool == null) {
88 throw new IllegalArgumentException("The thread pool for this TimeoutWatchdog cannot be null.");
89 }
90 this.timeout = timeout;
91 triggerTarget = target;
92 myThreadPool = threadPool;
93 }
94
95 /***
96 * Start this Watchdog, causing it to begin checking.
97 */
98 public void start() {
99 getLogger().debug("Calling start()");
100 lastReset = System.currentTimeMillis();
101 isChecking = true;
102 synchronized(this) {
103 if ( watchdogThread == null) {
104 myThreadPool.execute(this);
105 }
106 }
107 }
108
109 /***
110 * Reset this Watchdog. Tells the Watchdog thread to reset
111 * the timer when it next awakens.
112 */
113 public void reset() {
114 if (watchdogThread != null) {
115 getLogger().debug("Calling reset() " + watchdogThread.getName());
116 } else {
117 getLogger().debug("Calling reset() for inactive watchdog");
118 }
119 isReset = true;
120 }
121
122 /***
123 * Stop this Watchdog, causing the Watchdog to stop checking the trigger
124 * condition. The monitor can be restarted with a call to startWatchdog.
125 */
126 public void stop() {
127 if (watchdogThread != null) {
128 getLogger().debug("Calling stop() " + watchdogThread.getName());
129 } else {
130 getLogger().debug("Calling stop() for inactive watchdog");
131 }
132 isChecking = false;
133 }
134
135 /***
136 * Execute the body of the Watchdog, triggering as appropriate.
137 */
138 public void run() {
139
140 try {
141 watchdogThread = Thread.currentThread();
142
143 while ((!(Thread.currentThread().interrupted())) && (watchdogThread != null)) {
144 try {
145 if (!isChecking) {
146 if (getLogger().isDebugEnabled()) {
147 getLogger().debug("Watchdog " + Thread.currentThread().getName() + " is not active - going to exit.");
148 }
149 synchronized (this) {
150 if (!isChecking) {
151 watchdogThread = null;
152 }
153 continue;
154 }
155 } else {
156 long currentTime = System.currentTimeMillis();
157 if (isReset) {
158 isReset = false;
159 lastReset = currentTime;
160 }
161 long timeToSleep = lastReset + timeout - currentTime;
162 if (watchdogThread != null) {
163 getLogger().debug("Watchdog " + watchdogThread.getName() + " has time to sleep " + timeToSleep);
164 } else {
165 getLogger().debug("Watchdog has time to sleep " + timeToSleep);
166 }
167 if (timeToSleep <= 0) {
168 try {
169 synchronized (this) {
170 if ((isChecking) && (triggerTarget != null)) {
171 triggerTarget.execute();
172 }
173 watchdogThread = null;
174 }
175 } catch (Throwable t) {
176 getLogger().error("Encountered error while executing Watchdog target.", t);
177 }
178 isChecking = false;
179 continue;
180 } else {
181 synchronized(this) {
182 wait(timeToSleep);
183 }
184 }
185 }
186 } catch (InterruptedException ie) {
187 }
188 }
189
190 synchronized( this ) {
191 watchdogThread = null;
192 }
193 } finally {
194
195
196 Thread.currentThread().interrupted();
197 }
198 getLogger().debug("Watchdog " + Thread.currentThread().getName() + " is exiting run().");
199 }
200
201 /***
202 * @see org.apache.avalon.framework.activity.Disposable#dispose()
203 */
204 public void dispose() {
205 synchronized(this) {
206 isChecking = false;
207 if (watchdogThread != null) {
208 getLogger().debug("Calling disposeWatchdog() " + watchdogThread.getName());
209 } else {
210 getLogger().debug("Calling disposeWatchdog() for inactive watchdog");
211 }
212 if (watchdogThread != null) {
213 watchdogThread = null;
214 notifyAll();
215 }
216 ContainerUtil.dispose(triggerTarget);
217 triggerTarget = null;
218 }
219 }
220 }