MultiCache.java

128 lines | 4.034 kB Blame History Raw Download
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufrgs.inf.prosoft.cache;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 *
 * @author romulo
 * @param <K>
 * @param <V>
 */
public class MultiCache<K, V> implements Cache<K, V> {

    private static final boolean CACHE_REGISTER_SIZE = System.getenv("CACHE_REGISTER_SIZE") != null && System.getenv("CACHE_REGISTER_SIZE").equals("true");
    private final HashMap<K, V> map;
    private final HashMap<K, Thread> keyHasTTL;
    private final CachePerformance cachePerformance;
    private static final Logger LOGGER = Logger.getLogger(Cache.class.getName());

    public MultiCache() {
        this(new CachePerformance());
    }

    public MultiCache(String name) {
        this(new CachePerformance(name));
    }

    public MultiCache(CachePerformance cachingPerformance) {
        this.cachePerformance = cachingPerformance;
        this.map = new HashMap<>();
        this.keyHasTTL = new HashMap<>();
    }

    public CachePerformance getCachePerformance() {
        return this.cachePerformance;
    }

    @Override
    public void put(K key, V value, long timeToLive) {
        put(key, value);
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(timeToLive);
                invalidate(key);
            } catch (InterruptedException ex) {
                LOGGER.log(Level.WARNING, "interrupted time to live");
            }
        });
        this.keyHasTTL.put(key, thread);
        thread.start();
    }

    @Override
    public void put(K key, V value) {
        invalidate(key);
        this.map.put(key, value);
        String identifier = getIdentifier(value);
        if (CACHE_REGISTER_SIZE) {
            this.cachePerformance.registerEvent(EventType.ADDITION, identifier, CachePerformance.calculateObjectSize(value));
        } else {
            this.cachePerformance.registerEvent(EventType.ADDITION, identifier);
        }
        this.cachePerformance.registerSize(this.map.size());
    }

    @Override
    public V get(K key) throws KeyNotFoundException {
        if (!this.map.containsKey(key)) {
            this.cachePerformance.registerEvent(EventType.MISS);
            throw new KeyNotFoundException();
        }
        V get = this.map.get(key);
        String identifier = getIdentifier(get);
        if (CACHE_REGISTER_SIZE) {
            this.cachePerformance.registerEvent(EventType.HIT, identifier, CachePerformance.calculateObjectSize(get));
        } else {
            this.cachePerformance.registerEvent(EventType.HIT, identifier);
        }
        return get;
    }

    @Override
    public void invalidate(K key) {
        Thread timeToLiveThread = this.keyHasTTL.remove(key);
        if (timeToLiveThread != null) {
            timeToLiveThread.interrupt();
        }
        if (this.map.containsKey(key)) {
            V remove = this.map.remove(key);
            String identifier = getIdentifier(remove);
            if (CACHE_REGISTER_SIZE) {
                this.cachePerformance.registerEvent(EventType.INVALIDATION, identifier, CachePerformance.calculateObjectSize(remove));
            } else {
                this.cachePerformance.registerEvent(EventType.INVALIDATION, identifier);
            }
        }
    }

    public Set<Map.Entry<K, V>> entrySet() {
        return this.map.entrySet().stream()
                .collect(Collectors.
                        toMap(entry -> entry.getKey(), entry -> entry.getValue())
                ).entrySet();
    }

    public List<V> values() {
        return this.map.values().stream().collect(Collectors.toList());
    }

    public boolean containsKey(K key) {
        return this.map.containsKey(key);
    }

    private String getIdentifier(V value) {
        return value != null ? String.valueOf(value.hashCode()) : "null";
    }
}