/* * Copyright (c) 2002-2009 "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; } }