
com.googlecode.ehcache.annotations.RefreshingSelfPopulatingCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache-spring-annotations Show documentation
Show all versions of ehcache-spring-annotations Show documentation
Provides a simple model for integrating Ehcache in a Spring project via annotations.
The newest version!
/**
* 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 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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy