1 8 package org.ozoneDB.core.storage; 9 10 import java.util.Collection ; 11 import java.util.Comparator ; 12 import java.util.HashMap ; 13 import java.util.Iterator ; 14 import java.util.LinkedHashMap ; 15 import java.util.LinkedHashSet ; 16 import java.util.LinkedList ; 17 import java.util.List ; 18 import java.util.Map ; 19 import java.util.Properties ; 20 import java.util.Set ; 21 import java.util.Map.Entry; 22 import java.util.SortedSet ; 23 import java.util.TreeMap ; 24 import java.util.TreeSet ; 25 import java.util.logging.Level ; 26 import java.util.logging.Logger ; 27 import org.ozoneDB.OzoneInternalException; 28 import org.ozoneDB.core.ConfigurationException; 29 30 31 48 public class DelayCache extends AbstractTrimmingCache implements PropertyConfigurable { 49 50 private static final Logger log = Logger.getLogger(DelayCache.class.getName()); 51 52 public static final PropertyInfo DELAY = new PropertyInfo( 53 ".delay", 54 "int", 55 "1000", 56 "time (in ms) that an object can stay in this cache before may be thrown out", 57 new String [] {"100", "10000"} 58 ); 59 60 public static final PropertyInfo MINSLEEPTIME = new PropertyInfo( 61 ".minSleepTime", 62 "int", 63 "100", 64 "time (in ms) that the thread that does the actual removing will wait at least", 65 new String [] {"100", "10000"} 66 ); 67 68 public static final PropertyInfo INITIALCAPACITY = new PropertyInfo( 69 ".initialCapacity", 70 "int", 71 "1", 72 "number of objects that this cache reserves space for initially", 73 new String [] {"100", "100000"} 74 ); 75 76 public static final PropertyInfo LOADFACTOR = new PropertyInfo( 77 ".loadFactor", 78 "float, in range <0, 1>", 79 "0.75", 80 "load factor for this cache; see java.util.LinkedHashMap(int initialCapacity, float loadFactor)", 81 new String [] {"0.5", "0.95"} 82 ); 83 84 private int delay; 85 private int minSleepTime; 86 private Map keysToEntries; 87 private SortedSet entries; 88 private TrimThread trimThread; 89 90 private volatile long sleepUntil; 91 private volatile long wakingUpAt; 92 93 public DelayCache(Properties properties, String prefix) { 94 super(properties, prefix); 95 entries = new TreeSet (); 96 try { 97 float loadFactor = Float.parseFloat(properties.getProperty(prefix + LOADFACTOR.getKey(), LOADFACTOR.getDefaultValue())); 98 log.config(getPrefix() + " using a load factory of " + loadFactor); 99 int initialCapacity = Integer.parseInt(properties.getProperty(prefix + INITIALCAPACITY.getKey(), INITIALCAPACITY.getDefaultValue())); 100 log.config(getPrefix() + " using an initial capacity of " + initialCapacity); 101 keysToEntries = new LinkedHashMap (initialCapacity, loadFactor); 102 setDelay(Integer.parseInt(properties.getProperty(getPrefix() + DELAY.getKey(), DELAY.getDefaultValue()))); 103 log.config(getPrefix() + " using a delay of " + getDelay()); 104 setMinSleepTime(Integer.parseInt(properties.getProperty(getPrefix() + MINSLEEPTIME.getKey(), MINSLEEPTIME.getDefaultValue()))); 105 log.config(getPrefix() + " using a minimum sleep time of " + getMinSleepTime()); 106 } catch (NumberFormatException e) { 107 throw new ConfigurationException(e); 108 } 109 trimThread = new TrimThread(); 110 trimThread.start(); 111 } 112 113 protected final Map getKeysToEntries() { 114 return keysToEntries; 115 } 116 117 protected final SortedSet getEntries() { 118 return entries; 119 } 120 121 public Collection getPropertyInfos() { 122 Collection result = new LinkedList (); 123 result.add(INITIALCAPACITY); 124 result.add(DELAY); 125 result.add(LOADFACTOR); 126 result.add(MINSLEEPTIME); 127 return result; 128 } 129 130 public Object get(Object key) { 131 synchronized(getSynchronizer()) { 132 TimedEntry entry = (TimedEntry) getKeysToEntries().get(key); 133 return entry == null ? null : entry.value; 134 } 135 } 136 137 public void put(Object key, Object value) { 138 synchronized(getSynchronizer()) { 139 TimedEntry entry = (TimedEntry) getKeysToEntries().get(key); 140 if (entry != null) { 141 getEntries().remove(entry); 142 entry.touch(); 143 } else { 144 entry = new TimedEntry((Comparable ) key, value); 145 getKeysToEntries().put(key, entry); 146 } 147 getEntries().add(entry); 148 } 149 } 150 151 public Object remove(Object key) { 152 synchronized(getSynchronizer()) { 153 TimedEntry entry = (TimedEntry) getKeysToEntries().remove(key); 154 getEntries().remove(entry); 155 return entry == null ? null : entry.value; 156 } 157 } 158 159 private void trim() { 160 long now = System.currentTimeMillis(); 161 while (size() > 0) { 162 TimedEntry first = (TimedEntry) getEntries().first(); 163 if (now > first.time + getDelay()) { 164 getEntries().remove(first); 166 if (getTrimHandler() != null) { 167 getTrimHandler().trimming(first.key, first.value); 168 } 169 getKeysToEntries().remove(first.key); 170 } else { 171 if (first.time + getDelay() > sleepUntil) { 173 sleepUntil = first.time + getDelay(); 174 } else { 175 sleepUntil = 0; 176 } 177 break; 178 } 179 } 180 } 181 182 public int size() { 183 synchronized(getSynchronizer()) { 184 return getKeysToEntries().size(); 185 } 186 } 187 188 public Map copyToMap() { 189 synchronized(getSynchronizer()) { 190 HashMap result = new HashMap (size()); 191 for (Iterator i = getEntries().iterator(); i.hasNext(); ) { 192 TimedEntry entry = (TimedEntry) i.next(); 193 result.put(entry.key, entry.value); 194 } 195 return result; 196 } 197 } 198 199 public final int getDelay() { 200 return delay; 202 } 203 204 public final void setDelay(int delay) { 205 synchronized(getSynchronizer()) { 206 this.delay = delay; 207 trim(); 208 } 209 } 210 211 public final int getMinSleepTime() { 212 return minSleepTime; 214 } 215 216 public final void setMinSleepTime(int minSleepTime) { 217 synchronized(getSynchronizer()) { 218 this.minSleepTime = minSleepTime; 219 trim(); 220 } 221 } 222 223 protected final class TimedEntry implements Comparable { 224 225 public long time; 226 public Comparable key; 227 public Object value; 228 229 public TimedEntry(Comparable key, Object value) { 230 this.key = key; 231 this.value = value; 232 touch(); 233 } 234 235 public int compareTo(Object o) { 236 TimedEntry other = (TimedEntry) o; 237 int result = (int) (time - other.time); 238 return result != 0 ? result : key.compareTo(other.key); 239 } 240 241 public boolean equals(Object o) { 242 return compareTo(o) == 0; 243 } 244 245 public void touch() { 246 time = System.currentTimeMillis(); 247 } 248 } 249 250 private class TrimThread extends Thread { 251 252 public TrimThread() { 253 super("delayed trimmer"); 254 setDaemon(true); 255 } 256 257 public void run() { 258 synchronized(getSynchronizer()) { 259 for (;;) { 260 long now = System.currentTimeMillis(); 261 try { 262 getSynchronizer().wait(Math.max(sleepUntil - now, getMinSleepTime())); 263 } catch (InterruptedException ignore) { 264 } 265 trim(); 266 } 267 } 268 } 269 } 270 271 } 272 | Popular Tags |