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 + -
显示快捷键?