001: package net.suberic.util.cache;
002:
003: import java.util.*;
004:
005: /**
006: * This is a simple implementation of a sized cache. It uses
007: * SizedCacheEntry objects to store wrap its values and to keep track
008: * the items' sizes and last accessed times.
009: */
010: public abstract class AbstractSizedCache {
011:
012: protected HashMap cacheTable = new HashMap();
013:
014: protected LinkedList sortedKeys = new LinkedList();
015:
016: protected long size = 0;
017:
018: private long maxSize = 0;
019:
020: private long maxEntrySize = 0;
021:
022: private SizedCacheEntryFactory factory = null;
023:
024: /**
025: * Adds an object to the Cache.
026: */
027: public synchronized void add(Object key, Object value) {
028: if (value == null) {
029: invalidateCache(key);
030: } else {
031: SizedCacheEntry origEntry = (SizedCacheEntry) cacheTable
032: .get(key);
033: if (origEntry == null || !origEntry.equals(value)) {
034: invalidateCache(key);
035:
036: SizedCacheEntry sce = getFactory().createCacheEntry(
037: value);
038:
039: if (getMaxEntrySize() >= 0
040: && sce.getSize() < getMaxEntrySize()) {
041: if (getMaxSize() >= 0) {
042: while (sce.getSize() + getSize() > getMaxSize()) {
043: // keep removing the last entry in the table until there's
044: // enough space.
045: Object lastAccessedKey = sortedKeys
046: .getLast();
047: invalidateCache(lastAccessedKey);
048: }
049: }
050: cacheTable.put(key, sce);
051: size += sce.getSize();
052: reorderEntry(key);
053: }
054: }
055: }
056: }
057:
058: /**
059: * Gets an object from the Cache. If the object is not available,
060: * returns null.
061: */
062: public synchronized Object get(Object key) {
063: SizedCacheEntry sce = (SizedCacheEntry) cacheTable.get(key);
064: if (sce != null) {
065: Object returnValue = sce.getCachedValue();
066: sce.touchEntry();
067: reorderEntry(sce);
068: return returnValue;
069: } else
070: return null;
071: }
072:
073: /**
074: * Removes an object from the Cache.
075: */
076: public synchronized void invalidateCache(Object key) {
077: SizedCacheEntry value = (SizedCacheEntry) cacheTable.get(key);
078: if (value != null) {
079: size -= value.getSize();
080: value.removeFromCache();
081: }
082:
083: sortedKeys.remove(key);
084: cacheTable.remove(key);
085: }
086:
087: /**
088: * Invalidates the entire cache.
089: */
090: public synchronized void invalidateCache() {
091: Iterator it = cacheTable.keySet().iterator();
092: while (it.hasNext()) {
093: Object key = it.next();
094: SizedCacheEntry entry = (SizedCacheEntry) cacheTable
095: .get(key);
096: if (entry != null) {
097: entry.removeFromCache();
098: }
099: cacheTable.remove(key);
100: }
101: }
102:
103: /**
104: * Reorders the entry in the sortedKeys list.
105: */
106: private void reorderEntry(Object key) {
107: sortedKeys.remove(key);
108: sortedKeys.addFirst(key);
109: }
110:
111: /**
112: * Gets the size limit for the cache.
113: */
114: public long getMaxSize() {
115: return maxSize;
116: }
117:
118: /**
119: * Sets the size limit for the cache.
120: */
121: public void setMaxSize(long newSize) {
122: maxSize = newSize;
123: }
124:
125: /**
126: * Gets the size limit for individual entries in the cache.
127: */
128: public long getMaxEntrySize() {
129: return maxEntrySize;
130: }
131:
132: /**
133: * Sets the size limit for individual entries in the cache.
134: */
135: public void setMaxEntrySize(long newSize) {
136: maxEntrySize = newSize;
137: }
138:
139: /**
140: * Gets the current size of the cache.
141: */
142: public long getSize() {
143: return size;
144: }
145:
146: /**
147: * Gets the SizedCacheEntryFactory for this cache.
148: */
149: public SizedCacheEntryFactory getFactory() {
150: return factory;
151: }
152:
153: /**
154: * Sets the SizedCacheEntryFactory for this cache.
155: */
156: public void setFactory(SizedCacheEntryFactory newFactory) {
157: if (newFactory != null) {
158: factory = newFactory;
159: }
160: }
161: }
|