View Javadoc

1   package org.csc.phynixx.watchdog;
2   
3   /*
4    * #%L
5    * phynixx-watchdog
6    * %%
7    * Copyright (C) 2014 csc
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  
24  import org.csc.phynixx.common.logger.IPhynixxLogger;
25  import org.csc.phynixx.common.logger.PhynixxLogManager;
26  import org.csc.phynixx.watchdog.objectref.IObjectReference;
27  import org.csc.phynixx.watchdog.objectref.ObjectReference;
28  import org.csc.phynixx.watchdog.objectref.WeakObjectReference;
29  
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.Set;
33  
34  
35  class Watchdog implements Runnable, IWatchdog {
36  
37      private IPhynixxLogger log = PhynixxLogManager.getLogger(this.getClass());
38  
39      /**
40       * condition checks if the current Watchdog's thread is running
41       */
42      private RestartCondition restartCondition = null;
43  
44      /**
45       * to be able to restart the watchdog thread it is kept as a handle. The basic thread can be restarted
46       */
47      private class ThreadHandle {
48          private Thread thread = null;
49  
50          private ThreadHandle() {
51              super();
52          }
53  
54          private void start() {
55              if (this.thread != null && this.thread.isAlive()) {
56                  throw new IllegalStateException("Thread " + this.thread + " is alive and can't be restartet");
57              }
58              String title = "Watchdog::" + Watchdog.this.getId().toString();
59              this.thread = new Thread(Watchdog.this, title);
60              this.thread.start();
61          }
62  
63          private Thread getThread() {
64              return thread;
65          }
66  
67          public String toString() {
68              return this.thread != null ? this.thread.toString() : "null";
69          }
70  
71          private void shutdown() {
72              this.thread = null;
73          }
74      }
75  
76  
77      private Set<IObjectReference<IWatchedCondition>> conditions = new HashSet<IObjectReference<IWatchedCondition>>();
78  
79      private String description = "";
80  
81      private long checkInterval = 100L;
82      private volatile boolean killed = false;
83      private ThreadHandle threadHandle = new ThreadHandle();
84  
85  
86      private Long id =-1L;
87  
88  
89      /**
90       * instanciate Watchdog via WatchdogFactory
91       *
92       * @param id            TODO
93       * @param checkInterval
94       */
95      Watchdog(Long id, long checkInterval) {
96          this(id, checkInterval, "");
97      }
98  
99      Watchdog(Long id, long checkInterval, String description) {
100         super();
101         this.checkInterval = checkInterval;
102         this.description = description;
103         this.id = id;
104         this.threadHandle.start(); // start the watchdog thread
105         this.restartCondition = new RestartCondition(WatchdogRegistry.getWatchdogManagementInterval(), this);
106     }
107 
108 
109     /* (non-Javadoc)
110      * @see org.csc.phynixx.watchdog.IWatchog#getId()
111      */
112     public Long getId() {
113         return id;
114     }
115 
116     ThreadHandle getThreadHandle() {
117         return threadHandle;
118     }
119 
120 
121     /**
122      * @return
123      */
124     RestartCondition getRestartCondition() {
125         return restartCondition;
126     }
127 
128     /**
129      * @return thread executing the background watch
130      * my be null (the watchdog needs to be restarted)
131      */
132     public Thread getThread() {
133         ThreadHandle th = this.getThreadHandle();
134         return th.thread;
135     }
136 
137     /* (non-Javadoc)
138      * @see org.csc.phynixx.watchdog.IWatchog#getCheckInterval()
139      */
140     public long getCheckInterval() {
141         return checkInterval;
142     }
143 
144 
145     /* (non-Javadoc)
146      * @see org.csc.phynixx.watchdog.IWatchog#registerCondition(org.csc.phynixx.watchdog.IWatchedCondition)
147      */
148     public void registerCondition(IWatchedCondition cond) {
149         this.registerCondition(cond, true);
150     }
151 
152     /**
153      * @param cond
154      * @param weakReferenced indicates if the condition ist weak referenced
155      */
156     void registerCondition(IWatchedCondition cond, boolean weakReferenced) {
157         synchronized (conditions) {
158             if(!isConditionRegistered(cond)) {
159                 IObjectReference<IWatchedCondition> objRef = null;
160                 if (weakReferenced) {
161                     objRef = new WeakObjectReference(cond);
162                 } else {
163                     objRef = new ObjectReference(cond);
164                 }
165                 // keep a weak reference to the condition
166                 this.conditions.add(objRef);
167             }
168         }
169     }
170 
171     private boolean isConditionRegistered(IWatchedCondition cond) {
172         Set<IWatchedCondition> conds=copyConditions();
173         return conds.contains(cond);
174     }
175 
176 
177     /* (non-Javadoc)
178      * @see org.csc.phynixx.watchdog.IWatchog#getCountRegisteredConditions()
179      */
180     public int getCountRegisteredConditions() {
181         return this.conditions.size();
182     }
183 
184     /* (non-Javadoc)
185      * @see org.csc.phynixx.watchdog.IWatchog#getAliveConditions()
186      */
187     public Set<IWatchedCondition> getAliveConditions() {
188         return this.copyConditions();
189     }
190 
191 
192     /* (non-Javadoc)
193      * @see org.csc.phynixx.watchdog.IWatchog#unregisterCondition(org.csc.phynixx.watchdog.IWatchedCondition)
194      */
195     public void unregisterCondition(IWatchedCondition condition) {
196         synchronized (conditions) {
197             Set<IObjectReference<IWatchedCondition>> copiedRefs=new HashSet<IObjectReference<IWatchedCondition>>(this.conditions);
198 
199             for (Iterator<IObjectReference<IWatchedCondition>> iterator = copiedRefs.iterator(); iterator.hasNext(); ) {
200                 IObjectReference<IWatchedCondition> objRef =  iterator.next();
201                 IWatchedCondition cond = objRef.get();
202                 if (cond == null || cond.equals(condition)) {
203                     this.conditions.remove(objRef);
204                 }
205             }
206         }
207     }
208 
209     void copyConditions(IWatchdog wd) {
210         Set<IWatchedCondition> copiedConditions = this.copyConditions();
211         synchronized (wd) {
212             for (Iterator<IWatchedCondition> iterator = copiedConditions.iterator(); iterator.hasNext(); ) {
213                 wd.registerCondition( iterator.next());
214             }
215         }
216     }
217 
218     /**
219      * copies the current set of conditions and cleans up the current set of conditions
220      *
221      * @return
222      */
223     private Set<IWatchedCondition> copyConditions() {
224         Set<IWatchedCondition> copiedConditions = null;
225         // Copy the current Conditions ...
226         synchronized (conditions) {
227 
228             Set<IObjectReference<IWatchedCondition>> copiedObjref = new HashSet<IObjectReference<IWatchedCondition>>(this.conditions);
229             copiedConditions = new HashSet<IWatchedCondition>(this.conditions.size());
230 
231             this.conditions.clear();
232             IWatchedCondition cond = null;
233             for (Iterator<IObjectReference<IWatchedCondition>> iterator = copiedObjref.iterator(); iterator.hasNext(); ) {
234                 IObjectReference<IWatchedCondition> objRef =  iterator.next();
235                 cond = objRef.get();
236                 if (!objRef.isStale() && cond != null && !cond.isUseless()) {
237                     copiedConditions.add(cond);
238                     this.conditions.add(objRef);
239                 }
240             }
241 
242         }
243 
244         return copiedConditions;
245     }
246 
247 
248     /* (non-Javadoc)
249      * @see org.csc.phynixx.watchdog.IWatchog#isKilled()
250      */
251     public boolean isKilled() {
252         return killed;
253     }
254 
255     /* (non-Javadoc)
256      * @see org.csc.phynixx.watchdog.IWatchog#activate()
257      */
258     public synchronized void activate() {
259 
260         synchronized (this.conditions) {
261             IWatchedCondition cond = null;
262             for (Iterator iterator = this.conditions.iterator(); iterator.hasNext(); ) {
263                 IObjectReference objRef = (IObjectReference) iterator.next();
264                 if (objRef.get() != null) {
265                     cond = (IWatchedCondition) objRef.get();
266                     cond.setActive(true);
267                 }
268             }
269         }
270     }
271 
272     /* (non-Javadoc)
273      * @see org.csc.phynixx.watchdog.IWatchog#deactivate()
274      */
275     public synchronized void deactivate() {
276 
277         synchronized (this.conditions) {
278             IWatchedCondition cond = null;
279             for (Iterator iterator = this.conditions.iterator(); iterator.hasNext(); ) {
280                 IObjectReference objRef = (IObjectReference) iterator.next();
281                 if (objRef.get() != null) {
282                     cond = (IWatchedCondition) objRef.get();
283                     cond.setActive(false);
284                 }
285             }
286         }
287     }
288 
289     /**
290      * stops the executing thread but does not kill the thread.
291      * It can be restarted
292      */
293     public synchronized void stop() {
294         try {
295             this.kill();
296         } finally {
297             this.killed = false;
298             this.restartCondition.setUseless(false);
299             this.restartCondition.setActive(false);
300         }
301 
302     }
303 
304 
305     public synchronized void kill() {
306         this.restartCondition.setUseless(true);
307 
308         if (this.isKilled() || this.threadHandle.getThread() == null || !this.threadHandle.getThread().isAlive()) {
309             this.killed = true;
310             this.threadHandle.shutdown();
311             this.acknowledgeKilled();
312             return;
313         }
314 
315         // Interrupt the excuting thread an waiting for shutdown
316         if (!this.isKilled() && this.threadHandle.getThread() != null && this.threadHandle.getThread().isAlive()) {
317             this.killed = true;
318             this.threadHandle.getThread().interrupt();
319         }
320 
321         // wait until the thread is stopped ....
322         int limit = 10;
323         int count = 0;
324         while (this.threadHandle.getThread().isAlive() && count < limit) {
325             if (log.isInfoEnabled()) {
326                 log.info("Watchdog.kill: Waiting to notification " + this.getId());
327             }
328             try {
329                 this.wait(1000);
330             } catch (InterruptedException e) {
331             }
332             count++;
333         }
334         if (log.isInfoEnabled()) {
335             log.info("Watchdog.kill: Watchdog " + this.getId() + " killed");
336         }
337 
338         this.threadHandle.shutdown();
339     }
340 
341     public synchronized void restart() {
342 
343         this.kill();
344 
345         this.killed = false;
346         this.threadHandle.start();
347 
348         if (this.log.isInfoEnabled()) {
349             this.log.info(" Watchdog " + getId() + " restarted");
350         }
351 
352         this.restartCondition.setUseless(false);
353         this.restartCondition.setActive(true);
354 
355     }
356 
357     /* (non-Javadoc)
358      * @see org.csc.phynixx.watchdog.IWatchog#isUseless()
359      */
360     public boolean isUseless() {
361 
362         boolean useless = true;
363         synchronized (conditions) {
364             for (Iterator iterator = this.conditions.iterator(); iterator.hasNext(); ) {
365                 IObjectReference objRef = (IObjectReference) iterator.next();
366                 if (!objRef.isStale() && !((IWatchedCondition) (objRef.get())).isUseless()) {
367                     useless = false;
368                 }
369             }
370         }
371 
372         return useless;
373     }
374 
375 
376     public boolean isStale() {
377         return false;
378     }
379 
380     /* (non-Javadoc)
381      * @see org.csc.phynixx.watchdog.IWatchog#isAlive()
382      */
383     public boolean isAlive() {
384         if (this.threadHandle.getThread() != null) {
385             return this.threadHandle.getThread().isAlive();
386         }
387         return false;
388     }
389 
390     public void run() {
391         while (!this.isKilled()) {
392             // wait until it time to check the conditions
393             long start = System.currentTimeMillis();
394             long waiting = this.checkInterval;
395             while (waiting > 0) {
396                 try {
397                     Thread.currentThread().sleep(waiting);
398                 } catch (InterruptedException e) {
399                 } finally {
400                     if (this.isKilled()) {
401                         break;
402                     }
403                     waiting = this.checkInterval - (System.currentTimeMillis() - start);
404                 }
405             }
406 
407             // recvheck if killed
408             if (this.isKilled()) {
409                 break;
410             }
411 
412             evaluateConditions();
413 
414             // yielding ...
415             try {
416                 Thread.currentThread().sleep(10);
417             } catch (InterruptedException e) {
418             }
419         }
420         // acknowledge the thread starvation for all waiting for the dead of the thread
421         this.acknowledgeKilled();
422     }
423 
424     /**
425      * evaluate the conditions
426      * <p/>
427      * it's synchronized to prevent any other thread from changing the state of
428      * the watchdog or of one of its conditions
429      */
430     private synchronized void evaluateConditions() {
431         // Copy the current Conditions ...
432         Set<IWatchedCondition> copiedConditions = this.copyConditions();
433 
434         // check the conditions ....
435         for (Iterator<IWatchedCondition> iterator = copiedConditions.iterator(); iterator.hasNext(); ) {
436             IWatchedCondition cond = iterator.next();
437 
438             synchronized (cond) {
439                 // the conditions are not accessed in a synchronized way. This has to be  make sure by the Implementations
440                 if (cond.isActive() && !cond.checkCondition()) {
441                     cond.conditionViolated();
442                 }
443             }
444         }
445         return;
446     }
447 
448     private void acknowledgeKilled() {
449         synchronized (this) {
450             this.killed=true;
451             this.notifyAll();
452         }
453 
454     }
455 
456     /* (non-Javadoc)
457      * @see org.csc.phynixx.watchdog.IWatchog#getConditionInfos()
458 	 */
459     public String[] getConditionInfos() {
460         String[] conds = new String[conditions.size()];
461         synchronized (conditions) {
462             int i = 0;
463             for (Iterator iterator = this.conditions.iterator(); iterator.hasNext(); i++) {
464                 IObjectReference objRef = (IObjectReference) iterator.next();
465                 StringBuffer buffer = new StringBuffer(" -- ");
466                 if (objRef.isStale()) {
467                     buffer.append("primarily description :: " + objRef.getObjectDescription());
468                 } else {
469                     buffer.append(objRef.get());
470                 }
471                 buffer.append("; isStale=").append(objRef.isStale()).
472                         append(" ; isWeakReference=").append(objRef.isWeakReference());
473                 conds[i] = buffer.toString();
474             }
475         }
476 
477         return conds;
478     }
479 
480     /* (non-Javadoc)
481      * @see org.csc.phynixx.watchdog.IWatchog#getWatchdogInfo()
482      */
483     public String getWatchdogInfo() {
484         StringBuffer buffer = new StringBuffer("Watchdog [").
485                 append("ID=").append(this.id).
486                 append("; CheckInterval=").append(this.checkInterval).append(" msecs");
487         if (this.description != null && !this.description.equals("")) {
488             buffer.append("; ").append(this.description);
489         }
490         buffer.append("]");
491         buffer.append(" isAlive=").append(this.isAlive()).append("; ");
492         if (this.getThread() != null) {
493             buffer.append(this.getThread());
494         } else {
495             buffer.append("NO THREAD");
496         }
497         buffer.append(" #watched conditions=").append(this.getAliveConditions().size());
498 
499         return buffer.toString();
500     }
501 
502 
503     public String toString() {
504         StringBuffer buffer = new StringBuffer(this.getWatchdogInfo()).append('\n');
505 
506         String[] conditionInfos = this.getConditionInfos();
507         for (int i = 0; i < conditionInfos.length; i++) {
508             buffer.append(" . . . ").append(conditionInfos[i]).append("\n");
509         }
510 
511         return buffer.toString();
512 
513     }
514 
515 
516 }