Cache.java :  » Database-ORM » MMBase » org » mmbase » cache » Java Open Source

Java Open Source » Database ORM » MMBase 
MMBase » org » mmbase » cache » Cache.java
/*

This software is OSI Certified Open Source Software.
OSI Certified is a certification mark of the Open Source Initiative.

The license (Mozilla version 1.0) can be read at the MMBase site.
See http://www.MMBase.org/license

*/
package org.mmbase.cache;

import java.util.*;

import org.mmbase.util.*;
import org.mmbase.cache.implementation.*;
import org.mmbase.util.logging.Logger;
import org.mmbase.util.logging.Logging;

/**
 * A base class for all Caches. Extend this class for other caches.
 *
 * @author Michiel Meeuwissen
 * @version $Id: Cache.java,v 1.49 2008/02/03 17:33:56 nklasens Exp $
 */
abstract public class Cache<K, V> implements SizeMeasurable, Map<K, V> {

    private static final Logger log = Logging.getLoggerInstance(Cache.class);

    private boolean active = true;
    protected int maxEntrySize = -1; // no maximum/ implementation does not support;

    /**
     * @since MMBase-1.8
     */
    private CacheImplementationInterface<K, V> implementation;

    /**
     * The number of times an element was succesfully retrieved from this cache.
     */
    private long hits = 0;

    /**
     * The number of times an element could not be retrieved from this cache.
     */
    private long misses = 0;

    /**
     * The number of times an element was committed to this cache.
     */
    private long puts = 0;

    public Cache(int size) {
        // See: http://www.mmbase.org/jira/browse/MMB-1486
        implementation = new LRUCache<K, V>(size);
        //implementation = new LRUHashtable<K, V>(size);

        log.service("Creating cache " + getName() + ": " + getDescription());
    }

    void setImplementation(String clazz, Map<String,String> configValues) {
        try {
            Class<?> clas = Class.forName(clazz);
            if (implementation == null || (! clas.equals(implementation.getClass()))) {
                implementation = (CacheImplementationInterface<K,V>) clas.newInstance();
                implementation.config(configValues);
            }
        } catch (ClassNotFoundException cnfe) {
            log.error("For cache " + this + " " + cnfe.getClass().getName() + ": " + cnfe.getMessage());
        } catch (InstantiationException ie) {
            log.error("For cache " + this + " " + ie.getClass().getName() + ": " + ie.getMessage());
        } catch (IllegalAccessException iae) {
            log.error("For cache " + this + " " + iae.getClass().getName() + ": " + iae.getMessage());
        }
    }

    /**
     * Returns a name for this cache type. Default it is the class
     * name, but this normally will be overriden.
     */
    public String getName() {
        return getClass().getName();
    }

    /**
     * Gives a description for this cache type. This can be used in
     * cache overviews.
     */
    public String getDescription() {
        return "An all purpose Cache";
    }



    /**
     * Return the maximum entry size for the cache in bytes.  If the
     * cache-type supports it (default no), then no values bigger then
     * this will be stored in the cache.
     */
    public int getMaxEntrySize() {
        if (getDefaultMaxEntrySize() > 0) {
            return maxEntrySize;
        } else {
            return -1;
        }
    }

    /**
     * This has to be overridden by Caches which support max entry size.
     */

    protected int getDefaultMaxEntrySize() {
        return -1;
    }

    public Set<Map.Entry<K,V>> entrySet() {
        if (! active) return new HashSet<Map.Entry<K,V>>();
        return implementation.entrySet();
    }

