/*
* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.oscache.base.algorithm;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infoglue.deliver.cache.PageCacheHelper;
import java.util.*;
//import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* <p>LRU (Least Recently Used) algorithm for the cache.</p>
*
* <p>Since release 2.3 this class requires Java 1.4
* to use the <code>LinkedHashSet</code>. Use prior OSCache release which
* require the Jakarta commons-collections <code>SequencedHashMap</code>
* class or the <code>LinkedList</code> class if neither of the above
* classes are available.</p>
*
* <p>No synchronization is required in this class since the
* <code>AbstractConcurrentReadCache</code> already takes care of any
* synchronization requirements.</p>
*
* @version $Revision: 1.2.4.4 $
* @author <a href="mailto:salaman@teknos.com">Victor Salaman</a>
* @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
* @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
* @author <a href="mailto:chris@swebtec.com">Chris Miller</a>
*/
public class ImprovedLRUCache extends AbstractConcurrentReadCache //implements Runnable
{
private static final Log log = LogFactory.getLog(ImprovedLRUCache.class);
//private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private boolean isPageCache = false;
/**
* Cache queue containing all cache keys.
*/
protected Collection list = new LinkedHashSet();
/**
* A flag indicating whether there is a removal operation in progress.
*/
private volatile boolean removeInProgress = false;
/**
* Constructs an LRU Cache.
*/
public ImprovedLRUCache() {
super();
}
/**
* Constructors a LRU Cache of the specified capacity.
*
* @param capacity The maximum cache capacity.
*/
public ImprovedLRUCache(int capacity) {
this();
maxEntries = capacity;
}
/**
* An item was retrieved from the list. The LRU implementation moves
* the retrieved item's key to the front of the list.
*
* @param key The cache key of the item that was retrieved.
*/
protected void itemRetrieved(Object key) {
// Prevent list operations during remove
while (removeInProgress) {
try {
Thread.sleep(5);
} catch (InterruptedException ie) {
}
}
// We need to synchronize here because AbstractConcurrentReadCache
// doesn't prevent multiple threads from calling this method simultaneously.
synchronized (list) {
list.remove(key);
list.add(key);
}
/*
boolean lockSuccess = false;
try {
lockSuccess = lock.writeLock().tryLock(50, TimeUnit.MILLISECONDS);
} catch (InterruptedException e1) {
return;
}
try
{
if(!lockSuccess)
{
RequestAnalyser.getRequestAnalyser().registerComponentStatistics("Returned in ImprovedLRUCache because of lock timeout...", 1);
return;
}
list.remove(key);
list.add(key);
}
finally
{
if(lockSuccess)
lock.writeLock().unlock();
}
*/
}
/**
* An object was put in the cache. This implementation adds/moves the
* key to the end of the list.
*
* @param key The cache key of the item that was put.
*/
protected void itemPut(Object key) {
// Since this entry was just accessed, move it to the back of the list.
synchronized (list) { // A further fix for CACHE-44
list.remove(key);
list.add(key);
}
/*
boolean lockSuccess = false;
try {
lockSuccess = lock.writeLock().tryLock(50, TimeUnit.MILLISECONDS);
} catch (InterruptedException e1) {
return;
}
try
{
if(!lockSuccess)
{
RequestAnalyser.getRequestAnalyser().registerComponentStatistics("Returned in ImprovedLRUCache because of lock timeout...", 1);
return;
}
list.remove(key);
list.add(key);
}
finally
{
if(lockSuccess)
lock.writeLock().unlock();
}
*/
}
/**
* An item needs to be removed from the cache. The LRU implementation
* removes the first element in the list (ie, the item that was least-recently
* accessed).
*
* @return The key of whichever item was removed.
*/
protected Object removeItem() {
//logger.info("this.getGroupsForReading().size():" + this.getGroupsForReading().size());
/*
if(this.getGroupsForReading().size() > maxGroups)
maxGroups = this.getGroupsForReading().size();
*/
Object toRemove = null;
removeInProgress = true;
try {
while (toRemove == null) {
try {
toRemove = removeFirst();
} catch (Exception e) {
// List is empty.
// this is theorically possible if we have more than the size concurrent
// thread in getItem. Remove completed but add not done yet.
// We simply wait for add to complete.
do {
try {
Thread.sleep(5);
} catch (InterruptedException ie) {
}
} while (list.isEmpty());
}
}
} finally {
removeInProgress = false;
}
return toRemove;
}
/**
* Remove specified key since that object has been removed from the cache.
*
* @param key The cache key of the item that was removed.
*/
protected void itemRemoved(Object key)
{
if(isPageCache && key != null)
{
//Object fileName = this.get(key);
//if(fileName != null)
PageCacheHelper.getInstance().notifyKey(""+key);
//else
// System.out.println("No filename found for : " + key);
//PageCacheHelper.getInstance().clearPageCacheInThread("" + key);
}
list.remove(key);
if(size() == 0)
{
groups.clear();
this.list.clear();
}
}
/**
* Removes the first object from the list of keys.
*
* @return the object that was removed
*/
private Object removeFirst() {
Object toRemove = null;
synchronized (list) { // A further fix for CACHE-44 and CACHE-246
Iterator it = list.iterator();
toRemove = it.next();
it.remove();
this.remove(toRemove);
}
/*
boolean lockSuccess = false;
try {
lockSuccess = lock.writeLock().tryLock(50, TimeUnit.MILLISECONDS);
} catch (InterruptedException e1) {
return null;
}
try
{
if(!lockSuccess)
{
RequestAnalyser.getRequestAnalyser().registerComponentStatistics("Returned in ImprovedLRUCache.removeFirst because of lock timeout...", 1);
return null;
}
Iterator it = list.iterator();
toRemove = it.next();
it.remove();
this.remove(toRemove);
if(maxGroups > 1000 && maxGroups > (this.getGroupsForReading().size() * 5))
{
System.out.println("The group map must be made smaller:" + maxGroups);
HashMap newGroups = new HashMap();
newGroups.putAll(groups);
groups = newGroups;
maxGroups = 0;
}
}
finally
{
if(lockSuccess)
lock.writeLock().unlock();
}
*/
return toRemove;
}
/**
* @return the cacheName
*/
public boolean isPageCache()
{
return isPageCache;
}
/**
* @param cacheName the cacheName to set
*/
public void setIsPageCache(boolean isPageCache)
{
this.isPageCache = isPageCache;
}
}