KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > perseus > cache > lib > BasicCacheManager


1 /**
2  * Copyright (C) 2001-2002
3  * - France Telecom R&D
4  * - Laboratoire Logiciels, Systemes, Reseaux - UMR 5526, CNRS-INPG-UJF
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * Release: 1.0
21  *
22  * Authors:
23  *
24  */

25
26 package org.objectweb.perseus.cache.lib;
27
28 import org.objectweb.fractal.api.control.BindingController;
29 import org.objectweb.fractal.api.control.LifeCycleController;
30
31 import org.objectweb.perseus.cache.api.CacheAttributeController;
32 import org.objectweb.perseus.cache.api.CacheCapacityEvent;
33 import org.objectweb.perseus.cache.api.CacheCapacityEventListener;
34 import org.objectweb.perseus.cache.api.CacheEntry;
35 import org.objectweb.perseus.cache.api.CacheEntryFactory;
36 import org.objectweb.perseus.cache.api.CacheEvent;
37 import org.objectweb.perseus.cache.api.CacheEventListener;
38 import org.objectweb.perseus.cache.api.CacheException;
39 import org.objectweb.perseus.cache.api.CacheManager;
40 import org.objectweb.perseus.cache.api.FixableCacheEntry;
41 import org.objectweb.perseus.cache.api.InvalidCacheEntryException;
42 import org.objectweb.perseus.cache.api.UnbindManager;
43 import org.objectweb.perseus.cache.api.CacheFullException;
44 import org.objectweb.perseus.cache.replacement.api.ReplacementManager;
45 import org.objectweb.util.monolog.api.Logger;
46 import org.objectweb.util.monolog.api.BasicLevel;
47 import org.objectweb.util.monolog.api.LoggerFactory;
48
49 import java.util.HashMap JavaDoc;
50 import java.util.Iterator JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.HashSet JavaDoc;
53 import java.util.Collection JavaDoc;
54 import java.util.ArrayList JavaDoc;
55 import java.util.Collections JavaDoc;
56 import java.lang.ref.ReferenceQueue JavaDoc;
57 import java.lang.ref.Reference JavaDoc;
58 import java.lang.ref.WeakReference JavaDoc;
59
60 /**
61  * This class is an implementation of a cache. The data structure is a Hahsed
62  * map between cache entry identifier and an entry instance.
63  * This class is a fractal component.
64  *
65  * @author S.Chassande-Barrioz, E.Bruneton
66  */

