1 24 package org.riotfamily.cachius; 25 26 import java.io.File ; 27 import java.io.FileInputStream ; 28 import java.io.FileOutputStream ; 29 import java.io.IOException ; 30 import java.io.InvalidClassException ; 31 import java.io.ObjectInputStream ; 32 import java.io.ObjectOutputStream ; 33 import java.io.Serializable ; 34 import java.util.HashMap ; 35 36 import org.apache.commons.logging.Log; 37 import org.apache.commons.logging.LogFactory; 38 39 44 public final class Cache implements Serializable { 45 46 private static final String CACHE_FILE = "cache-info"; 47 48 private static Log log = LogFactory.getLog(Cache.class); 49 50 private int size; 51 private int capacity; 52 53 private transient CacheItem first; 54 private transient CacheItem last; 55 56 private transient HashMap map; 57 private transient File cacheDir = null; 58 private transient File [] dirs = null; 59 60 private transient int numberOfDirs; 61 private transient int currentDir = 0; 62 63 66 private Cache(int capacity, File cacheDir) { 67 this.size = 0; 68 this.cacheDir = cacheDir; 69 this.map = new HashMap (capacity); 70 setCapacity(capacity); 71 clear(); 72 } 73 74 public File getCacheDir() { 75 return cacheDir; 76 } 77 78 public void setCacheDir(File cacheDir) { 79 if (this.cacheDir != null) { 80 clear(); 81 } 82 this.cacheDir = cacheDir; 83 } 84 85 92 public synchronized CacheItem getItem(String key) { 93 CacheItem item = (CacheItem) map.get(key); 94 if (item == null) { 95 item = newItem(key); 96 } 97 else { 98 touch(item); 99 } 100 return item; 101 } 102 103 107 public synchronized void setCapacity(int capacity) { 108 this.capacity = capacity; 109 numberOfDirs = capacity / 1000; 110 dirs = new File [numberOfDirs]; 111 while (size > capacity) { 112 map.remove(last.getKey()); 113 unlink(last); 114 size--; 115 } 116 } 117 118 122 private void touch(CacheItem item) { 123 unlink(item); 124 link(item); 125 } 126 127 131 private CacheItem newItem(String key) { 132 try { 133 if (capacity == 0) { 134 return null; 135 } 136 log.debug("Creating new cache item for " + key); 137 CacheItem item = createCacheItem(key); 138 139 if (size >= capacity) { 140 log.debug("Maximum size exceeded. Removing: " + last.getKey()); 141 map.remove(last.getKey()); 142 last.delete(); 143 unlink(last); 144 } 145 else { 146 size++; 147 } 148 149 link(item); 150 map.put(key, item); 151 152 return item; 153 } 154 catch (IOException e) { 155 log.error("Error creating item.", e); 156 return null; 157 } 158 } 159 160 163 private void link(CacheItem item) { 164 item.setPrevious(null); 165 item.setNext(first); 166 if (first == null) { 167 last = item; 168 } 169 else { 170 first.setPrevious(item); 171 } 172 173 first = item; 174 } 175 176 179 private void unlink(CacheItem item) { 180 CacheItem previous = item.getPrevious(); 181 CacheItem next = item.getNext(); 182 183 if (previous != null) { 184 previous.setNext(next); 185 } 186 else { 187 first = next; 188 } 189 190 if (next != null) { 191 next.setPrevious(previous); 192 } 193 else { 194 last = previous; 195 } 196 } 197 198 protected File getNextDir() { 199 cacheDir.mkdirs(); 200 if (numberOfDirs <= 1) { 201 return cacheDir; 202 } 203 if (currentDir >= numberOfDirs) { 204 currentDir = 0; 205 } 206 File dir = dirs[currentDir]; 207 if (dir == null) { 208 dir = new File (cacheDir, String.valueOf(currentDir)); 209 dir.mkdir(); 210 dirs[currentDir] = dir; 211 } 212 currentDir++; 213 return dir; 214 } 215 216 public CacheItem createCacheItem(String key) throws IOException { 217 return new CacheItem(key, getNextDir()); 218 } 219 220 private synchronized void clear() { 221 log.info("Removing all cache entries ..."); 222 File [] entries = cacheDir.listFiles(); 223 for (int i = 0; i < entries.length; i++) { 224 deleteFile(entries[i]); 225 } 226 } 227 228 private void deleteFile(File f) { 229 if (f.isDirectory()) { 230 File [] entries = f.listFiles(); 231 for (int i = 0; i < entries.length; i++) { 232 deleteFile(entries[i]); 233 } 234 } 235 f.delete(); 236 } 237 238 public void invalidateTaggedItems(String tag) { 239 log.debug("Invalidating items taged as " + tag); 240 CacheItem item = first; 241 while (item != null) { 242 if (item.hasTag(tag)) { 243 log.debug("Deleting CacheItem " + item.getKey()); 244 item.invalidate(); 245 } 246 item = item.getNext(); 247 } 248 } 249 250 256 public static Cache newInstance(int capacity, File cacheDir, 257 boolean restore) throws IOException { 258 259 if (!cacheDir.exists() && !cacheDir.mkdirs()) { 260 throw new IOException ("Can't create cache directory: " + cacheDir); 261 } 262 File f = new File (cacheDir, CACHE_FILE); 263 if (restore && f.exists()) { 264 log.info("Trying to build cache from file: " + f); 265 try { 266 ObjectInputStream in = new ObjectInputStream ( 267 new FileInputStream (f)); 268 269 Cache cache = (Cache) in.readObject(); 270 in.close(); 271 f.delete(); 272 cache.setCacheDir(cacheDir); 273 cache.setCapacity(capacity); 274 log.info("Cache has been successfully deserialized. " + 275 "Number of items: " + cache.size); 276 277 return cache; 278 } 279 catch (InvalidClassException e) { 280 log.info("Serialized cache has been discarded due to " + 281 "version incompatibilies."); 282 } 283 catch (IOException e) { 284 log.warn("Deserialization failed."); 285 } 286 catch (ClassNotFoundException e) { 287 log.warn("Deserialization failed.", e); 288 } 289 } 290 log.info("Building new cache in: " + cacheDir); 291 return new Cache(capacity, cacheDir); 292 } 293 294 297 public synchronized void persist() { 298 File f = new File (cacheDir, CACHE_FILE); 299 if (!f.exists()) { 300 try { 301 log.info("Persisting the cache state ..."); 302 ObjectOutputStream out = new ObjectOutputStream ( 303 new FileOutputStream (f)); 304 305 out.writeObject(this); 306 out.close(); 307 log.info("Cache state saved in " + f); 308 } 309 catch (IOException e) { 310 log.error("Can't save cache state", e); 311 } 312 } 313 } 314 315 private void writeObject(ObjectOutputStream out) throws IOException { 316 out.defaultWriteObject(); 317 CacheItem item = first; 318 while (item != null) { 319 out.writeObject(item); 320 item = item.getNext(); 321 } 322 } 323 324 private void readObject(ObjectInputStream in) throws IOException , 325 ClassNotFoundException { 326 327 in.defaultReadObject(); 328 map = new HashMap (capacity); 329 if (size > 0) { 330 CacheItem item = (CacheItem) in.readObject(); 331 first = item; 332 CacheItem prev = item; 333 for (int i = 1; i < size; i++) { 334 item = (CacheItem) in.readObject(); 335 item.setPrevious(prev); 336 prev.setNext(item); 337 map.put(item.getKey(), item); 338 prev = item; 339 } 340 last = item; 341 } 342 } 343 344 } 345 | Popular Tags |