/* * Copyright (C) 2005 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a * copy of the License at * * http://www.alfresco.org/legal/license.txt * * 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 org.alfresco.repo.cache; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * A cache implementation that maintains items up to a threshold size. If the threshold size is reached * it begins removing old items during get() calls as they time-out after a specified timeout value. *

* If the threshold value is not reached, the items are not removed unless specifically requested with * a call to remove() or clear(). *

* If the max size value is reached then no more items are added to the cache until some are removed * either explicitly or automically via timed-out values. * * @author Kevin Roast */ public class AutoExpireCache implements SimpleCache { // TODO: configure these values via Spring private final long TIMEDIFF = 1000000L * 1000L * 60L * 5L; // 5 mins in nano-seconds private final int MAXSIZE = 4096; // maximum size of the cache private final float THRESHOLD = 0.75f; // before we start removing items private int maxsize = MAXSIZE; private float threshold = THRESHOLD; private Map> cache = new HashMap>(maxsize, 1.0f); /** * Default constructor */ public AutoExpireCache() { } /** * Constructor */ public AutoExpireCache(int maxsize, float threshold) { maxsize = maxsize; threshold = threshold; } /** * @see org.alfresco.repo.cache.SimpleCache#get(K) */ public synchronized V get(K key) { CacheItem wrapper = cache.get(key); if (wrapper != null) { // we cache values till a specific timeout then remove them // this also gives other values a chance to get added if we are nearing the max size if (cache.size() > (maxsize * threshold) && System.nanoTime() > (wrapper.Timestamp + TIMEDIFF)) { //if (log.isDebugEnabled()) // log.debug("*****Removing timedout key: " + key); cache.remove(key); wrapper = null; } } return wrapper != null ? wrapper.Value : null; } /** * @see org.alfresco.repo.cache.SimpleCache#put(K, V) */ public synchronized void put(K key, V value) { if (cache.size() < maxsize) { //if (log.isDebugEnabled()) // log.debug("***Adding new key: " + key + " size: " + cache.size()); cache.put(key, new CacheItem(key, value)); } } /** * @see org.alfresco.repo.cache.SimpleCache#remove(K) */ public synchronized void remove(K key) { cache.remove(key); } /** * @see org.alfresco.repo.cache.SimpleCache#clear() */ public void clear() { // better to let the GC deal with removing the old map in one shot // rather than try to clear each slot slowly using clear() cache = new HashMap>(maxsize, 1.0f); } /** * @see org.alfresco.repo.cache.SimpleCache#contains(K) */ public synchronized boolean contains(K key) { return false; } /** * Simple wrapper class for a cache item. * Stores a timestamp of when the item was added so it can be purged from the cache later. */ private static class CacheItem { CacheItem(K key, V value) { this.key = key; Value = value; Timestamp = System.nanoTime(); } public int hashCode() { return key.hashCode(); } public boolean equals(Object o) { if (o instanceof CacheItem == false) return false; return key.equals( ((CacheItem)o).key ); } private K key; long Timestamp; V Value; } }