com.scireum.open.cache.ManagedCache.java Source code

Java tutorial

Introduction

Here is the source code for com.scireum.open.cache.ManagedCache.java

Source

/**
 * Copyright (c) 2012 scireum GmbH - Andreas Haufler - aha@scireum.de
 *
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.scireum.open.cache;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.map.LRUMap;

import com.scireum.open.statistics.Counter;

class ManagedCache<K, V> implements Cache<K, V> {

    private static final int MAX_HISTORY = 25;
    protected int maxSize;
    protected ValueComputer<K, V> computer;
    protected Map<K, CacheEntry<K, V>> data;
    protected Counter hits = new Counter();
    protected Counter misses = new Counter();
    protected List<Long> usesHistory = new ArrayList<Long>(MAX_HISTORY);
    protected List<Long> hitRateHistory = new ArrayList<Long>(MAX_HISTORY);
    protected Date lastEvictionRun = null;
    protected final String name;
    protected final long timeToLive;
    protected final ValueVerifier<V> verifier;
    private final long verificationInterval;

    @SuppressWarnings("unchecked")
    public ManagedCache(String name, int maxSize, long timeToLive, ValueComputer<K, V> valueComputer,
            ValueVerifier<V> verifier, long verificationInterval) {
        this.name = name;
        this.maxSize = maxSize;
        this.verificationInterval = verificationInterval;
        if (maxSize > 0) {
            this.data = Collections.synchronizedMap(new LRUMap(maxSize));
        } else {
            this.data = Collections.synchronizedMap(new HashMap<K, CacheEntry<K, V>>(maxSize));
        }
        this.timeToLive = timeToLive;
        this.computer = valueComputer;
        this.verifier = verifier;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getMaxSize() {
        return maxSize;
    }

    @Override
    public int getSize() {
        return data.size();
    }

    @Override
    public long getUses() {
        return hits.getCount() + misses.getCount();
    }

    @Override
    public Long getHitRate() {
        long h = hits.getCount();
        long m = misses.getCount();
        return h + m == 0L ? 0L : Math.round(100d * (double) h / (double) (h + m));
    }

    @Override
    public Date getLastEvictionRun() {
        return lastEvictionRun;
    }

    @Override
    public void runEviction() {
        usesHistory.add(getUses());
        if (usesHistory.size() > MAX_HISTORY) {
            usesHistory.remove(0);
        }
        hitRateHistory.add(getHitRate());
        if (hitRateHistory.size() > MAX_HISTORY) {
            hitRateHistory.remove(0);
        }
        hits.reset();
        misses.reset();
        lastEvictionRun = new Date();
        if (timeToLive <= 0) {
            return;
        }
        // Remove all outdated entries...
        long now = System.currentTimeMillis();
        Iterator<Entry<K, CacheEntry<K, V>>> iter = data.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<K, CacheEntry<K, V>> next = iter.next();
            if (next.getValue().getMaxAge() == 0 || next.getValue().getMaxAge() > now) {
                return;
            }
            iter.remove();
        }
    }

    @Override
    public void clear() {
        data.clear();
        misses.reset();
        hits.reset();
        lastEvictionRun = new Date();
    }

    @Override
    public V get(K key) {
        return get(key, this.computer);
    }

    @Override
    public boolean contains(K key) {
        return data.containsKey(key);
    };

    @Override
    public V get(K key, ValueComputer<K, V> computer) {
        long now = System.currentTimeMillis();
        CacheEntry<K, V> entry = data.get(key);
        if (entry != null && entry.getMaxAge() > 0 && entry.getMaxAge() < now) {
            data.remove(key);
            entry = null;
        }
        if (verifier != null && entry != null && verificationInterval > 0 && entry.getNextVerification() < now) {
            if (!verifier.valid(entry.getValue())) {
                entry = null;
            } else {
                entry.setNextVerification(verificationInterval + now);
            }
        }
        if (entry != null) {
            hits.inc();
            entry.getHits().inc();
            return entry.getValue();
        } else {
            misses.inc();
            if (computer != null) {
                V value = computer.compute(key);
                put(key, value);
                return value;
            }
            return null;
        }
    }

    @Override
    public void put(K key, V value) {
        CacheEntry<K, V> cv = new CacheEntry<K, V>(key, value,
                timeToLive > 0 ? timeToLive + System.currentTimeMillis() : 0,
                verificationInterval + System.currentTimeMillis());
        data.put(key, cv);
    }

    @Override
    public void remove(K key) {
        data.remove(key);
    }

    @Override
    public Iterator<K> keySet() {
        return data.keySet().iterator();
    }

    @Override
    public List<CacheEntry<K, V>> getContents() {
        return new ArrayList<CacheEntry<K, V>>(data.values());
    }

    @Override
    public List<Long> getUseHistory() {
        return usesHistory;
    }

    @Override
    public List<Long> getHitRateHistory() {
        return hitRateHistory;
    }

}