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