KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > LRUEnterpriseContextCachePolicy


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.ejb.plugins;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Timer JavaDoc;
26 import java.util.TimerTask JavaDoc;
27
28 import org.jboss.deployment.DeploymentException;
29 import org.jboss.ejb.EnterpriseContext;
30 import org.jboss.logging.Logger;
31 import org.jboss.metadata.MetaData;
32 import org.jboss.metadata.XmlLoadable;
33 import org.jboss.monitor.Monitorable;
34 import org.jboss.monitor.client.BeanCacheSnapshot;
35 import org.jboss.util.LRUCachePolicy;
36 import org.w3c.dom.Element JavaDoc;
37
38 /**
39  * Least Recently Used cache policy for EnterpriseContexts.
40  *
41  * @see AbstractInstanceCache
42  * @author <a HREF="mailto:simone.bordet@compaq.com">Simone Bordet</a>
43  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
44  * @version $Revision: 44565 $
45  */

46 public class LRUEnterpriseContextCachePolicy extends LRUCachePolicy
47    implements XmlLoadable, Monitorable
48 {
49    // Constants -----------------------------------------------------
50

51    // Attributes ----------------------------------------------------
52
protected static Logger log = Logger.getLogger(LRUEnterpriseContextCachePolicy.class);
53    protected static Timer JavaDoc tasksTimer = new Timer JavaDoc(true);
54    static
55    {
56       log.debug("Cache policy timer started, tasksTimer="+tasksTimer);
57    }
58
59    /** The AbstractInstanceCache that uses this cache policy */
60    private AbstractInstanceCache m_cache;
61
62    /** The period of the resizer's runs */
63    private long m_resizerPeriod;
64
65    /** The period of the overager's runs */
66    private long m_overagerPeriod;
67
68    /** The age after which a bean is automatically passivated */
69    private long m_maxBeanAge;
70
71    /**
72     * Enlarge cache capacity if there is a cache miss every or less
73     * this member's value
74     */

75    private long m_minPeriod;
76
77    /**
78     * Shrink cache capacity if there is a cache miss every or more
79     * this member's value
80     */

81    private long m_maxPeriod;
82
83    /**
84     * The resizer will always try to keep the cache capacity so
85     * that the cache is this member's value loaded of cached objects
86     */

87    private double m_factor;
88
89    /** The overager timer task */
90    private TimerTask JavaDoc m_overager;
91
92    /** The resizer timer task */
93    private TimerTask JavaDoc m_resizer;
94
95    /** Useful for log messages */
96    private StringBuffer JavaDoc m_buffer = new StringBuffer JavaDoc();
97
98
99    // Static --------------------------------------------------------
100

101    // Constructors --------------------------------------------------
102

103    /**
104     * Creates a LRU cache policy object given the instance cache that use
105     * this policy object.
106     */

107    public LRUEnterpriseContextCachePolicy(AbstractInstanceCache eic)
108    {
109       if (eic == null)
110          throw new IllegalArgumentException JavaDoc
111             ("Instance cache argument cannot be null");
112
113       m_cache = eic;
114    }
115
116    // Public --------------------------------------------------------
117

118    // Monitorable implementation ------------------------------------
119

120    public void sample(Object JavaDoc s)
121    {
122       if( m_cache == null )
123          return;
124
125       BeanCacheSnapshot snapshot = (BeanCacheSnapshot)s;
126       LRUList list = getList();
127       synchronized (m_cache.getCacheLock())
128       {
129          snapshot.m_cacheMinCapacity = list.m_minCapacity;
130          snapshot.m_cacheMaxCapacity = list.m_maxCapacity;
131          snapshot.m_cacheCapacity = list.m_capacity;
132          snapshot.m_cacheSize = list.m_count;
133       }
134    }
135
136    // Z implementation ----------------------------------------------
137

138    public void start()
139    {
140       if (m_resizerPeriod > 0)
141       {
142          m_resizer = new ResizerTask(m_resizerPeriod);
143          long delay = (long) (Math.random() * m_resizerPeriod);
144          tasksTimer.schedule(m_resizer, delay, m_resizerPeriod);
145       }
146
147       if (m_overagerPeriod > 0)
148       {
149          m_overager = new OveragerTask(m_overagerPeriod);
150          long delay = (long) (Math.random() * m_overagerPeriod);
151          tasksTimer.schedule(m_overager, delay, m_overagerPeriod);
152       }
153    }
154
155    public void stop()
156    {
157       if (m_resizer != null) {m_resizer.cancel();}
158       if (m_overager != null) {m_overager.cancel();}
159       super.stop();
160    }
161
162    public void destroy()
163    {
164       m_overager = null;
165       m_resizer = null;
166       m_cache = null;
167       super.destroy();
168    }
169
170    /**
171     * Reads from the configuration the parameters for this cache policy, that are
172     * all optionals.
173     * FIXME 20010626 marcf:
174     * Simone seriously arent' all the options overkill? give it another 6 month .
175     * Remember you are exposing the guts of this to the end user, also provide defaults
176     * so that if an entry is not specified you can still work and it looks _much_ better in
177     * the configuration files.
178     *
179     */

180    public void importXml(Element JavaDoc element) throws DeploymentException
181    {
182       String JavaDoc min = MetaData.getElementContent(MetaData.getOptionalChild(element, "min-capacity"));
183       String JavaDoc max = MetaData.getElementContent(MetaData.getOptionalChild(element, "max-capacity"));
184       String JavaDoc op = MetaData.getElementContent(MetaData.getOptionalChild(element, "overager-period"));
185       String JavaDoc rp = MetaData.getElementContent(MetaData.getOptionalChild(element, "resizer-period"));
186       String JavaDoc ma = MetaData.getElementContent(MetaData.getOptionalChild(element, "max-bean-age"));
187       String JavaDoc map = MetaData.getElementContent(MetaData.getOptionalChild(element, "max-cache-miss-period"));
188       String JavaDoc mip = MetaData.getElementContent(MetaData.getOptionalChild(element, "min-cache-miss-period"));
189       String JavaDoc fa = MetaData.getElementContent(MetaData.getOptionalChild(element, "cache-load-factor"));
190       try
191       {
192          if (min != null)
193          {
194             int s = Integer.parseInt(min);
195             if (s <= 0)
196             {
197                throw new DeploymentException("Min cache capacity can't be <= 0");
198             }
199             m_minCapacity = s;
200          }
201          if (max != null)
202          {
203             int s = Integer.parseInt(max);
204             if (s <= 0)
205             {
206                throw new DeploymentException("Max cache capacity can't be <= 0");
207             }
208             m_maxCapacity = s;
209          }
210          if (op != null)
211          {
212             int p = Integer.parseInt(op);
213             if (p <= 0) {throw new DeploymentException("Overager period can't be <= 0");}
214             m_overagerPeriod = p * 1000;
215          }
216          if (rp != null)
217          {
218             int p = Integer.parseInt(rp);
219             if (p <= 0) {throw new DeploymentException("Resizer period can't be <= 0");}
220             m_resizerPeriod = p * 1000;
221          }
222          if (ma != null)
223          {
224             int a = Integer.parseInt(ma);
225             if (a <= 0) {throw new DeploymentException("Max bean age can't be <= 0");}
226             m_maxBeanAge = a * 1000;
227          }
228          if (map != null)
229          {
230             int p = Integer.parseInt(map);
231             if (p <= 0) {throw new DeploymentException("Max cache miss period can't be <= 0");}
232             m_maxPeriod = p * 1000;
233          }
234          if (mip != null)
235          {
236             int p = Integer.parseInt(mip);
237             if (p <= 0) {throw new DeploymentException("Min cache miss period can't be <= 0");}
238             m_minPeriod = p * 1000;
239          }
240          if (fa != null)
241          {
242             double f = Double.parseDouble(fa);
243             if (f <= 0.0) {throw new DeploymentException("Cache load factor can't be <= 0");}
244             m_factor = f;
245          }
246       }
247       catch (NumberFormatException JavaDoc x)
248       {
249          throw new DeploymentException("Can't parse policy configuration", x);
250       }
251    }
252
253    // Y overrides ---------------------------------------------------
254

255    /**
256     * Flush is overriden here because in this policy impl
257     * flush might not actually remove all the instances from the cache.
258     * Those instances that are in use (associated with a transaction) should not
259     * be removed from the cache. So, the iteration is done not until the cache is empty
260     * but until we tried to age-out every instance in the cache.
261     */

262    public void flush()
263    {
264       int i = size();
265       LRUCacheEntry entry = null;
266       while (i-- > 0 && (entry = m_list.m_tail) != null)
267       {
268          ageOut(entry);
269       }
270    }
271    
272    // Package protected ---------------------------------------------
273

274    // Protected -----------------------------------------------------
275

276    protected LRUList createList()
277    {
278       return new ContextLRUList();
279    }
280
281
282    protected void ageOut(LRUCacheEntry entry)
283    {
284       if( m_cache == null )
285          return;
286
287       if (entry == null)
288       {
289          throw new IllegalArgumentException JavaDoc
290             ("Cannot remove a null cache entry");
291       }
292
293       if( log.isTraceEnabled() )
294       {
295          m_buffer.setLength(0);
296          m_buffer.append("Aging out from cache bean ");
297          m_buffer.append(m_cache.getContainer().getBeanMetaData().getEjbName());
298          m_buffer.append("with id = ");
299          m_buffer.append(entry.m_key);
300          m_buffer.append("; cache size = ");
301          m_buffer.append(getList().m_count);
302          log.trace(m_buffer.toString());
303       }
304
305       // This will schedule the passivation
306
m_cache.release((EnterpriseContext)entry.m_object);
307    }
308    protected void cacheMiss()
309    {
310       LRUList list = getList();
311       ++list.m_cacheMiss;
312    }
313
314    // Private -------------------------------------------------------
315

316    private LRUList getList()
317    {
318       return m_list;
319    }
320
321    // Inner classes -------------------------------------------------
322

323    /**
324     * This TimerTask resizes the cache capacity using the cache miss frequency
325     * algorithm, that is the more cache misses we have, the more the cache size
326     * is enlarged, and viceversa. <p>
327     * Of course, the maximum and minimum capacity are the bounds that this
328     * resizer never passes.
329     */

330    protected class ResizerTask extends TimerTask JavaDoc
331    {
332       private String JavaDoc m_message;
333       private StringBuffer JavaDoc m_buffer;
334       private long resizerPeriod;
335
336       protected ResizerTask(long resizerPeriod)
337       {
338          this.resizerPeriod = resizerPeriod;
339          m_message = "Resized cache for bean " +
340             m_cache.getContainer().getBeanMetaData().getEjbName() +
341             ": old capacity = ";
342          m_buffer = new StringBuffer JavaDoc();
343       }
344
345       public void run()
346       {
347          // For now implemented as a Cache Miss Frequency algorithm
348
if( m_cache == null )
349          {
350             cancel();
351             return;
352          }
353
354          LRUList list = getList();
355
356          // Sync with the cache, since it is accessed also by another thread
357
synchronized (m_cache.getCacheLock())
358          {
359             int period = list.m_cacheMiss == 0 ? Integer.MAX_VALUE : (int)(resizerPeriod / list.m_cacheMiss);
360             int cap = list.m_capacity;
361             if (period <= m_minPeriod && cap < list.m_maxCapacity)
362             {
363                // Enlarge cache capacity: if period == m_minPeriod then
364
// the capacity is increased of the (1-m_factor)*100 %.
365
double factor = 1.0 + ((double)m_minPeriod / period) * (1.0 - m_factor);
366                int newCap = (int)(cap * factor);
367                list.m_capacity = newCap < list.m_maxCapacity ? newCap : list.m_maxCapacity;
368                log(cap, list.m_capacity);
369             }
370             else if (period >= m_maxPeriod &&
371                      cap > list.m_minCapacity &&
372                      list.m_count < (cap * m_factor))
373             {
374                // Shrink cache capacity
375
int newCap = (int)(list.m_count / m_factor);
376                list.m_capacity = newCap > list.m_minCapacity ? newCap : list.m_minCapacity;
377                log(cap, list.m_capacity);
378             }
379             list.m_cacheMiss = 0;
380          }
381       }
382
383       private void log(int oldCapacity, int newCapacity)
384       {
385          if( log.isTraceEnabled() )
386          {
387             m_buffer.setLength(0);
388             m_buffer.append(m_message);
389             m_buffer.append(oldCapacity);
390             m_buffer.append(", new capacity = ");
391             m_buffer.append(newCapacity);
392             log.trace(m_buffer.toString());
393          }
394       }
395    }
396
397    /**
398     * This TimerTask passivates cached beans that have not been called for a while.
399     */

400    protected class OveragerTask extends TimerTask JavaDoc
401    {
402       private String JavaDoc m_message;
403       private StringBuffer JavaDoc m_buffer;
404
405       protected OveragerTask(long period)
406       {
407          m_message = getTaskLogMessage() + " " +
408             m_cache.getContainer().getBeanMetaData().getEjbName() +
409             " with id = ";
410          m_buffer = new StringBuffer JavaDoc();
411       }
412
413       public void run()
414       {
415          if( m_cache == null )
416          {
417             cancel();
418             return;
419          }
420
421          LRUList list = getList();
422          long now = System.currentTimeMillis();
423          ArrayList JavaDoc passivateEntries = null;
424          synchronized (m_cache.getCacheLock())
425          {
426             for (LRUCacheEntry entry = list.m_tail; entry != null; entry = entry.m_prev)
427             {
428                if (now - entry.m_time >= getMaxAge())
429                {
430                   // Attempt to remove this entry from cache
431
if (passivateEntries == null) passivateEntries = new ArrayList JavaDoc();
432                   passivateEntries.add(entry);
433                }
434                else
435                {
436                   break;
437                }
438             }
439          }
440          // We need to do this outside of cache lock because of deadlock possibilities
441
// with EntityInstanceInterceptor and Stateful. This is because tryToPassivate
442
// calls lock.synch and other interceptor call lock.synch and after call a cache method that locks
443
if (passivateEntries != null)
444          {
445             for (int i = 0; i < passivateEntries.size(); i++)
446             {
447                LRUCacheEntry entry = (LRUCacheEntry) passivateEntries.get(i);
448                try
449                {
450                   m_cache.tryToPassivate((EnterpriseContext) entry.m_object);
451                }
452                catch (Throwable JavaDoc t)
453                {
454                   log.debug("Ignored error while trying to passivate ctx", t);
455                }
456             }
457          }
458       }
459
460       private void log(Object JavaDoc key, int count)
461       {
462          if( log.isTraceEnabled() )
463          {
464             m_buffer.setLength(0);
465             m_buffer.append(m_message);
466             m_buffer.append(key);
467             m_buffer.append(" - Cache size = ");
468             m_buffer.append(count);
469             log.trace(m_buffer.toString());
470          }
471       }
472
473       protected String JavaDoc getTaskLogMessage()
474       {
475          return "Scheduling for passivation overaged bean";
476       }
477
478       protected String JavaDoc getJMSTaskType()
479       {
480          return "OVERAGER";
481       }
482
483       protected long getMaxAge()
484       {
485          return m_maxBeanAge;
486       }
487    }
488
489    /**
490     * Subclass that logs list activity events.
491     */

492    protected class ContextLRUList extends LRUList
493    {
494       boolean trace = log.isTraceEnabled();
495       protected void entryPromotion(LRUCacheEntry entry)
496       {
497          if (trace)
498             log.trace("entryPromotion, entry="+entry);
499             
500          // The cache is full, temporarily increase it
501
if (m_count == m_capacity && m_capacity >= m_maxCapacity)
502          {
503             ++m_capacity;
504             log.warn("Cache has reached maximum capacity for container " +
505                      m_cache.getContainer().getJmxName() +
506                      " - probably because all instances are in use. " +
507                      "Temporarily increasing the size to " + m_capacity);
508          }
509       }
510       protected void entryAdded(LRUCacheEntry entry)
511       {
512          if (trace)
513             log.trace("entryAdded, entry="+entry);
514       }
515       protected void entryRemoved(LRUCacheEntry entry)
516       {
517          if (trace)
518             log.trace("entryRemoved, entry="+entry);
519       }
520       protected void capacityChanged(int oldCapacity)
521       {
522          if (trace)
523             log.trace("capacityChanged, oldCapacity="+oldCapacity);
524       }
525    }
526
527 }
528
Popular Tags