    /**
     * Checks whether the key object should be cached.
     * This method returns <code>false</code> if either the current cache is inactive, or the object to cache
     * has a cache policy associated that prohibits caching of the object.
     * @param key the object to be cached
     * @return <code>true</code> if the object can be cached
     * @since MMBase-1.8
     */
    protected boolean checkCachePolicy(Object key) {
        CachePolicy policy = null;
        if (active) {
            if (key instanceof Cacheable) {
                policy = ((Cacheable)key).getCachePolicy();
                if (policy != null) {
                    return policy.checkPolicy(key);
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Like 'get' of Maps but considers if the cache is active or not,  and the cache policy of the key.
     */
    public  V get(Object key) {
        if (!checkCachePolicy(key)) {
            return null;
        }
        V res = implementation.get(key);
        if (res != null) {
            hits++;
        } else {
            misses++;
        }
        return res;
    }

    /**
     * Like 'put' of LRUHashtable but considers if the cache is active or not.
     *
     */
    public V put(K key, V value) {
        if (!checkCachePolicy(key)) {
            return null;
        }
        puts++;
        return implementation.put(key, value);
    }

    /**
     * Returns the number of times an element was succesfully retrieved
     * from the table.
     */
    public long getHits() {
        return hits;
    }

    /**
     * Returns the number of times an element cpould not be retrieved
     * from the table.
     */
    public long getMisses() {
        return misses;
    }

    /**
     * Returns the number of times an element was committed to the table.
     */
    public long getPuts() {
        return puts;
    }

    public  void setMaxSize(int size) {
        implementation.setMaxSize(size);
    }
    public  int maxSize() {
        return implementation.maxSize();
    }

    /**
     * @see java.util.Map#size()
     */
    public  int size() {
        return implementation.size();
    }
    public  boolean contains(Object key) {
        return implementation.containsKey(key);
    }

    public int getCount(K key) {
        return implementation.getCount(key);
    }

    /**
     * Returns the ratio of hits and misses.
     * The higher the ratio, the more succesfull the table retrieval is.
     * A value of 1 means every attempt to retrieve data is succesfull,
     * while a value nearing 0 means most times the object requested it is
     * not available.
     * Generally a high ratio means the table can be shrunk, while a low ratio
     * means its size needs to be increased.
     *
     * @return A double between 0 and 1 or NaN.
     */
    public double getRatio() {
        return ((double) hits) / (  hits + misses );
    }


    /**
     * Returns statistics on this table.
     * The information shown includes number of accesses, ratio of misses and hits,
     * current size, and number of puts.
     */
    public String getStats() {
        return "Access "+ (hits + misses) + " Ratio " + getRatio() + " Size " + size() + " Puts " + puts;
    }


    /**
     * Sets this cache to active or passive.
     * TODO: Writing back to caches.xml if necessary (if this call was nog caused by change of caches.xml itself)
     */
    public void setActive(boolean a) {
        active = a;
        if (! active) {
            implementation.clear();
        }
        // inactive caches cannot contain anything
        // another option would be to override also the 'contains' methods (which you problable should not use any way)
    }

    public String toString() {
        return "Cache " + getName() + ", Ratio: " + getRatio() + " " + implementation;
    }

    /**
     * Wether this cache is active or not.
     */
    public final boolean isActive() {
        return active;
    }

    public int getByteSize() {
        return getByteSize(new SizeOf());
    }

    public int getByteSize(SizeOf sizeof) {
        int size = 26;
        if (implementation instanceof SizeMeasurable) {
            size += ((SizeMeasurable) implementation).getByteSize(sizeof);
        } else {
            // sizeof.sizeof(implementation) does not work because this.equals(implementation)
            Iterator<Map.Entry<K, V>> i = implementation.entrySet().iterator();
            while(i.hasNext()) {
                Map.Entry<K, V> entry = i.next();
                size += sizeof.sizeof(entry.getKey());
                size += sizeof.sizeof(entry.getValue());
            }
        }
        return size;
    }

    /**
     * Returns the sum of bytesizes of every key and value. This may count too much, because objects
     * (like Nodes) may occur in more then one value, but this is considerably cheaper then {@link
     * #getByteSize()}, which has to keep a Collection of every counted object.
     * @since MMBase-1.8
     */
    public int getCheapByteSize() {
        int size = 0;
        SizeOf sizeof = new SizeOf();
        Iterator<Map.Entry<K, V>> i = implementation.entrySet().iterator();
        while(i.hasNext()) {
            Map.Entry<K, V> entry = i.next();
            size += sizeof.sizeof(entry.getKey());
            size += sizeof.sizeof(entry.getValue());
            sizeof.clear();
        }
        return size;
    }


    /**
     * @see java.util.Map#clear()
     */
    public void clear() {
        implementation.clear();
    }


    /**
     * @see java.util.Map#containsKey(java.lang.Object)
     */
    public boolean containsKey(Object key) {
        return implementation.containsKey(key);
    }


    /**
     * @see java.util.Map#containsValue(java.lang.Object)
     */
    public boolean containsValue(Object value) {
        return implementation.containsValue(value);
    }


    /**
     * @see java.util.Map#equals(java.lang.Object)
     */
    public boolean equals(Object o) {
        // odd, but this is accordinding to javadoc of Map.
        if (o == this)
            return true;

        if (!(o instanceof Cache))
            return false;
        Cache<?,?> c = (Cache<?,?>) o;
        if (!c.getName().equals(getName()))
            return false;;
        return implementation.equals(o);
    }


    /**
     * @see java.util.Map#hashCode()
     */
    public int hashCode() {
        int hash = getName().hashCode();
        hash = HashCodeUtil.hashCode(hash, implementation.hashCode());
        return hash;
    }


    /**
     * @see java.util.Map#isEmpty()
     */
    public boolean isEmpty() {
        return implementation.isEmpty();
    }


    /**
     * @see java.util.Map#keySet()
     */
    public Set<K> keySet() {
        return implementation.keySet();
    }


    /**
     * @see java.util.Map#putAll(java.util.Map)
     */
    public void putAll(Map<? extends K,? extends V> t) {
        implementation.putAll(t);
    }


    /**
     * @see java.util.Map#remove(java.lang.Object)
     */
    public V remove(Object key) {
        return implementation.remove(key);
    }


    /**
     * @see java.util.Map#values()
     */
    public Collection<V> values() {
        return implementation.values();
    }


    /**
     * Puts this cache in the caches repository.
     * @see CacheManager#putCache(Cache)
     */

    public Cache<K,V> putCache() {
        return CacheManager.putCache(this);
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.