001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.cache;
011:
012: import java.util.*;
013:
014: import org.mmbase.util.*;
015: import org.mmbase.cache.implementation.*;
016: import org.mmbase.util.logging.Logger;
017: import org.mmbase.util.logging.Logging;
018:
019: /**
020: * A base class for all Caches. Extend this class for other caches.
021: *
022: * @author Michiel Meeuwissen
023: * @version $Id: Cache.java,v 1.49 2008/02/03 17:33:56 nklasens Exp $
024: */
025: abstract public class Cache<K, V> implements SizeMeasurable, Map<K, V> {
026:
027: private static final Logger log = Logging
028: .getLoggerInstance(Cache.class);
029:
030: private boolean active = true;
031: protected int maxEntrySize = -1; // no maximum/ implementation does not support;
032:
033: /**
034: * @since MMBase-1.8
035: */
036: private CacheImplementationInterface<K, V> implementation;
037:
038: /**
039: * The number of times an element was succesfully retrieved from this cache.
040: */
041: private long hits = 0;
042:
043: /**
044: * The number of times an element could not be retrieved from this cache.
045: */
046: private long misses = 0;
047:
048: /**
049: * The number of times an element was committed to this cache.
050: */
051: private long puts = 0;
052:
053: public Cache(int size) {
054: // See: http://www.mmbase.org/jira/browse/MMB-1486
055: implementation = new LRUCache<K, V>(size);
056: //implementation = new LRUHashtable<K, V>(size);
057:
058: log.service("Creating cache " + getName() + ": "
059: + getDescription());
060: }
061:
062: void setImplementation(String clazz,
063: Map<String, String> configValues) {
064: try {
065: Class<?> clas = Class.forName(clazz);
066: if (implementation == null
067: || (!clas.equals(implementation.getClass()))) {
068: implementation = (CacheImplementationInterface<K, V>) clas
069: .newInstance();
070: implementation.config(configValues);
071: }
072: } catch (ClassNotFoundException cnfe) {
073: log.error("For cache " + this + " "
074: + cnfe.getClass().getName() + ": "
075: + cnfe.getMessage());
076: } catch (InstantiationException ie) {
077: log.error("For cache " + this + " "
078: + ie.getClass().getName() + ": " + ie.getMessage());
079: } catch (IllegalAccessException iae) {
080: log.error("For cache " + this + " "
081: + iae.getClass().getName() + ": "
082: + iae.getMessage());
083: }
084: }
085:
086: /**
087: * Returns a name for this cache type. Default it is the class
088: * name, but this normally will be overriden.
089: */
090: public String getName() {
091: return getClass().getName();
092: }
093:
094: /**
095: * Gives a description for this cache type. This can be used in
096: * cache overviews.
097: */
098: public String getDescription() {
099: return "An all purpose Cache";
100: }
101:
102: /**
103: * Return the maximum entry size for the cache in bytes. If the
104: * cache-type supports it (default no), then no values bigger then
105: * this will be stored in the cache.
106: */
107: public int getMaxEntrySize() {
108: if (getDefaultMaxEntrySize() > 0) {
109: return maxEntrySize;
110: } else {
111: return -1;
112: }
113: }
114:
115: /**
116: * This has to be overridden by Caches which support max entry size.
117: */
118:
119: protected int getDefaultMaxEntrySize() {
120: return -1;
121: }
122:
123: public Set<Map.Entry<K, V>> entrySet() {
124: if (!active)
125: return new HashSet<Map.Entry<K, V>>();
126: return implementation.entrySet();
127: }
128:
129: /**
130: * Checks whether the key object should be cached.
131: * This method returns <code>false</code> if either the current cache is inactive, or the object to cache
132: * has a cache policy associated that prohibits caching of the object.
133: * @param key the object to be cached
134: * @return <code>true</code> if the object can be cached
135: * @since MMBase-1.8
136: */
137: protected boolean checkCachePolicy(Object key) {
138: CachePolicy policy = null;
139: if (active) {
140: if (key instanceof Cacheable) {
141: policy = ((Cacheable) key).getCachePolicy();
142: if (policy != null) {
143: return policy.checkPolicy(key);
144: }
145: }
146: return true;
147: }
148: return false;
149: }
150:
151: /**
152: * Like 'get' of Maps but considers if the cache is active or not, and the cache policy of the key.
153: */
154: public V get(Object key) {
155: if (!checkCachePolicy(key)) {
156: return null;
157: }
158: V res = implementation.get(key);
159: if (res != null) {
160: hits++;
161: } else {
162: misses++;
163: }
164: return res;
165: }
166:
167: /**
168: * Like 'put' of LRUHashtable but considers if the cache is active or not.
169: *
170: */
171: public V put(K key, V value) {
172: if (!checkCachePolicy(key)) {
173: return null;
174: }
175: puts++;
176: return implementation.put(key, value);
177: }
178:
179: /**
180: * Returns the number of times an element was succesfully retrieved
181: * from the table.
182: */
183: public long getHits() {
184: return hits;
185: }
186:
187: /**
188: * Returns the number of times an element cpould not be retrieved
189: * from the table.
190: */
191: public long getMisses() {
192: return misses;
193: }
194:
195: /**
196: * Returns the number of times an element was committed to the table.
197: */
198: public long getPuts() {
199: return puts;
200: }
201:
202: public void setMaxSize(int size) {
203: implementation.setMaxSize(size);
204: }
205:
206: public int maxSize() {
207: return implementation.maxSize();
208: }
209:
210: /**
211: * @see java.util.Map#size()
212: */
213: public int size() {
214: return implementation.size();
215: }
216:
217: public boolean contains(Object key) {
218: return implementation.containsKey(key);
219: }
220:
221: public int getCount(K key) {
222: return implementation.getCount(key);
223: }
224:
225: /**
226: * Returns the ratio of hits and misses.
227: * The higher the ratio, the more succesfull the table retrieval is.
228: * A value of 1 means every attempt to retrieve data is succesfull,
229: * while a value nearing 0 means most times the object requested it is
230: * not available.
231: * Generally a high ratio means the table can be shrunk, while a low ratio
232: * means its size needs to be increased.
233: *
234: * @return A double between 0 and 1 or NaN.
235: */
236: public double getRatio() {
237: return ((double) hits) / (hits + misses);
238: }
239:
240: /**
241: * Returns statistics on this table.
242: * The information shown includes number of accesses, ratio of misses and hits,
243: * current size, and number of puts.
244: */
245: public String getStats() {
246: return "Access " + (hits + misses) + " Ratio " + getRatio()
247: + " Size " + size() + " Puts " + puts;
248: }
249:
250: /**
251: * Sets this cache to active or passive.
252: * TODO: Writing back to caches.xml if necessary (if this call was nog caused by change of caches.xml itself)
253: */
254: public void setActive(boolean a) {
255: active = a;
256: if (!active) {
257: implementation.clear();
258: }
259: // inactive caches cannot contain anything
260: // another option would be to override also the 'contains' methods (which you problable should not use any way)
261: }
262:
263: public String toString() {
264: return "Cache " + getName() + ", Ratio: " + getRatio() + " "
265: + implementation;
266: }
267:
268: /**
269: * Wether this cache is active or not.
270: */
271: public final boolean isActive() {
272: return active;
273: }
274:
275: public int getByteSize() {
276: return getByteSize(new SizeOf());
277: }
278:
279: public int getByteSize(SizeOf sizeof) {
280: int size = 26;
281: if (implementation instanceof SizeMeasurable) {
282: size += ((SizeMeasurable) implementation)
283: .getByteSize(sizeof);
284: } else {
285: // sizeof.sizeof(implementation) does not work because this.equals(implementation)
286: Iterator<Map.Entry<K, V>> i = implementation.entrySet()
287: .iterator();
288: while (i.hasNext()) {
289: Map.Entry<K, V> entry = i.next();
290: size += sizeof.sizeof(entry.getKey());
291: size += sizeof.sizeof(entry.getValue());
292: }
293: }
294: return size;
295: }
296:
297: /**
298: * Returns the sum of bytesizes of every key and value. This may count too much, because objects
299: * (like Nodes) may occur in more then one value, but this is considerably cheaper then {@link
300: * #getByteSize()}, which has to keep a Collection of every counted object.
301: * @since MMBase-1.8
302: */
303: public int getCheapByteSize() {
304: int size = 0;
305: SizeOf sizeof = new SizeOf();
306: Iterator<Map.Entry<K, V>> i = implementation.entrySet()
307: .iterator();
308: while (i.hasNext()) {
309: Map.Entry<K, V> entry = i.next();
310: size += sizeof.sizeof(entry.getKey());
311: size += sizeof.sizeof(entry.getValue());
312: sizeof.clear();
313: }
314: return size;
315: }
316:
317: /**
318: * @see java.util.Map#clear()
319: */
320: public void clear() {
321: implementation.clear();
322: }
323:
324: /**
325: * @see java.util.Map#containsKey(java.lang.Object)
326: */
327: public boolean containsKey(Object key) {
328: return implementation.containsKey(key);
329: }
330:
331: /**
332: * @see java.util.Map#containsValue(java.lang.Object)
333: */
334: public boolean containsValue(Object value) {
335: return implementation.containsValue(value);
336: }
337:
338: /**
339: * @see java.util.Map#equals(java.lang.Object)
340: */
341: public boolean equals(Object o) {
342: // odd, but this is accordinding to javadoc of Map.
343: if (o == this )
344: return true;
345:
346: if (!(o instanceof Cache))
347: return false;
348: Cache<?, ?> c = (Cache<?, ?>) o;
349: if (!c.getName().equals(getName()))
350: return false;
351: ;
352: return implementation.equals(o);
353: }
354:
355: /**
356: * @see java.util.Map#hashCode()
357: */
358: public int hashCode() {
359: int hash = getName().hashCode();
360: hash = HashCodeUtil.hashCode(hash, implementation.hashCode());
361: return hash;
362: }
363:
364: /**
365: * @see java.util.Map#isEmpty()
366: */
367: public boolean isEmpty() {
368: return implementation.isEmpty();
369: }
370:
371: /**
372: * @see java.util.Map#keySet()
373: */
374: public Set<K> keySet() {
375: return implementation.keySet();
376: }
377:
378: /**
379: * @see java.util.Map#putAll(java.util.Map)
380: */
381: public void putAll(Map<? extends K, ? extends V> t) {
382: implementation.putAll(t);
383: }
384:
385: /**
386: * @see java.util.Map#remove(java.lang.Object)
387: */
388: public V remove(Object key) {
389: return implementation.remove(key);
390: }
391:
392: /**
393: * @see java.util.Map#values()
394: */
395: public Collection<V> values() {
396: return implementation.values();
397: }
398:
399: /**
400: * Puts this cache in the caches repository.
401: * @see CacheManager#putCache(Cache)
402: */
403:
404: public Cache<K, V> putCache() {
405: return CacheManager.putCache(this);
406: }
407:
408: }
|