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