/**
* Copyright (c) 2002-2010 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.cache;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Simple implementation of Least-recently-used cache.
*
* The cache has a <CODE>maxSize</CODE> set and when the number of cached
* elements exceeds that limit the least recently used element will be removed.
*/
public class LruCache<K,E> implements Cache<K,E>
{
private final String name;
int maxSize = 1000;
private boolean resizing = false;
private boolean adaptive = false;
private final AdaptiveCacheManager cacheManager;
private Map<K,E> cache = new LinkedHashMap<K,E>( 500, 0.75f, true )
{
protected boolean removeEldestEntry( Map.Entry<K,E> eldest )
{
// synchronization miss with old value on maxSize here is ok
if ( super.size() > maxSize )
{
if ( isAdaptive() && !isResizing() )
{
adaptCache();
}
else
{
super.remove( eldest.getKey() );
elementCleaned( eldest.getValue() );
}
}
return false;
}
};
void adaptCache()
{
if ( cacheManager != null )
{
cacheManager.adaptCache( this );
}
}
// public void setMaxSize( int maxSize )
// {
// this.maxSize = maxSize;
// }
/**
* Creates a LRU cache. If <CODE>maxSize < 1</CODE> an
* IllegalArgumentException is thrown.
*
* @param name
* name of cache
* @param maxSize
* maximum size of this cache
* @param cacheManager
* adaptive cache manager or null if adaptive caching not needed
*/
public LruCache( String name, int maxSize, AdaptiveCacheManager cacheManager )
{
this.cacheManager = cacheManager;
if ( name == null || maxSize < 1 )
{
throw new IllegalArgumentException( "maxSize=" + maxSize
+ ", name=" + name );
}
this.name = name;
this.maxSize = maxSize;
}
public String getName()
{
return this.name;
}
public synchronized void put( K key, E element )
{
if ( key == null || element == null )
{
throw new IllegalArgumentException( "key=" + key + ", element="
+ element );
}
cache.put( key, element );
}
public synchronized E remove( K key )
{
if ( key == null )
{
throw new IllegalArgumentException( "Null parameter" );
}
return cache.remove( key );
}
public synchronized E get( K key )
{
if ( key == null )
{
throw new IllegalArgumentException();
}
return cache.get( key );
}
public synchronized void clear()
{
resizeInternal( 0 );
}
public synchronized int size()
{
return cache.size();
}
/**
* Returns the maximum size of this cache.
*
* @return maximum size
*/
public int maxSize()
{
return maxSize;
}
/**
* Changes the max size of the cache. If <CODE>newMaxSize</CODE> is
* greater then <CODE>maxSize()</CODE> next invoke to <CODE>maxSize()</CODE>
* will return <CODE>newMaxSize</CODE> and the entries in cache will not
* be modified.
* <p>
* If <CODE>newMaxSize</CODE> is less then <CODE>size()</CODE>
* the cache will shrink itself removing least recently used element until
* <CODE>size()</CODE> equals <CODE>newMaxSize</CODE>. For each element
* removed the {@link #elementCleaned} method is invoked.
* <p>
* If <CODE>newMaxSize</CODE> is less then <CODE>1</CODE> an
* {@link IllegalArgumentException} is thrown.
*
* @param newMaxSize
* the new maximum size of the cache
*/
public synchronized void resize( int newMaxSize )
{
if ( newMaxSize < 1 )
{
throw new IllegalArgumentException( "newMaxSize=" + newMaxSize );
}
resizeInternal( newMaxSize );
}
private void resizeInternal( int newMaxSize )
{
resizing = true;
try
{
if ( newMaxSize >= size() )
{
maxSize = newMaxSize;
}
else if ( newMaxSize == 0 )
{
cache.clear();
}
else
{
maxSize = newMaxSize;
java.util.Iterator<Map.Entry<K,E>> itr = cache.entrySet()
.iterator();
while ( itr.hasNext() && cache.size() > maxSize )
{
E element = itr.next().getValue();
itr.remove();
elementCleaned( element );
}
}
}
finally
{
resizing = false;
}
}
boolean isResizing()
{
return resizing;
}
public void elementCleaned( E element )
{
}
public boolean isAdaptive()
{
return adaptive;
}
public void setAdaptiveStatus( boolean status )
{
this.adaptive = status;
}
public void putAll( Map<K, E> map )
{
cache.putAll( map );
}
}