67 public class BasicCacheManager
68         implements
69         CacheManager,
70         UnbindManager,
71         BindingController,
72         LifeCycleController,
73         CacheAttributeController {
74
75     public final static String JavaDoc DEFAULT_AUTO_CLEAN_SIZE = "7%";
76     public final static String JavaDoc CACHE_ENTRY_FACTORY_BINDING = "cache-entry-factory";
77     public final static String JavaDoc REPLACEMENT_MANAGER_BINDING = "replacement-manager";
78     public final static String JavaDoc CACHE_LISTENER_BINDING = "cache-listeners";
79     public final static String JavaDoc CACHE_CAPACTITY_LISTENER_BINDING = "cache-capacity-listeners";
80
81
82     /**
83      * This class is a basic implementation of the CacheEvent interface.
84      */

85     protected class Event implements CacheEvent {
86
87         public int eventId = 0;
88         public CacheEntry ce = null;
89         public Object JavaDoc oid = null;
90         public int cacheSize = -2;
91
92         public Event(int eventid, Object JavaDoc oid, int cachesize) {
93             eventId = eventid;
94             this.oid = oid;
95             cacheSize = cachesize;
96         }
97
98         public Event(int eventid, CacheEntry _ce, int cachesize) {
99             eventId = eventid;
100             ce = _ce;
101             cacheSize = cachesize;
102         }
103
104         public int getEventId() {
105             return eventId;
106         }
107
108         public Object JavaDoc getCeIdentifier() {
109             return oid;
110         }
111
112         public CacheEntry getEntry() {
113             return ce;
114         }
115     }
116
117     /**
118      * This class is a basic implementation of the CacheCapacityEvent interface.
119      */

120     protected class CapacityEvent implements CacheCapacityEvent {
121
122         public int eventId = 0;
123         public int oldCacheSize = -2;
124         public int cacheSize = -2;
125
126         public CapacityEvent(int eventid, int oldCacheSize, int cacheSize) {
127             this.eventId = eventid;
128             this.cacheSize = cacheSize;
129             this.oldCacheSize = oldCacheSize;
130         }
131
132         public int getEventId() {
133             return eventId;
134         }
135
136         public int getOldSize() {
137             return oldCacheSize;
138         }
139
140         public int getSize() {
141             return cacheSize;
142         }
143     }
144
145     /**
146      * This field is the factory of CacheEntry instances.
147      */

148     protected CacheEntryFactory cef = null;
149
150     protected ReplacementManager rm = null;
151
152     /**
153      * This field is the cache. This is a Map which the key are object
154      * identifiers and the values are FixableCacheEntry instances.
155      */

156     protected Map JavaDoc cache;
157
158     /**
159      * Queue used to register the weak references stored in the cache.
160      */

161     protected ReferenceQueue JavaDoc queue;
162
163     /**
164      * This field is the max size of the cache.
165      */

166     protected int cacheMaxSize;
167
168     private BackgroundCleaner bgcleaner;
169     protected int cacheThresHold = 0;
170     protected String JavaDoc cacheThresHoldStr;
171
172     protected Logger logger = null;
173     protected Logger bgclogger = null;
174
175     /**
176      * This field is the list of CacheEventListener registered.
177      */

178     protected HashMap JavaDoc cels;
179
180     /**
181      * This field is the list of CacheEventListener registered.
182      */

183     protected HashMap JavaDoc ccels;
184
185     private String JavaDoc autoCleanSizeStr;
186
187     private int autoCleanSize;
188
189     private boolean started = false;
190
191     /**
192      * It builds a BasicCacheManager with an empty CacheEventListener list,
193      * an empty cache and the default max size.
194      */

195     public BasicCacheManager() {
196         cels = new HashMap JavaDoc();
197         ccels = new HashMap JavaDoc();
198         cache = new HashMap JavaDoc();
199         queue = new ReferenceQueue JavaDoc();
200         cacheMaxSize = NO_LIMIT;
201         started = false;
202         autoCleanSizeStr = null;
203         cacheThresHold = 0;
204     }
205
206     public BackgroundCleaner getBgcleaner() {
207         if (bgcleaner == null && cacheThresHold > 0) {
208             bgcleaner = new BackgroundCleaner(rm, bgclogger);
209         }
210         return bgcleaner;
211     }
212
213     //IMPLEMENTATION OF THE LifeCycleController INTERFACE //
214
//----------------------------------------------------//
215

216     public String JavaDoc getFcState() {
217         return started ? STARTED : STOPPED ;
218     }
219
220     public void startFc() {
221         if (!started) {
222             started = true;
223             autoCleanSize = getValue(autoCleanSizeStr);
224             cacheThresHold = getValue(cacheThresHoldStr);
225             CacheCapacityEvent ev = new CapacityEvent(
226                     CacheCapacityEvent.NOTIFY_CACHE_RESIZE, NO_LIMIT, this.cacheMaxSize);
227             for (Iterator JavaDoc it = ccels.values().iterator(); it.hasNext();) {
228                 ((CacheCapacityEventListener) it.next()).cacheResized(ev);
229             }
230         }
231     }
232
233     public void stopFc() {
234         if (started) {
235             started = false;
236             try {
237                 // resizes the cache to 0 in order to clear it.
238
setMaxObjects(0);
239             } catch (CacheException e) {
240             }
241         }
242     }
243
244     //IMPLEMENTATION OF THE UserBindingControler INTERFACE //
245
//-----------------------------------------------------//
246

247     public String JavaDoc[] listFc() {
248         HashSet JavaDoc itfs = new HashSet JavaDoc();
249         itfs.add(CACHE_ENTRY_FACTORY_BINDING);
250         itfs.add(REPLACEMENT_MANAGER_BINDING);
251         itfs.add(CACHE_LISTENER_BINDING);
252         itfs.add(CACHE_CAPACTITY_LISTENER_BINDING);
253         return (String JavaDoc[])itfs.toArray(new String JavaDoc[itfs.size()]);
254     }
255
256     public Object JavaDoc lookupFc(String JavaDoc s) {
257         if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) {
258             return cef;
259         } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) {
260             return rm;
261         } else if (s.startsWith(CACHE_LISTENER_BINDING)) {
262             return cels.get(s);
263         } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) {
264             return ccels.get(s);
265         } else {
266             return null;
267         }
268     }
269
270     public void bindFc(String JavaDoc s, Object JavaDoc o) {
271         if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) {
272             cef = (CacheEntryFactory) o;
273         } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) {
274             rm = (ReplacementManager) o;
275         } else if (s.startsWith(CACHE_LISTENER_BINDING)) {
276             cels.put(s, o);
277         } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) {
278             ccels.put(s, o);
279         } else if ("monolog-factory".equals(s)) {
280             bgclogger = ((LoggerFactory) o)
281                     .getLogger(logger.getName() + ".bgcleaner");
282         } else if ("logger".equals(s)) {
283             logger = (Logger) o;
284         }
285     }
286
287     public void unbindFc(String JavaDoc s) {
288         if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) {
289             cef = null;
290         } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) {
291             rm = null;
292             if (bgcleaner != null) {
293                 bgcleaner.stop();
294                 bgcleaner = null;
295             }
296         } else if (s.startsWith(CACHE_LISTENER_BINDING)) {
297             cels.remove(s);
298         } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) {
299             ccels.remove(s);
300         }
301     }
302
303     //IMPLEMENTATION OF THE CacheAttributeControler INTERFACE //
304
//--------------------------------------------------------//
305

