001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.util.watchdog;
019:
020: import org.apache.excalibur.thread.ThreadPool;
021: import org.apache.avalon.framework.activity.Disposable;
022: import org.apache.avalon.framework.container.ContainerUtil;
023: import org.apache.avalon.framework.logger.AbstractLogEnabled;
024:
025: /**
026: * This class represents an watchdog process that serves to
027: * monitor a situation and triggers an action after a certain time has
028: * passed. This implementation is deliberately inaccurate, trading
029: * accuracy for minimal impact on reset. This should be used when
030: * the time of the Watchdog trigger is not critical, and a high number
031: * of resets are expected.
032: *
033: */
034: public class InaccurateTimeoutWatchdog extends AbstractLogEnabled
035: implements Watchdog, Runnable, Disposable {
036:
037: /**
038: * Whether the watchdog is currently checking the trigger condition
039: */
040: private volatile boolean isChecking = false;
041:
042: /**
043: * Whether the watchdog has been reset since the thread slept.
044: */
045: private volatile boolean isReset = false;
046:
047: /**
048: * The number of milliseconds until the watchdog times out.
049: */
050: private final long timeout;
051:
052: /**
053: * The last time the internal timer was reset, as measured in milliseconds since
054: * January 1, 1970 00:00:00.000 GMT.
055: */
056: private volatile long lastReset;
057:
058: /**
059: * The WatchdogTarget whose execute() method will be called upon triggering
060: * of the condition.
061: */
062: private WatchdogTarget triggerTarget;
063:
064: /**
065: * The thread that runs the watchdog.
066: */
067: private Thread watchdogThread;
068:
069: /**
070: * The thread pool used to generate InaccurateTimeoutWatchdogs
071: */
072: private ThreadPool myThreadPool;
073:
074: /**
075: * The sole constructor for the InaccurateTimeoutWatchdog
076: *
077: * @param timeout the time (in msec) that it will take the Watchdog to timeout
078: * @param target the WatchdogTarget to be executed when this Watchdog expires
079: * @param threadPool the thread pool used to generate threads for this implementation.
080: */
081: public InaccurateTimeoutWatchdog(long timeout,
082: WatchdogTarget target, ThreadPool threadPool) {
083: if (target == null) {
084: throw new IllegalArgumentException(
085: "The WatchdogTarget for this TimeoutWatchdog cannot be null.");
086: }
087: if (threadPool == null) {
088: throw new IllegalArgumentException(
089: "The thread pool for this TimeoutWatchdog cannot be null.");
090: }
091: this .timeout = timeout;
092: triggerTarget = target;
093: myThreadPool = threadPool;
094: }
095:
096: /**
097: * Start this Watchdog, causing it to begin checking.
098: */
099: public void start() {
100: getLogger().debug("Calling start()");
101: lastReset = System.currentTimeMillis();
102: isChecking = true;
103: synchronized (this ) {
104: if (watchdogThread == null) {
105: myThreadPool.execute(this );
106: }
107: }
108: }
109:
110: /**
111: * Reset this Watchdog. Tells the Watchdog thread to reset
112: * the timer when it next awakens.
113: */
114: public void reset() {
115: if (watchdogThread != null) {
116: getLogger().debug(
117: "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(
131: "Calling stop() " + watchdogThread.getName());
132: } else {
133: getLogger().debug("Calling stop() for inactive watchdog");
134: }
135: isChecking = false;
136: }
137:
138: /**
139: * Execute the body of the Watchdog, triggering as appropriate.
140: */
141: public void run() {
142:
143: try {
144: watchdogThread = Thread.currentThread();
145:
146: while ((!(Thread.currentThread().interrupted()))
147: && (watchdogThread != null)) {
148: try {
149: if (!isChecking) {
150: if (getLogger().isDebugEnabled()) {
151: getLogger()
152: .debug(
153: "Watchdog "
154: + Thread
155: .currentThread()
156: .getName()
157: + " is not active - going to exit.");
158: }
159: synchronized (this ) {
160: if (!isChecking) {
161: watchdogThread = null;
162: }
163: continue;
164: }
165: } else {
166: long currentTime = System.currentTimeMillis();
167: if (isReset) {
168: isReset = false;
169: lastReset = currentTime;
170: }
171: long timeToSleep = lastReset + timeout
172: - currentTime;
173: if (watchdogThread != null) {
174: getLogger().debug(
175: "Watchdog "
176: + watchdogThread.getName()
177: + " has time to sleep "
178: + timeToSleep);
179: } else {
180: getLogger().debug(
181: "Watchdog has time to sleep "
182: + timeToSleep);
183: }
184: if (timeToSleep <= 0) {
185: try {
186: synchronized (this ) {
187: if ((isChecking)
188: && (triggerTarget != null)) {
189: triggerTarget.execute();
190: }
191: watchdogThread = null;
192: }
193: } catch (Throwable t) {
194: getLogger()
195: .error(
196: "Encountered error while executing Watchdog target.",
197: t);
198: }
199: isChecking = false;
200: continue;
201: } else {
202: synchronized (this ) {
203: wait(timeToSleep);
204: }
205: }
206: }
207: } catch (InterruptedException ie) {
208: }
209: }
210:
211: synchronized (this ) {
212: watchdogThread = null;
213: }
214: } finally {
215: // Ensure that the thread is in a non-interrupted state when it gets returned
216: // to the pool.
217: Thread.currentThread().interrupted();
218: }
219: getLogger().debug(
220: "Watchdog " + Thread.currentThread().getName()
221: + " is exiting run().");
222: }
223:
224: /**
225: * @see org.apache.avalon.framework.activity.Disposable#dispose()
226: */
227: public void dispose() {
228: synchronized (this ) {
229: isChecking = false;
230: if (watchdogThread != null) {
231: getLogger().debug(
232: "Calling disposeWatchdog() "
233: + watchdogThread.getName());
234: } else {
235: getLogger()
236: .debug(
237: "Calling disposeWatchdog() for inactive watchdog");
238: }
239: if (watchdogThread != null) {
240: watchdogThread = null;
241: notifyAll();
242: }
243: ContainerUtil.dispose(triggerTarget);
244: triggerTarget = null;
245: }
246: }
247: }
|