View Javadoc

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             // Ensure that the thread is in a non-interrupted state when it gets returned
195             // to the pool.
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 }