306     public void setMaxObjects(int size)
307             throws IllegalArgumentException JavaDoc, CacheException {
308         if (logger != null && logger.isLoggable(BasicLevel.DEBUG))
309             logger.log(BasicLevel.DEBUG, "resize the cache: "
310                     + (size == NO_LIMIT ? "UNLIMITED" : "" + size));
311         if (size == NO_LIMIT) {
312             cacheMaxSize = NO_LIMIT;
313             return;
314         }
315         if (size < 0)
316             throw new IllegalArgumentException JavaDoc(
317                     "Impossible to set the cache size to a negative value: " + size);
318         synchronized (cache) {
319             CacheCapacityEvent ev = new CapacityEvent(
320                     CacheCapacityEvent.NOTIFY_CACHE_RESIZE, cacheMaxSize, size);
321             if (logger != null && logger.isLoggable(BasicLevel.DEBUG))
322                 logger.log(BasicLevel.DEBUG,
323                     "resize the cache: " + cacheMaxSize + " to " + size
324                         + " (Nd elem=" + cache.size() + ")");
325             int delta = size - cache.size();
326             if (delta < 0) {
327                 delta = -delta;
328                 int free = rm.forceFree(delta);
329                 if (delta > free)
330                     throw new CacheException("It stays " + (delta - free)
331                             + " resources which have not been free");
332             }
333             cacheMaxSize = size;
334             if (started) {
335                 for (Iterator JavaDoc it = ccels.values().iterator(); it.hasNext();) {
336                     ((CacheCapacityEventListener) it.next()).cacheResized(ev);
337                 }
338             } else {
339                 //the composant has been stopped, rehash the data structure
340
Map JavaDoc old = cache;
341                 cache = new HashMap JavaDoc(cacheMaxSize, 1);
342                 if (old.size() > 0) {
343                     cache.putAll(cache);
344                 }
345             }
346         }
347     }
348
349     public int getMaxObjects() {
350         return cacheMaxSize;
351     }
352
353     public int getCurrentSize() {
354         return cache.size();
355     }
356
357     public Collection JavaDoc getCurrentEntryIdentifiers() {
358         synchronized(cache) {
359             if (cache.size() == 0) {
360                 return Collections.EMPTY_SET;
361             }
362             return Collections.unmodifiableCollection(cache.keySet());
363         }
364     }
365
366     public void setMemorySize(int size) throws IllegalArgumentException JavaDoc {
367         if (size < 0)
368             throw new IllegalArgumentException JavaDoc(
369                     "Impossible to set the cache size to a negative value: " + size);
370     }
371
372     public int getMemorySize() {
373         return 0;
374     }
375
376     public int getCurrentMemorySize() {
377         return 0;
378     }
379
380     public void setAutoCleanSize(String JavaDoc size) {
381         autoCleanSizeStr = size;
382         autoCleanSize = getValue(autoCleanSizeStr);
383     }
384
385     public String JavaDoc getAutoCleanSize() {
386         return autoCleanSizeStr;
387     }
388
389     public void setAutoCleanThreshold(String JavaDoc size) {
390         cacheThresHoldStr = size;
391         cacheThresHold = getValue(cacheThresHoldStr);
392     }
393
394     public String JavaDoc getAutoCleanThreshold() {
395         return cacheThresHoldStr;
396     }
397
398     private int getValue(String JavaDoc str) {
399         if (cacheMaxSize == NO_LIMIT) {
400             return 0;
401         }
402         if (str == null) {
403             str = "75%";
404         }
405         int i = str.indexOf('%');
406         if (i == -1) {
407             return Integer.parseInt(str);
408         } else {
409             int res = Integer.parseInt(str.substring(0, i));
410             return (cacheMaxSize * res) / 100;
411         }
412     }
413
414     //IMPLEMENTATION OF THE CacheManager INTERFACE //
415
//---------------------------------------------//
416

