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