/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.core.cache; import java.util.LinkedHashMap; import java.util.Map.Entry; import org.osgi.service.log.LogService; import org.eclipse.equinox.log.Logger; import org.eclipse.riena.core.Log4r; import org.eclipse.riena.internal.core.Activator; import org.eclipse.riena.internal.core.cache.ICacheEntry; import org.eclipse.riena.internal.core.cache.SimpleCacheEntry; import org.eclipse.riena.internal.core.ignore.IgnoreFindBugs; /** * LRU=(Last Recently Used) Implementation for IGenericObjectCache (alternative * to GenericObjectCache) * * Beyond the description in the interface, this implementation has no more * entries than minSize. So minSize is also the maxSize. The oldest (last * recently used) entry is removed once minSize/maxSize is reached. */ public class LRUCache<K, V> implements IGenericObjectCache<K, V> { private final static Logger LOGGER = Log4r.getLogger(Activator.getDefault(), GenericObjectCache.class); private LinkedHashMap<K, ICacheEntry<K, V>> lruMap = null; private long timeout; /** minimum count of entries to keep * */ private int minimumSize; private int statHit; private int statNotFound; private int statTimeout; private static int statDisplayCount; private String name = "LRUCache : "; //$NON-NLS-1$ /** * */ public LRUCache() { super(); LOGGER.log(LogService.LOG_INFO, "creating new LRUCache instance"); //$NON-NLS-1$ lruMap = new LinkedHashMap<K, ICacheEntry<K, V>>(); // default timeout 1 minute setTimeout(60000); } /* * (non-Javadoc) * * @see * org.eclipse.riena.core.cache.IGenericObjectCache#setName(java.lang.String * ) */ public void setName(final String name) { this.name = name + " : "; //$NON-NLS-1$ } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#setTimeout(int) */ public void setTimeout(final int milliseconds) { LOGGER.log(LogService.LOG_INFO, "setTimeout = " + milliseconds); //$NON-NLS-1$ timeout = milliseconds; } public int getTimeout() { return (int) timeout; } public synchronized V get(final K key) { LOGGER.log(LogService.LOG_DEBUG, "get = " + key); //$NON-NLS-1$ final ICacheEntry<K, V> entry = lruMap.get(key); /** do we find the entry * */ if (entry == null) { statNotFound++; printStat(); return null; } final long timePassed = System.currentTimeMillis() - entry.getTimestamp(); /** is the entry expired * */ if (timePassed < timeout) { /** does the soft reference still point anywhere * */ statHit++; printStat(); return entry.getValue(); } else { remove(key); statTimeout++; printStat(); return null; } } private void printStat() { statDisplayCount++; if (statDisplayCount > 100) { LOGGER.log(LogService.LOG_INFO, name + "Hit / NotFound / Timeout " //$NON-NLS-1$ + statHit + " / " //$NON-NLS-1$ + statNotFound + " / " //$NON-NLS-1$ + statTimeout); statDisplayCount = 0; } } @IgnoreFindBugs(value = "IS2_INCONSISTENT_SYNC", justification = "not that critical, just statistics") public String getStatistic() { return name + "Hit / NotFound / Miss / Timeout " //$NON-NLS-1$ + statHit + " / " + statNotFound + " / " + statTimeout; //$NON-NLS-1$ //$NON-NLS-2$ } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#put(Object, * java.lang.Object) */ public synchronized void put(final K key, final V value) { LOGGER.log(LogService.LOG_DEBUG, "put = " + key + ", " + value); //$NON-NLS-1$ //$NON-NLS-2$ lruMap.put(key, new SimpleCacheEntry<K, V>(value, key)); } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#clear() */ public synchronized void clear() { LOGGER.log(LogService.LOG_DEBUG, "clear"); //$NON-NLS-1$ lruMap.clear(); } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#remove(Object) */ public synchronized void remove(final K key) { LOGGER.log(LogService.LOG_DEBUG, "remove = " + key); //$NON-NLS-1$ lruMap.remove(key); } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#size() */ public synchronized int size() { LOGGER.log(LogService.LOG_DEBUG, "size <= " + lruMap.size()); //$NON-NLS-1$ return lruMap.size(); } public int getSize() { return size(); } /** * @see org.eclipse.riena.core.cache.IGenericObjectCache#setMinimumSize(int) */ public synchronized void setMinimumSize(final int minSize) { LOGGER.log(LogService.LOG_INFO, "setMinSize = " + minSize); //$NON-NLS-1$ minimumSize = minSize; lruMap = new LRUHashMap<K, V>(minSize); } public synchronized int getMinimumSize() { return minimumSize; } /** * HashMap that implements a Last Recently Used schema on a LinkedHashMap */ private static class LRUHashMap<K, V> extends LinkedHashMap<K, ICacheEntry<K, V>> { private final int minSize; private static final long serialVersionUID = 6499327049035525641L; /** * @param minSize */ public LRUHashMap(final int minSize) { super(minSize, .75F, true); this.minSize = minSize; } /* * (non-Javadoc) * * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) */ @Override protected boolean removeEldestEntry(final Entry<K, ICacheEntry<K, V>> eldest) { return size() > minSize; } } }