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