package org.sakaiproject.calendar.impl; import java.util.List; import java.util.Timer; import java.util.TimerTask; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This class scans an EhCache for items and refreshes them. * It uses the last update value to determine if the item in the cache needs updating. * When the new item it put back into the cache it keeps the original insertion date and * the last access date to that expiration works correctly. * * So we have the TTL of the cache * <p> * We want to use get/put on the cache so that if we were replicating the cache any changes in the cached * object would have the opportunity to be replicated across the cache. * * @author buckett * */ public class EhCacheRefresh { private static final Log LOG = LogFactory.getLog(EhCacheRefresh.class); // The EhCache we want to refresh private Cache cache; // The class that does the refreshing of the elements in the cache. private ElementRefresher refresher; // The timer that we schedule our refreshes on. private Timer timer; // Default is to refresh the items every hour private int updateInterval = 1000 * 60 * 60; private int minAge = 0; public void init() { if (minAge == 0) { minAge = updateInterval; } // Creates our timer TimerTask task = new TimerTask(){ @Override public void run() { try { refresh(); } catch (Exception e) { LOG.error("Problem refreshing the cache: "+ cache.getName(), e); } } }; // Run is every so often timer.schedule(task, 0, updateInterval); } public void setCache(Cache cache) { this.cache = cache; } public void setRefresher(ElementRefresher refresher) { this.refresher = refresher; } public void setTimer(Timer timer) { this.timer = timer; } /** * @param updateInterval The update interval in minutes. */ public void setUpdateInterval(int updateInterval) { if (!(updateInterval > 0)) { throw new IllegalArgumentException("Update interval must be greater than 0."); } this.updateInterval = updateInterval * 1000 * 60; } /** * This defaults to the updateInterval is not set. * @param minAge Minimum age of an item in the cache before it is updated. Specified in milliseconds. */ public void setMinAge(int minAge) { this.minAge = minAge; } /** * This checks the elements in the cache and any for any that are older than the update interval * we refresh them. */ public void refresh() { List<?> keys = cache.getKeysWithExpiryCheck(); int updated = 0; for (Object key : keys) { Element element = cache.getQuiet(key); // May have expired since our copy of the keys was taken. if (element != null) { long refreshElementOlder = System.currentTimeMillis() - minAge; if (element.getLatestOfCreationAndUpdateTime() < refreshElementOlder) { Object replacement = refresher.updateElement( element.getKey(), element.getObjectValue()); if (replacement != null) { // This has a threading issue in that someone else may update the entry // details while we are updating it and so we could lose a change. Element newElement = new Element(element.getKey(), replacement, element.getVersion(), element.getCreationTime(), element.getLastAccessTime(), element.getLastUpdateTime(), element.getHitCount()); newElement.updateUpdateStatistics(); // We don't use put() as it would reset all the cache statistics which // we want to maintain. cache.putQuiet(newElement); updated++; if (LOG.isDebugEnabled()) { LOG.debug("Updated "+ element.getKey()+ " to "+ replacement + " in cache "+ cache.getName()); } } else { cache.remove(key); if (LOG.isDebugEnabled()) { LOG.debug("Removed "+ key+ " as replacement was null from cache "+ cache.getName()); } } } } } LOG.info("Refreshed "+ updated+ " of "+ keys.size()+ " in cache "+ cache.getName()); } }