/* Index ECM Engine - A system for managing the capture (when created
* or received), classification (cataloguing), storage, retrieval,
* revision, sharing, reuse and disposition of documents.
*
* Copyright (C) 2008 Regione Piemonte
* Copyright (C) 2008 Provincia di Torino
* Copyright (C) 2008 Comune di Torino
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package it.doqui.index.ecmengine.business.personalization.splitting.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.cache.SimpleCache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class ExpiringCache<K extends Serializable, V> implements SimpleCache<K, V> {
private static long DEFAULT_TIMEOUT = 1000L * 120L; // 120 secondi
private static long DEFAULT_EXPIRE_INTERVAL = 1000L * 30L; // 30 secondi
private static Log logger = LogFactory.getLog(SplittingNodeServiceConstants.ECMENGINE_SPLITTING_LOG_CATEGORY);
private Map<K, ExpiringValue<V>> cacheMap;
private long timeout;
private long expireInterval;
private long lastExpire;
public ExpiringCache(long timeout, long expireInterval) {
this(timeout);
this.expireInterval = (expireInterval > 0L) ? expireInterval : DEFAULT_EXPIRE_INTERVAL;
}
public ExpiringCache(long timeout) {
this();
this.timeout = (timeout > 0L) ? timeout : DEFAULT_TIMEOUT;
}
public ExpiringCache() {
this.timeout = DEFAULT_TIMEOUT;
this.expireInterval = DEFAULT_EXPIRE_INTERVAL;
cacheMap = new HashMap<K, ExpiringValue<V>>(600); // About 450 entries...
}
public void clear() {
cacheMap.clear();
}
public boolean contains(K key) {
if (cacheMap.containsKey(key)) {
return (get(key) != null);
}
return false;
}
public V get(K key) {
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::get] SIZE: " + cacheMap.size());
}
ExpiringValue<V> expiringValue = cacheMap.get(key);
if (expiringValue == null) {
return null;
} else {
V value = expiringValue.get();
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::get] GET: " + key + " -> " + value);
}
return value;
}
}
public Collection<K> getKeys() {
return cacheMap.keySet();
}
public void put(K key, V value) {
if (value == null) {
logger.warn("[ExpiringCache::put] Attempt to add null value ignored. Key: " + key);
return;
}
ExpiringValue<V> expiringValue = new ExpiringValue<V>(timeout);
expiringValue.put(value);
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::put] PUT: " + key + " -> " + value);
}
cacheMap.put(key, expiringValue);
cleanup(); // Cleanup cache after every put
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::put] SIZE: " + cacheMap.size());
}
}
public void remove(K key) {
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::remove] REM: " + key);
}
cacheMap.remove(key);
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::remove] SIZE: " + cacheMap.size());
}
}
private void cleanup() {
if (lastExpire + expireInterval > System.currentTimeMillis()) {
return;
}
List<K> toRemove = new ArrayList<K>();
for (Map.Entry<K, ExpiringValue<V>> entry : cacheMap.entrySet()) {
if (entry.getValue().isExpired()) {
toRemove.add(entry.getKey());
}
}
for (K key : toRemove) {
cacheMap.remove(key);
}
lastExpire = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("[ExpiringCache::cleanup] EXP: " + toRemove.size());
}
return;
}
private class ExpiringValue<T> {
private long timeout;
private long snapshot = 0;
private T value;
public ExpiringValue(long timeout) {
this.timeout = timeout;
}
public void put(T value) {
this.value = value;
this.snapshot = System.currentTimeMillis();
}
public T get() {
this.snapshot = System.currentTimeMillis(); // renew
return this.value;
}
public boolean isExpired() {
return (snapshot + timeout < System.currentTimeMillis());
}
/**
* Clear the cache value
*/
public void clear() {
this.value = null;
}
}
}