View Javadoc

1   /**********************************************************************
2    * SoftKeyedObjectCache.java
3    * created on 12.03.2005 by netseeker
4    * $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/cache/SoftKeyedObjectCache.java,v $
5    * $Date: 2006/02/04 14:16:21 $
6    * $Revision: 1.3 $
7    *********************************************************************/
8   package de.netseeker.ejoe.cache;
9   
10  import java.lang.ref.Reference;
11  import java.lang.ref.ReferenceQueue;
12  import java.lang.ref.SoftReference;
13  import java.util.Iterator;
14  import java.util.LinkedHashMap;
15  import java.util.Map;
16  
17  /***
18   * A keyed value cache implementation that uses soft references to store cached
19   * values. Once a value got garbage collected by the JVM, the associated
20   * key-value pair will be removed by the next put() or get() operation.
21   *
22   * @author netseeker
23   */
24  public final class SoftKeyedObjectCache
25  {
26  	private final ReferenceQueue	_refQueue	= new ReferenceQueue();
27  
28  	private int						_size		= 10000;
29  
30  	private final Map				_cache;
31  
32  	private final Map				_references;
33  
34  	/***
35  	 * Create a new cache which will store max. 10000 objects. If the cache
36  	 * reaches max. size it will ditch some of the oldest key-value pairs by
37  	 * each put operation
38  	 */
39  	public SoftKeyedObjectCache()
40  	{
41  		this._cache = new LinkedHashMap(this._size);
42  		this._references = new LinkedHashMap(this._size);
43  	}
44  
45  	/***
46  	 * Create a new cache of the given size. If the cache reaches max. size it
47  	 * will ditch some of the oldest key-value pairs by each put operation
48  	 *
49  	 * @param size
50  	 *            max. amount of objects to hold in this cache
51  	 */
52  	public SoftKeyedObjectCache(int size)
53  	{
54  		this._size = size;
55  		this._cache = new LinkedHashMap(size);
56  		this._references = new LinkedHashMap(size);
57  	}
58  
59  	/***
60  	 * Adds a new entry to this cache under the given key
61  	 *
62  	 * @param key
63  	 *            identifier under which the given object will be found in this
64  	 *            cache
65  	 * @param obj
66  	 *            object to cache
67  	 */
68  	public void put(Object key, Object obj)
69  	{
70  		clean();
71  		// cache reached max. size?
72  		if (this._cache.size() >= this._size)
73  		{
74  			// behave like a timed-cache and remove the five oldest key-value
75  			// pairs
76  			Iterator it = this._cache.keySet().iterator();
77  			int count = 0;
78  			do
79  			{
80  				remove(it.next());
81  				count++;
82  			}
83  			while (count < 5 && it.hasNext());
84  		}
85  
86  		SoftReference ref = new SoftReference(obj, this._refQueue);
87  		this._cache.put(key, ref);
88  		this._references.put(ref, key);
89  	}
90  
91  	/***
92  	 * Returns an entry from this cache
93  	 *
94  	 * @param key
95  	 * @return object that is cached under the given key or null if either no
96  	 *         object is cached under the given key, or an object was cached
97  	 *         under the given key but is already garbage collected
98  	 */
99  	public Object get(Object key)
100 	{
101 		clean();
102 		Object value = null;
103 		SoftReference ref = (SoftReference) this._cache.get(key);
104 		if (ref != null)
105 		{
106 			value = ref.get();
107 		}
108 		return value;
109 	}
110 
111 	/***
112 	 * Removes the given key and it's associated value from this cache
113 	 *
114 	 * @param key
115 	 * @return the removed value or null
116 	 */
117 	public Object remove(Object key)
118 	{
119 		clean();
120 		Object obj = null;
121 
122 		if (key != null)
123 		{
124 			SoftReference ref = (SoftReference) this._cache.get(key);
125 			if (ref != null)
126 			{
127 				obj = ref.get();
128 				this._references.remove(ref);
129 			}
130 			this._cache.remove(key);
131 		}
132 
133 		return obj;
134 	}
135 
136 	/***
137 	 * garbage collects all dereferenced objects in this cache
138 	 */
139 	private void clean()
140 	{
141 		Reference ref;
142 
143 		while ((ref = this._refQueue.poll()) != null)
144 		{
145 			Object key = this._references.get(ref);
146 			if (key != null)
147 			{
148 				this._cache.remove(key);
149 			}
150 
151 			this._references.remove(ref);
152 		}
153 	}
154 }