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 }