417     public CacheEntry bind(Object JavaDoc id, Object JavaDoc object) throws CacheException {
418         if (id == null || object == null)
419             throw new IllegalArgumentException JavaDoc(
420                     "Impossible to add a cache entry with a null identifier or object");
421         if (logger.isLoggable(BasicLevel.DEBUG))
422             logger.log(BasicLevel.DEBUG, "bind an element in the cache, id:" + id);
423         synchronized (cache) {
424             clean();
425
426             // checks that the cache is not full
427
int size = cache.size();
428             if (cacheMaxSize != NO_LIMIT) {
429                 if (size >= cacheMaxSize) {
430                     // removes one element
431
rm.forceFree(autoCleanSize);
432                     size = cache.size();
433                     if (size >= cacheMaxSize) {
434                         // if the cache is still full, calls gc and waits some time
435
forceClean();
436                         size = cache.size();
437                         if (size >= cacheMaxSize) {
438                             // if the cache is really full, throws an exception
439
throw new CacheFullException();
440                         }
441                     }
442                 } else if ((size + 1) >= cacheThresHold
443                         && getBgcleaner() != null) {
444                     getBgcleaner().backgroungCleanup(autoCleanSize);
445                 }
446             }
447
448             // checks that 'id' is not already bound
449
WeakCacheEntry ref = (WeakCacheEntry) cache.get(id);
450             if (ref != null) {
451                 FixableCacheEntry entry = (FixableCacheEntry) ref.get();
452                 //if (entry != null && !(entry.getCeObject() == object && ref.ce == null)) {
453
if (entry != null && ref.ce != null) {
454                     throw new CacheException("Identifier is already bound with "
455                             + (entry.getCeObject() == object ? "the same" : "another")
456                             + " object: \nid=" + id);
457                 }
458             }
459
460             FixableCacheEntry ce = cef.create(id, object);
461             cache.put(id, new WeakCacheEntry(ce, queue));
462             rm.addForReplacement(ce);
463             CacheEvent ev =
464                     new Event(CacheEvent.NOTIFY_BIND, ce, ++size);
465             logger.log(BasicLevel.DEBUG, "notify the bind");
466             for (Iterator JavaDoc it = cels.values().iterator(); it.hasNext();) {
467                 ((CacheEventListener) it.next()).entryBound(ev);
468             }
469             return ce;
470         }
471     }
472
473     public CacheEntry lookup(Object JavaDoc id) {
474         if (id == null)
475             throw new IllegalArgumentException JavaDoc(
476                     "Impossible to find cache entry with a null identifier");
477         synchronized (cache) {
478             try {
479                 CacheEntry ce = get(id);
480                 if (logger.isLoggable(BasicLevel.DEBUG)) {
481                     logger.log(BasicLevel.DEBUG, "lookup: id=" + id);
482                 }
483                 return ce;
484             } catch (InvalidCacheEntryException e) {
485                 return null;
486             }
487         }
488     }
489
490     public void fix(CacheEntry _ce) throws CacheException {
491         if (_ce == null)
492             throw new IllegalArgumentException JavaDoc(
493                     "Impossible to fix a null cache entry");
494         synchronized (_ce) {
495             FixableCacheEntry ce = get(_ce.getCeIdentifier());
496             ce.fixCe();
497             if (logger.isLoggable(BasicLevel.DEBUG))
498                 logger.log(BasicLevel.DEBUG,
499                         "Fix an element in the cache, id:" + ce.getCeIdentifier()
500                         + " / count:" + ce.getCeFixCount());
501         }
502     }
503
504     public void unfix(CacheEntry ce) throws CacheException {
505         if (ce == null)
506             throw new IllegalArgumentException JavaDoc(
507                     "Impossible to unfix a null cache entry");
508         synchronized (ce) {
509             ((FixableCacheEntry) ce).unfixCe();
510             if (logger.isLoggable(BasicLevel.DEBUG))
511                 logger.log(BasicLevel.DEBUG,
512                         "UnFix an element in the cache, id:" + ce.getCeIdentifier()
513                         + " / count:" + ((FixableCacheEntry) ce).getCeFixCount());
514         }
515     }
516
517     public void touch(CacheEntry ce) throws CacheException {
518         if (ce == null)
519             throw new IllegalArgumentException JavaDoc(
520                     "Impossible to touch a null cache entry");
521         synchronized (ce) {
522             // Check that the entry exists in the cache
523
rm.adjustForReplacement((FixableCacheEntry) ce);
524         }
525     }
526
527     //IMPLEMENTATION OF THE UnbindManager INTERFACE //
528
//----------------------------------------------//
529

