001 /* 002 // This software is subject to the terms of the Common Public License 003 // Agreement, available at the following URL: 004 // http://www.opensource.org/licenses/cpl.html. 005 // Copyright (C) 2004-2005 TONBELLER AG 006 // All Rights Reserved. 007 // You must accept the terms of that agreement to use this software. 008 */ 009 package mondrian.rolap.cache; 010 011 import mondrian.util.Pair; 012 013 import java.lang.ref.ReferenceQueue; 014 import java.lang.ref.SoftReference; 015 import java.util.*; 016 017 /** 018 * A map with soft references that is cleaned up in regular intervals. 019 * <p> 020 * There is no contains(key) method because it makes no sense - after 021 * contains() returns true, the garbage collector may remove 022 * the value that was contained. Instead the code should call get() and 023 * keep a reference to the value to prevent garbage collection. 024 * 025 * @author av 026 * @since Nov 3, 2005 027 * @version $Id: //open/mondrian/src/main/mondrian/rolap/cache/SoftSmartCache.java#7 $ 028 */ 029 public class SoftSmartCache<K, V> implements SmartCache<K, V> { 030 031 private final Map<K, CacheReference> cache = 032 new HashMap<K, CacheReference>(); 033 034 private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); 035 036 /** 037 * an entry in the cache that contains the key for 038 * the cache map to remove the entry when its value 039 * has been garbage collected 040 * 041 * @author rk 042 * @since Nov 7, 2005 043 */ 044 class CacheReference extends SoftReference<V> { 045 K key; 046 047 public CacheReference(K key, V value) { 048 super(value, queue); 049 this.key = key; 050 } 051 052 public String toString() { 053 return String.valueOf(get()); 054 } 055 } 056 057 /* (non-Javadoc) 058 * @see mondrian.rolap.cache.SmartCache#put(java.lang.Object, java.lang.Object) 059 */ 060 public synchronized V put(K key, V value) { 061 // remove garbage collected entries from cache 062 CacheReference ref; 063 while ((ref = (CacheReference) queue.poll()) != null) { 064 cache.remove(ref.key); 065 } 066 067 // put new entry into cache 068 ref = new CacheReference(key, value); 069 ref = cache.put(key, ref); 070 if (ref != null) { 071 return ref.get(); 072 } 073 return null; 074 } 075 076 /* (non-Javadoc) 077 * @see mondrian.rolap.cache.SmartCache#get(java.lang.Object) 078 */ 079 public synchronized V get(K key) { 080 CacheReference ref = cache.get(key); 081 if (ref == null) { 082 return null; 083 } 084 V value = ref.get(); 085 if (value == null) { 086 cache.remove(key); 087 } 088 return value; 089 } 090 091 /* (non-Javadoc) 092 * @see mondrian.rolap.cache.SmartCache#clear() 093 */ 094 public void clear() { 095 cache.clear(); 096 } 097 098 /* (non-Javadoc) 099 * @see mondrian.rolap.cache.SmartCache#size() 100 */ 101 public int size() { 102 return cache.size(); 103 } 104 105 public Iterator<Map.Entry<K, V>> iterator() { 106 final Iterator<Map.Entry<K, CacheReference>> cacheIterator = 107 cache.entrySet().iterator(); 108 return new Iterator<Map.Entry<K, V>>() { 109 private Map.Entry<K,V> entry; 110 111 public boolean hasNext() { 112 if (entry != null) { 113 return true; 114 } 115 while (cacheIterator.hasNext()) { 116 Map.Entry<K,CacheReference> cacheEntry = 117 cacheIterator.next(); 118 // skip over entries that have been garbage collected 119 final V value = cacheEntry.getValue().get(); 120 if (value != null) { 121 // Would use AbstractMap.SimpleEntry but it's not public 122 // until JDK 1.6. 123 entry = new Pair<K,V>(cacheEntry.getKey(), value); 124 return true; 125 } 126 } 127 return false; 128 } 129 130 public Map.Entry<K, V> next() { 131 Map.Entry<K, V> entry = this.entry; 132 this.entry = null; 133 return entry; 134 } 135 136 public void remove() { 137 cacheIterator.remove(); 138 } 139 }; 140 } 141 } 142 143 // End SoftSmartCache.java 144