MultiCache.java
Home
/
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
cache /
MultiCache.java
/*
* 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.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @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, Optional<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);
Optional<V> optional = Optional.ofNullable(value);
this.map.put(key, optional);
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) {
Optional<V> optional = this.map.get(key);
if (optional == null) {
this.cachePerformance.registerEvent(EventType.MISS);
return null;
}
V get = optional.orElse(null);
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();
}
Optional<V> optional = this.map.remove(key);
if (optional != null) {
V remove = optional.orElse(null);
String identifier = getIdentifier(remove);
if (CACHE_REGISTER_SIZE) {
this.cachePerformance.registerEvent(EventType.INVALIDATION, identifier, CachePerformance.calculateObjectSize(remove));
} else {
this.cachePerformance.registerEvent(EventType.INVALIDATION, identifier);
}
}
}
private String getIdentifier(V value) {
return value != null ? String.valueOf(value.hashCode()) : "null";
}
}