View Javadoc

1   /*
2    * $Id: ExpiryMonitor.java 22665 2011-08-15 06:33:46Z mike.schilling $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util.monitor;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.lifecycle.Disposable;
15  import org.mule.config.i18n.CoreMessages;
16  import org.mule.util.concurrent.DaemonThreadFactory;
17  
18  import java.util.Iterator;
19  import java.util.Map;
20  import java.util.concurrent.ConcurrentHashMap;
21  import java.util.concurrent.ScheduledThreadPoolExecutor;
22  import java.util.concurrent.TimeUnit;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  /**
28   * <code>ExpiryMonitor</code> can monitor objects beased on an expiry time and can
29   * invoke a callback method once the object time has expired. If the object does
30   * expire it is removed from this monitor.
31   */
32  public class ExpiryMonitor implements Runnable, Disposable
33  {
34      /**
35       * logger used by this class
36       */
37      protected static final Log logger = LogFactory.getLog(ExpiryMonitor.class);
38  
39      protected ScheduledThreadPoolExecutor scheduler;
40  
41      private Map monitors;
42  
43      private int monitorFrequency;
44  
45      private String name;
46      
47      private ClassLoader contextClassLoader;
48  
49      private MuleContext muleContext;
50  
51      private boolean onPollingNodeOnly;
52  
53      public ExpiryMonitor(MuleContext muleContext, boolean onPollingNodeOnly)
54      {
55          this.muleContext = muleContext;
56          this.onPollingNodeOnly = onPollingNodeOnly;
57      }
58  
59      public ExpiryMonitor(String name, MuleContext muleContext, boolean onPollingNodeOnly)
60      {
61          this(name, 1000, muleContext, onPollingNodeOnly);
62      }
63  
64      public ExpiryMonitor(String name, int monitorFrequency, MuleContext muleContext, boolean onPollingNodeOnly)
65      {
66          this(muleContext, onPollingNodeOnly);
67          this.name = name;
68          this.monitorFrequency = monitorFrequency;
69          init();
70      }
71  
72      public ExpiryMonitor(String name, int monitorFrequency, ClassLoader contextClassLoader, MuleContext muleContext, boolean onPollingNodeOnly)
73      {
74          this(muleContext, onPollingNodeOnly);
75          this.name = name;
76          this.monitorFrequency = monitorFrequency;
77          this.contextClassLoader = contextClassLoader;
78          init();
79      }
80      
81      public ExpiryMonitor(String name, int monitorFrequency, ScheduledThreadPoolExecutor scheduler, MuleContext muleContext, boolean onPollingNodeOnly)
82      {
83          this(muleContext, onPollingNodeOnly);
84          this.name = name;
85          this.monitorFrequency = monitorFrequency;
86          this.scheduler = scheduler;
87          init();
88      }
89  
90      protected void init()
91      {
92          if (monitorFrequency <= 0)
93          {
94              throw new IllegalArgumentException(CoreMessages.propertyHasInvalidValue("monitorFrequency",
95                      new Integer(monitorFrequency)).toString());
96          }
97          monitors = new ConcurrentHashMap();
98          if (scheduler == null)
99          {
100             this.scheduler = new ScheduledThreadPoolExecutor(1);
101             scheduler.setThreadFactory(new DaemonThreadFactory(name + ".expiry.monitor", contextClassLoader));
102             scheduler.scheduleWithFixedDelay(this, 0, monitorFrequency,
103                                              TimeUnit.MILLISECONDS);
104         }
105     }
106 
107     /**
108      * Adds an expirable object to monitor. If the Object is already being monitored
109      * it will be reset and the millisecond timeout will be ignored
110      *
111      * @param value     the expiry value
112      * @param timeUnit  The time unit of the Expiry value
113      * @param expirable the objec that will expire
114      */
115     public void addExpirable(long value, TimeUnit timeUnit, Expirable expirable)
116     {
117         if (isRegistered(expirable))
118         {
119             resetExpirable(expirable);
120         }
121         else
122         {
123             if (logger.isDebugEnabled())
124             {
125                 logger.debug("Adding new expirable: " + expirable);
126             }
127             monitors.put(expirable, new ExpirableHolder(timeUnit.toNanos(value), expirable));
128         }
129     }
130 
131     public boolean isRegistered(Expirable expirable)
132     {
133         return (monitors.get(expirable) != null);
134     }
135 
136     public void removeExpirable(Expirable expirable)
137     {
138         if (logger.isDebugEnabled())
139         {
140             logger.debug("Removing expirable: " + expirable);
141         }
142         monitors.remove(expirable);
143     }
144 
145     public void resetExpirable(Expirable expirable)
146     {
147         ExpirableHolder eh = (ExpirableHolder) monitors.get(expirable);
148         if (eh != null)
149         {
150             eh.reset();
151             if (logger.isDebugEnabled())
152             {
153                 logger.debug("Reset expirable: " + expirable);
154             }
155         }
156     }
157 
158     /**
159      * The action to be performed by this timer task.
160      */
161     public void run()
162     {
163         ExpirableHolder holder;
164 
165         if (!onPollingNodeOnly || muleContext == null || muleContext.isPrimaryPollingInstance())
166         {
167             for (Iterator iterator = monitors.values().iterator(); iterator.hasNext();)
168             {
169                 holder = (ExpirableHolder) iterator.next();
170                 if (holder.isExpired())
171                 {
172                     removeExpirable(holder.getExpirable());
173                     holder.getExpirable().expired();
174                 }
175             }
176         }
177     }
178 
179     public void dispose()
180     {
181         logger.info("disposing monitor");
182         scheduler.shutdown();
183         ExpirableHolder holder;
184         for (Iterator iterator = monitors.values().iterator(); iterator.hasNext();)
185         {
186             holder = (ExpirableHolder) iterator.next();
187             removeExpirable(holder.getExpirable());
188             try
189             {
190                 holder.getExpirable().expired();
191             }
192             catch (Exception e)
193             {
194                 // TODO MULE-863: What should we really do?
195                 logger.debug(e.getMessage());
196             }
197         }
198     }
199 
200     private static class ExpirableHolder
201     {
202 
203         private long nanoseconds;
204         private Expirable expirable;
205         private long created;
206 
207         public ExpirableHolder(long nanoseconds, Expirable expirable)
208         {
209             this.nanoseconds = nanoseconds;
210             this.expirable = expirable;
211             created = System.nanoTime();
212         }
213 
214         public long getNanoSeconds()
215         {
216             return nanoseconds;
217         }
218 
219         public Expirable getExpirable()
220         {
221             return expirable;
222         }
223 
224         public boolean isExpired()
225         {
226             return (System.nanoTime() - nanoseconds) > created;
227         }
228 
229         public void reset()
230         {
231             created = System.nanoTime();
232         }
233     }
234 }