001    package net.sf.cpsolver.ifs.util;
002    
003    import java.lang.ref.Reference;
004    import java.lang.ref.ReferenceQueue;
005    import java.lang.ref.SoftReference;
006    import java.lang.ref.WeakReference;
007    import java.util.Collection;
008    import java.util.HashSet;
009    import java.util.Hashtable;
010    import java.util.Iterator;
011    import java.util.Map;
012    import java.util.Set;
013    import java.util.Vector;
014    
015    import org.apache.log4j.Logger;
016    
017    /** Simple table cache (key, value) using java soft references.
018     * 
019     * @version
020     * IFS 1.1 (Iterative Forward Search)<br>
021     * Copyright (C) 2006 Tomáš Müller<br>
022     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
023     * Lazenska 391, 76314 Zlin, Czech Republic<br>
024     * <br>
025     * This library is free software; you can redistribute it and/or
026     * modify it under the terms of the GNU Lesser General Public
027     * License as published by the Free Software Foundation; either
028     * version 2.1 of the License, or (at your option) any later version.
029     * <br><br>
030     * This library is distributed in the hope that it will be useful,
031     * but WITHOUT ANY WARRANTY; without even the implied warranty of
032     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
033     * Lesser General Public License for more details.
034     * <br><br>
035     * You should have received a copy of the GNU Lesser General Public
036     * License along with this library; if not, write to the Free Software
037     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
038     */
039    
040    public class SoftCache implements Map {
041            private static Logger sLogger = Logger.getLogger(SoftCache.class);
042            private Hashtable iCache = new Hashtable();
043            private ReferenceQueue iQueue = new ReferenceQueue();
044            
045            public SoftCache() {
046                    (new SoftCacheCleanupThread(this)).start();
047            }
048            
049            public synchronized boolean isEmpty() {
050                    return iCache.isEmpty();
051            }
052            
053            public synchronized void clear() {
054                    iCache.clear();
055            }
056            
057            public synchronized boolean containsKey(Object key) {
058                    return iCache.containsKey(key);
059            }
060            
061            public synchronized boolean containsValue(Object value) {
062                    for (Iterator i=iCache.values().iterator();i.hasNext();) {
063                            Reference ref = (Reference)i.next();
064                            if (value.equals(ref.get())) return true;
065                    }
066                    return false;
067            }
068            
069            public synchronized Object get(Object key) {
070                    Reference ref = (Reference)iCache.get(key);
071                    return (ref==null?null:ref.get());
072            }
073            public synchronized Object remove(Object key) {
074                    Reference ref = (Reference)iCache.remove(key);
075                    return (ref==null?null:ref.get());
076            }
077            public Object put(Object key, Object value) {
078                    return putReference(key, new SoftReference(value, iQueue));
079            }
080            public Object putSoft(Object key, Object value) {
081                    return putReference(key, new SoftReference(value, iQueue));
082            }
083            public Object putWeak(Object key, Object value) {
084                    return putReference(key, new WeakReference(value, iQueue));
085            }
086            private synchronized Object putReference(Object key, Reference ref) {
087                    Reference old = (Reference)iCache.put(key, ref);
088                    return (old==null?null:old.get());
089            }
090            public void putAll(Map map) {
091                    for (Iterator i=map.entrySet().iterator();i.hasNext();) {
092                            Map.Entry entry = (Map.Entry)i.next();
093                            put(entry.getKey(),entry.getValue());
094                    }
095            }
096            public synchronized int size() {
097                    return iCache.size();
098            }
099            public synchronized Set keySet() { 
100                    return iCache.keySet();
101            }
102            public synchronized Collection values() {
103                    Vector ret = new Vector(iCache.size());
104                    for (Iterator i=iCache.values().iterator();i.hasNext();) {
105                            Reference ref = (Reference)i.next();
106                            Object value = ref.get();
107                            if (value!=null) ret.addElement(value);
108                    }
109                    return ret;
110            }
111            public synchronized Set entrySet() {
112                    HashSet ret = new HashSet(iCache.size());
113                    for (Iterator i=iCache.entrySet().iterator();i.hasNext();) {
114                            Map.Entry entry = (Map.Entry)i.next();
115                            Reference ref = (Reference)entry.getValue();
116                            Object value = ref.get();
117                            if (value!=null) ret.add(new Entry(entry.getKey(),value));
118                            
119                    }
120                    return ret;
121            }
122            public boolean equals(Object o) {
123                    if (o==null || !(o instanceof Map)) return false;
124                    return entrySet().equals(((Map)o).entrySet());
125            }
126            
127            public synchronized void cleanDeallocated() {
128                    int nrCleaned = 0;
129                    for (Iterator i=iCache.entrySet().iterator();i.hasNext();) {
130                            Map.Entry entry = (Map.Entry)i.next();
131                            SoftReference ref = (SoftReference)entry.getValue();
132                            if (ref.get()==null) {
133                                    i.remove();
134                                    nrCleaned++;
135                            }
136                    }
137                    sLogger.debug("cleaned "+nrCleaned+" of "+(iCache.size()+nrCleaned)+" items.");
138            }
139    
140            private ReferenceQueue getQueue() { return iQueue; }
141            
142            private static class SoftCacheCleanupThread extends Thread {
143                    private static Logger sLogger = Logger.getLogger(SoftCacheCleanupThread.class);
144                    private WeakReference iSoftCache = null;
145                    private SoftCacheCleanupThread(SoftCache softCache) {
146                            setDaemon(true);
147                            setName("SoftCacheCleanup");
148                            iSoftCache = new WeakReference(softCache);
149                    }
150                    private ReferenceQueue getQueue() {
151                            SoftCache softCache = (SoftCache)iSoftCache.get();
152                            return (softCache==null?null:softCache.getQueue());
153                    }
154                    private void cleanup() {
155                            SoftCache softCache = (SoftCache)iSoftCache.get();
156                            if (softCache!=null)
157                                    softCache.cleanDeallocated();
158                    }
159                    public void run() {
160                            try {
161                                    while (true) {
162                                            ReferenceQueue q = getQueue();
163                                            if (q==null) break; //soft cache deallocated -- stop the thread
164                                            if (q.remove(10000)==null) continue; //was there something deallocated?
165                                            while (q.poll()!=null); //pull all the deallocated references from the queue
166                                            cleanup(); //clean the cache
167                                    }
168                                    sLogger.debug("cache terminated");
169                            } catch (Exception e) {
170                                    sLogger.error("cleanup thread failed, reason:"+e.getMessage(),e);
171                            }
172                    }
173            }
174            
175            private static class Entry implements Map.Entry {
176                    private Object iKey = null;
177                    private Object iValue = null;
178                    private Entry(Object key, Object value) {
179                            iKey = key; iValue = value;
180                    }
181                    public boolean equals(Object o) {
182                            if (o==null || !(o instanceof Map.Entry)) return false;
183                            Map.Entry e = (Map.Entry)o;
184                            return (getKey()==null?e.getKey()==null:getKey().equals(e.getKey())) &&
185                                            (getValue()==null?e.getValue()==null:getValue().equals(e.getValue()));
186                    }
187                    public int hashCode() {
188                            return (getKey()==null?0:getKey().hashCode()) ^ (getValue()==null?0:getValue().hashCode());
189                    }
190                    public Object getKey() {
191                            return iKey;
192                    }
193                    public Object getValue() {
194                            return iValue;
195                    }
196                    public Object setValue(Object value) throws UnsupportedOperationException{
197                            throw new UnsupportedOperationException();
198                    }
199            }
200            
201            public static void test() {
202                    for (int t=0;t<10;t++) {
203                            SoftCache x = new SoftCache();
204                            for (int i=0;i<1000000;i++)
205                                    x.put(new Integer(i),new byte[1024]);
206                    }
207            }
208    }