View Javadoc

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