530     public boolean unbind(Object JavaDoc oid, boolean force) throws CacheException {
531         synchronized (cache) { //to prevent lookup, bind in concurrence
532
return _unbind(oid, force);
533         }
534     }
535     private boolean _unbind(Object JavaDoc oid, boolean force) throws CacheException {
536         if (logger.isLoggable(BasicLevel.DEBUG))
537             logger.log(BasicLevel.DEBUG,
538                     "Unbind an element from the cache, id:" + oid);
539         clean();
540         WeakCacheEntry ref = (WeakCacheEntry) cache.get(oid);
541         if (ref == null) {
542             return true;
543         }
544         FixableCacheEntry ce = (FixableCacheEntry) ref.get();
545         if (ce == null) {
546             throw new InvalidCacheEntryException();
547         }
548         synchronized(ce) {
549             if (ce.getCeFixCount() > 0) {
550                 return false;
551             }
552             // removes the strong reference to the real entry,
553
// but does not remove it from 'cache'
554
ref.ce = null;
555             if (force) {
556                 cache.remove(oid);
557                 CacheEvent ev =
558                         new Event(CacheEvent.NOTIFY_UNBIND, oid, cache.size());
559                 for (Iterator JavaDoc it = cels.values().iterator(); it.hasNext();) {
560                     ((CacheEventListener) it.next()).entryUnbound(ev);
561                 }
562                 rm.removeForReplacement(oid);
563             }
564             return force;
565         }
566     }
567
568     public Collection JavaDoc unbindAll(Collection JavaDoc oids, boolean force) throws CacheException {
569         ArrayList JavaDoc unbound = new ArrayList JavaDoc(oids.size());
570         synchronized (cache) { //to prevent lookup, bind in concurrence
571
if (cache.size() == 0) {
572                 unbound.addAll(oids);
573                 return unbound;
574             }
575             Iterator JavaDoc it = oids.iterator();
576             while(it.hasNext()) {
577                 Object JavaDoc oid = it.next();
578                 if (!unbound.contains(oid) && _unbind(oid, force)) {
579                     unbound.add(oid);
580                 }
581             }
582             if (!force) {
583                 /* In case of force == false, we run the GC in order to really
584                    evict the entries */

585                 forceClean();
586                 clean();
587                 if (cache.size() == 0) {
588                     // Optimization when the user has asked an evict all on the
589
// cache
590
unbound = new ArrayList JavaDoc(oids);
591                     return unbound;
592                 }
593                 // Check if some entries has been removed
594
it = oids.iterator();
595                 while(it.hasNext()) {
596                     Object JavaDoc oid = it.next();
597                     WeakCacheEntry ref = (WeakCacheEntry) cache.get(oid);
598                     if (ref == null) {
599                         if (!unbound.contains(oid)) {
600                             unbound.add(oid);
601                         }
602                     }
603                 }
604             }
605         }
606         return unbound;
607     }
608
609     public Collection JavaDoc unbindUnfixed(boolean force) throws CacheException {
610         return Collections.EMPTY_SET;
611     }
612
613     //----------------------------------------------//
614

