Java tutorial
/** * Copyright 2010-2011 Nicholas Blair, Eric Dalquist * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.googlecode.ehcache.annotations; import java.io.Serializable; import java.util.Timer; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import net.sf.ehcache.Status; import net.sf.ehcache.constructs.blocking.CacheEntryFactory; import net.sf.ehcache.constructs.blocking.SelfPopulatingCache; import net.sf.ehcache.event.CacheManagerEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.task.TaskExecutor; import com.google.common.collect.MapMaker; import com.googlecode.ehcache.annotations.support.TaskSchedulerAdapter; import com.googlecode.ehcache.annotations.support.TimerTaskSchedulerAdapter; /** * Extension of SelfPopulatingCache that schedules a periodic call of {@link #refresh()} via the specified * {@link TaskSchedulerAdapter}. Also overrides {@link #refreshElement(Element, Ehcache, boolean)} to allow for * asynchronous refresh of elements iff an {@link Executor} was provided to the constructor. */ public class RefreshingSelfPopulatingCache extends SelfPopulatingCache { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); //Use a weak key concurrent map to track async refreshes without danger of memory leaks private final ConcurrentMap<Serializable, Long> refreshQueue = new MapMaker().weakKeys().makeMap(); private final TaskSchedulerAdapter scheduler; private final TaskExecutor executer; private final long refreshInterval; public RefreshingSelfPopulatingCache(Ehcache cache, CacheEntryFactory cacheEntryFactory, TaskSchedulerAdapter scheduler, TaskExecutor executer, long refreshInterval) { super(cache, cacheEntryFactory); final Timer timer; if (scheduler == null) { timer = new Timer(cache.getName() + " Cache Refresh Timer", true); this.scheduler = new TimerTaskSchedulerAdapter(timer); } else { timer = null; this.scheduler = scheduler; } this.executer = executer; this.refreshInterval = refreshInterval; this.scheduleRefreshTask(); //Register a listener with the cache manager to make sure we clear out our timer thread cleanly this.getCacheManager().setCacheManagerEventListener(new CacheManagerEventListener() { public void notifyCacheRemoved(String cacheName) { } public void notifyCacheAdded(String cacheName) { } public void init() throws CacheException { } public Status getStatus() { return null; } public void dispose() throws CacheException { if (timer != null) { timer.cancel(); } } }); } public long getRefreshInterval() { return refreshInterval; } protected void scheduleRefreshTask() { scheduler.scheduleAtFixedRate(new Runnable() { public void run() { try { refresh(); } catch (Throwable t) { logger.error( "An exception was thrown while refreshing the cache. Review the previous log statements for errors related to individual cache entries.", t); } } }, refreshInterval); } @Override protected Element refreshElement(final Element element, final Ehcache backingCache, final boolean quiet) throws Exception { final Serializable key = element.getKey(); //For refresh track that the element is being refreshed so we don't queue up multiple refresh attempts final long now = System.currentTimeMillis(); final Long existingRefreshStart = this.refreshQueue.putIfAbsent(key, now); if (existingRefreshStart != null) { this.logger.warn( "Key {} in cache {} is already being refreshed started {}ms ago, it will be skipped for this refresh iteration.", new Object[] { key, backingCache.getName(), now - existingRefreshStart }); return null; } boolean clearRefreshFlag = true; try { if (element.isExpired()) { this.logger.debug("Element for key {} has expired, this key will not be refreshed", key); return null; } //Check if the element is old enough to try refreshing it final long age = now - element.getLatestOfCreationAndUpdateTime(); if (age < refreshInterval) { this.logger.debug( "Element for key {} is only {}ms old and will not be refreshed. Refresh age is {}", new Object[] { key, age, refreshInterval }); return null; } final Object value = element.getObjectValue(); if (!(value instanceof RefreshableCacheEntry)) { this.logger.warn( "RefreshingSelfPopulatingCache contains an entry which is not a RefreshableCacheEntry for key {} this entry will be ignored during refresh.", key); return null; } //If no executor refresh synchronously if (this.executer == null) { super.refreshElement(element, backingCache, quiet); } //If executor refresh via thread-pool else { clearRefreshFlag = false; //Submit the refresh task to the executor executer.execute(new Runnable() { public void run() { try { RefreshingSelfPopulatingCache.super.refreshElement(element, backingCache, quiet); } catch (Throwable e) { logger.error("An exception was thrown while refreshing the ca", e); } finally { //Clear out the refresh tracker reference refreshQueue.remove(key); } } }); } } catch (final Exception e) { this.logger.warn("An exception was thrown while refreshing the cache for " + element + ". This element may not have been refreshed", e); } finally { //Clear out the refresh tracker reference if sync refresh was done if (clearRefreshFlag) { refreshQueue.remove(key); } } return element; } }