ncache.java
来自「一款Java实现的HTTP代理服务器」· Java 代码 · 共 620 行 · 第 1/2 页
JAVA
620 行
package rabbit.cache;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantReadWriteLock;import java.util.zip.GZIPInputStream;import java.util.zip.GZIPOutputStream;import rabbit.util.Logger;import rabbit.util.SProperties;/** The NCache is like a Map in lookup/insert/delete * The NCache is persistent over sessions (saves itself to disk). * The NCache is selfcleaning, that is it removes old stuff. * * @author <a href="mailto:robo@khelekore.org">Robert Olofsson</a> */public class NCache<K, V> implements Cache<K, V>, Runnable { private static String DIR = "cache"; // standard dir. private static long SIZE = 10 * 1024 * 1024; // 10 MB. private static long CT = 24 * 60 * 60 * 1000; // 1 day. private static String CACHEINDEX = "cache.index"; // the indexfile. private static String TEMPDIR = "temp"; private static int filesperdir = 256; // reasonable? private boolean changed = false; // have we changed? private Thread cleaner = null; // remover of old stuff. private int cleanLoopTime = 60 * 1000; // sleeptime between cleanups. private long maxSize = 0; private long cacheTime = 0; private long fileNo = 0; private long currentSize = 0; private String dir = null; private Map<FiledKey<K>, CacheEntry<K, V>> htab = null; private List<CacheEntry<K, V>> vec = null; private File tempdir = null; private Object dirLock = new Object (); private Logger logger; private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock (); private final Lock r = rwl.readLock (); private final Lock w = rwl.writeLock (); public boolean running = true; /** Create a cache that uses default values. */ public NCache (Logger logger, SProperties props) { this.logger = logger; htab = new HashMap<FiledKey<K>, CacheEntry<K, V>> (); vec = new ArrayList<CacheEntry<K, V>> (); setup (logger, props); cleaner = new Thread (this, getClass ().getName () + ".cleaner"); cleaner.setDaemon (true); cleaner.start (); } public URL getCacheDir () { r.lock (); try { if (dir == null) return null; return new File (dir).toURI ().toURL (); } catch (MalformedURLException e) { return null; } finally { r.unlock (); } } /** Sets the cachedir. This will flush the cache and make * it try to read in the cache from the new dir. * @param newDir the name of the new directory to use. */ public void setCacheDir (String newDir) { w.lock (); try { // save old cachedir. if (dir != null) writeCacheIndex (); // does new dir exist? dir = newDir; File dirtest = new File (dir); if (!dirtest.exists ()) { dirtest.mkdirs (); if (!dirtest.exists ()) { logger.logError ("could not create cachedir"); } } else if (dirtest.isFile ()) { logger.logError ("Cachedir is a file"); } synchronized (dirLock) { tempdir = new File (dir + File.separator + TEMPDIR); if (!tempdir.exists ()) { tempdir.mkdir (); if (!tempdir.exists ()) { logger.logError ("couldl not create cache tempdir"); } } else if (tempdir.isFile ()) { logger.logError ("Cache temp dir is a file"); } } // move to new dir. readCacheIndex (); } finally { w.unlock (); } } /** Get the maximum size for this cache. * @return the maximum size in bytes this cache. */ public long getMaxSize () { return maxSize; } /** Set the maximum size for this cache. * @param newMaxSize the new maximum size for the cache. */ public void setMaxSize (long newMaxSize) { maxSize = newMaxSize; } /** Get the number of miliseconds the cache stores things usually. * This is the standard expiretime for objects, but you can set it for * CacheEntries individially if you want to. * NOTE 1: dont trust that an object will be in the cache this long. * NOTE 2: dont trust that an object will be removed from the cache * when it expires. * @return the number of miliseconds objects are stored normally. */ public long getCacheTime () { return cacheTime; } /** Set the standard expiry-time for CacheEntries * @param newCacheTime the number of miliseconds to keep objects normally. */ public void setCacheTime (long newCacheTime) { cacheTime = newCacheTime; } /** Get how long time the cleaner sleeps between cleanups. */ public int getCleanLoopTime () { return cleanLoopTime; } /** Set how long time the cleaner sleeps between cleanups. * @param newCleanLoopTime the number of miliseconds to sleep. */ public void setCleanLoopTime (int newCleanLoopTime) { cleanLoopTime = newCleanLoopTime; } /** Get the current size of the cache * @return the current size of the cache in bytes. */ public long getCurrentSize () { r.lock (); try { return currentSize; } finally { r.unlock (); } } /** Get the current number of entries in the cache. * @return the current number of entries in the cache. */ public long getNumberOfEntries () { r.lock (); try { return htab.size (); } finally { r.unlock (); } } /** Check that the key really exists. * This may cause Filed data to be read in. */ private boolean checkKey (CacheEntry<K, V> e) { return (e.getKey () != null); } /** Check that the data hook exists. */ private boolean checkHook (CacheEntry<K, V> e) { if (e instanceof NCacheEntry) { NCacheEntry<K, V> ne = (NCacheEntry<K, V>)e; FiledHook<V> hook = ne.getRealDataHook (); if (hook != null && hook instanceof FiledHook) { String entryName = getEntryName (e); File f = new File (entryName + ".hook"); if (!f.exists ()) return false; } // no hook is legal. return true; } return false; } /** Get the CacheEntry assosiated with given object. * @param k the key. * @return the CacheEntry or null (if not found). */ public CacheEntry<K, V> getEntry (K k) { CacheEntry<K, V> ent; r.lock (); try { ent = htab.get (new MemoryKey<K> (k)); } finally { r.unlock (); } if (ent != null) { /* used to check "!checkKey (ent) &&" but that is unneccessary * get will read in data. */ if (!checkHook (ent)) { // bad entry... remove (ent.getKey ()); } } /* If you want to implement LRU or something like that: if (ent != null) ent.setVisited (new Date ()); */ return ent; } /** Get the file name for a real cache entry. */ public String getEntryName (CacheEntry<K, V> ent) { return getEntryName (ent.getId (), true); } /** Get the file name for a cache entry. * @param id the id of the cache entry * @param real false if this is a temporary cache file, * true if it is a realized entry. */ public String getEntryName (long id, boolean real) { StringBuilder sb = new StringBuilder (50); sb.append (dir); sb.append (File.separator); if (!real) { sb.append (TEMPDIR); } else { long fdir = id / filesperdir; sb.append (fdir); } sb.append (File.separator); sb.append (id); return sb.toString (); } /** Reserve space for a CacheEntry with key o. * @param k the key for the CacheEntry. * @return a new CacheEntry initialized for the cache. */ public CacheEntry<K, V> newEntry (K k) { long newId = 0; // allocate the id for the new entry. w.lock (); try { newId = fileNo; fileNo++; } finally { w.unlock (); } // allocate the entry. NCacheEntry<K, V> entry = new NCacheEntry<K, V> (k, newId); entry.setExpires (System.currentTimeMillis () + getCacheTime ()); return entry; } /** Insert a CacheEntry into the cache. * @param ent the CacheEntry to store. */ public void addEntry (CacheEntry<K, V> ent) { if (ent == null) return; File cfile = new File (getEntryName (ent.getId (), false)); if (!cfile.exists()) { return; } long fdir = ent.getId () / filesperdir; File f = new File (dir, "" + fdir);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?