615     private FixableCacheEntry get(Object JavaDoc id) throws InvalidCacheEntryException {
616         Reference JavaDoc ref = (Reference JavaDoc) cache.get(id);
617         if (ref == null) {
618             /* The cache can be resized during the call, and the entry can
619             disappear temporaly. Then to really known if the entry does not
620             exist a synchrnonization of the cache is neeeded. */

621             synchronized(cache) {
622                 ref = (Reference JavaDoc) cache.get(id);
623             }
624             if (ref == null) {
625                 throw new InvalidCacheEntryException(id);
626             }
627         }
628         FixableCacheEntry entry = (FixableCacheEntry) ref.get();
629         if (entry == null) {
630             throw new InvalidCacheEntryException();
631         }
632         //Re-born the entry ;-)
633
((WeakCacheEntry) ref).ce = entry;
634         return entry;
635     }
636
637     /**
638      * Removes all entries that have been garbage collected. Does not block.
639      */

640     private void clean() {
641         while (true) {
642             WeakCacheEntry ref = (WeakCacheEntry) queue.poll();
643             if (ref != null && ref.ce == null) {
644                 if (logger.isLoggable(BasicLevel.DEBUG))
645                     logger.log(BasicLevel.DEBUG,
646                             "Really remove from the cache, id:" + ref.getIdentifier());
647                 Object JavaDoc oid = ref.getIdentifier();
648                 cache.remove(oid);
649                 CacheEvent ev = new Event(CacheEvent.NOTIFY_UNBIND,
650                         oid, cache.size());
651                 for (Iterator JavaDoc it = cels.values().iterator(); it.hasNext();) {
652                     ((CacheEventListener) it.next()).entryUnbound(ev);
653                 }
654                 rm.removeForReplacement(oid);
655             } else {
656                 return;
657             }
658         }
659     }
660
661     /**
662      * Calls the garbage collector and waits until an entry can be removed, or a
663      * timeout period (1 second) has expired.
664      */

665     private void forceClean() {
666         logger.log(BasicLevel.DEBUG, "forceClean()");
667         for (int i = 0; i < 6; ++i) {
668             System.gc();
669             WeakCacheEntry ref = null;
670             try {
671                 ref = (WeakCacheEntry) queue.remove(50);
672             } catch (InterruptedException JavaDoc e) {
673             }
674             if (ref != null) {
675                 Object JavaDoc oid = ref.getIdentifier();
676                 cache.remove(oid);
677                 CacheEvent ev = new Event(CacheEvent.NOTIFY_UNBIND,
678                         oid, cache.size());
679                 for (Iterator JavaDoc it = cels.values().iterator(); it.hasNext();) {
680                     ((CacheEventListener) it.next()).entryUnbound(ev);
681                 }
682                 rm.removeForReplacement(oid);
683                 return;
684             }
685         }
686     }
687
688     /**
689      * The entries stored in 'cache' are weak references to the real entries.
690      * Each weak entry also stores the id of the entry (this id is used in clean
691      * and forceClean to remove the entry from 'cache')
692      */

693     private static class WeakCacheEntry extends WeakReference JavaDoc {
694
695         private final Object JavaDoc id;
696
697         // strong reference to the real entry: prevents it from being garbage
698
// collected until the unbind method is called.
699
public CacheEntry ce;
700
701         public WeakCacheEntry(CacheEntry ce, ReferenceQueue JavaDoc queue) {
702             super(ce, queue);
703             this.id = ce.getCeIdentifier();
704             this.ce = ce;
705         }
706
707         public Object JavaDoc getIdentifier() {
708             return id;
709         }
710     }
711 }
712
Popular Tags