All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jdon.cache.UtilCache Maven / Gradle / Ivy

/*
 * $Id: UtilCache.java,v 1.2 2005/01/31 05:27:54 jdon Exp $
 *
 *  Copyright (c) 2001, 2002 The Open For Business Project - www.ofbiz.org
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
 *  OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 *  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.jdon.cache;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.jdon.util.ConcurrentLinkedList;
import com.jdon.util.Debug;
import com.jdon.util.PropsUtil;
import com.jdon.util.UtilValidate;

/**
 * 

* Generalized caching utility. Provides a number of caching features: *

    *
  • Limited or unlimited element capacity *
  • If limited, removes elements with the LRU (Least Recently Used) algorithm *
  • Keeps track of when each element was loaded into the cache *
  • Using the expireTime can report whether a given element has expired *
  • Counts misses and hits *
* */ public class UtilCache { public static String module = UtilCache.class.getName(); private ScheduledExecutorService scheduExecStatic = Executors.newScheduledThreadPool(1); /** A list of the elements order by Least Recent Use */ public final ConcurrentLinkedList keyLRUList = new ConcurrentLinkedList(); /** * A hashtable containing a CacheLine object with a value and a loadTime for * each element. for above jdk1.5 */ public final Map cacheLineTable = new ConcurrentHashMap(); /** A count of the number of cache hits */ protected volatile long hitCount = 0; /** A count of the number of cache misses */ protected volatile long missCount = 0; /** * The maximum number of elements in the cache. If set to 0, there will be * no limit on the number of elements in the cache. */ protected volatile long maxSize = 0; /** * Specifies the amount of time since initial loading before an element will * be reported as expired. If set to 0, elements will never expire. */ protected volatile long expireTime = 0; /** * Specifies whether or not to use soft references for this cache, defaults * to false */ protected volatile boolean useSoftReference = false; /** * Default constructor, all members stay at default values as defined in * cache.properties, or the defaults in this file if cache.properties is not * found, or there are no 'default' entries in it. */ public UtilCache(PropsUtil propsUtil) { setPropertiesParams(propsUtil, "default"); startMon(expireTime); } public UtilCache(int maxSize, long expireTime, boolean useSoftReference) { this.maxSize = maxSize; this.expireTime = expireTime; this.useSoftReference = useSoftReference; startMon(expireTime); } public void startMon(long expireTime) { Runnable task = new Runnable() { public void run() { clearExpired(); } }; scheduExecStatic.scheduleAtFixedRate(task, (expireTime / 1000) + 30, (expireTime / 1000) + 30, TimeUnit.SECONDS); } protected void setPropertiesParams(PropsUtil propsUtil, String cacheName) { if (propsUtil == null) { System.err.println(" UtilCache propsUtil not yet set!! "); return; } try { String value = propsUtil.getProperty("cache." + cacheName + ".maxSize"); if (UtilValidate.isNotEmpty(value)) { Debug.logVerbose("[JdonFramework]found cache configure: cache." + cacheName + ".maxSize = " + value, module); Long longValue = new Long(value); if (longValue != null) { maxSize = longValue.longValue(); } } } catch (Exception e) { } try { String value = propsUtil.getProperty("cache." + cacheName + ".expireTime"); if (UtilValidate.isNotEmpty(value)) { Long longValue = new Long(value); if (longValue != null) { expireTime = longValue.longValue(); } } } catch (Exception e) { } try { String value = propsUtil.getProperty("cache." + cacheName + ".useSoftReference"); if (UtilValidate.isNotEmpty(value)) { useSoftReference = "true".equals(value); } } catch (Exception e) { } } /** * Puts or loads the passed element into the cache * * @param key * The key for the element, used to reference it in the hastables * and LRU linked list * @param value * The value of the element */ public void put(Object key, Object value) { if ((key == null) || (value == null)) return; try { if (maxSize > 0) { // when maxSize is changed, the setter will take care of filling // the // LRU list if (cacheLineTable.containsKey(key)) { keyLRUList.moveFirst(key); } else { keyLRUList.addFirst(key); } } if (expireTime > 0) { cacheLineTable.put(key, new CacheLine(value, useSoftReference, System.currentTimeMillis())); } else { cacheLineTable.put(key, new CacheLine(value, useSoftReference)); } if (maxSize > 0 && cacheLineTable.size() > maxSize) { Object lastKey = keyLRUList.getLast(); removeObject(lastKey); } } catch (Exception e) { Debug.logError(e); } finally { } Debug.logVerbose("[JdonFramework]cache now size = " + keyLRUList.size() + " maxSize =" + maxSize + " this Cache id:" + this.hashCode(), module); } /** * Gets an element from the cache according to the specified key. If the * requested element hasExpired, it is removed before it is looked up which * causes the function to return null. * * @param key * The key for the element, used to reference it in the hastables * and LRU linked list * @return The value of the element specified by the key */ public Object get(final Object key) { if (key == null) return null; if (!cacheLineTable.containsKey(key)) return null; CacheLine line = (CacheLine) cacheLineTable.get(key); if (hasExpired(line)) { removeObject(key); line = null; } if (line == null) { missCount++; return null; } hitCount++; // double hitPercent = 100*(double)hitCount/(hitCount + missCount); // Debug.logVerbose("[JdonFramework]cache hit percent: " + // percentFormat.format(hitPercent)+"%", module); if (maxSize > 0) { keyLRUList.moveFirst(key); } return line.getValue(); } /** * Removes an element from the cache according to the specified key * * @param key * The key for the element, used to reference it in the hastables * and LRU linked list * @return The value of the removed element specified by the key */ public void remove(Object key) { removeObject(key); } private void removeObject(Object key) { if (key == null) { missCount++; } CacheLine line = (CacheLine) cacheLineTable.remove(key); if (line != null) { if (maxSize > 0) keyLRUList.remove(key); } else { missCount++; } } public Set keySet() { return cacheLineTable.keySet(); } public Collection values() { return cacheLineTable.values(); } /** Removes all elements from this cache */ public void clear() { cacheLineTable.clear(); keyLRUList.clear(); clearCounters(); } /** Removes all elements from this cache */ public void clearAllCaches() { clear(); } /** * Returns the number of successful hits on the cache * * @return The number of successful cache hits */ public long getHitCount() { return hitCount; } /** * Returns the number of cache misses * * @return The number of cache misses */ public long getMissCount() { return missCount; } /** * Clears the hit and miss counters */ public void clearCounters() { hitCount = 0; missCount = 0; } /** * Sets the maximum number of elements in the cache. If 0, there is no * maximum. * * @param maxSize * The maximum number of elements in the cache */ public void setMaxSize(long maxSize) { // if the new maxSize is <= 0, clear keyLRUList if (maxSize <= 0) { keyLRUList.clear(); } else if (maxSize > 0 && this.maxSize <= 0) { // if the new maxSize > 0 and the old is <= 0, fill in LRU list - // order will be meaningless for now Iterator keys = cacheLineTable.keySet().iterator(); while (keys.hasNext()) { keyLRUList.add(keys.next()); } } // if the new maxSize is less than the current cache size, shrink the // cache. if (maxSize > 0 && cacheLineTable.size() > maxSize) { while (cacheLineTable.size() > maxSize) { Object lastKey = keyLRUList.getLast(); removeObject(lastKey); } } this.maxSize = maxSize; } /** * Returns the current maximum number of elements in the cache * * @return The maximum number of elements in the cache */ public long getMaxSize() { return maxSize; } /** * Sets the expire time for the cache elements. If 0, elements never expire. * * @param expireTime * The expire time for the cache elements */ public void setExpireTime(long expireTime) { // if expire time was <= 0 and is now greater, fill expire table now if (this.expireTime <= 0 && expireTime > 0) { long currentTime = System.currentTimeMillis(); Iterator values = cacheLineTable.values().iterator(); while (values.hasNext()) { CacheLine line = (CacheLine) values.next(); line.setLoadTime(currentTime); } } else if (this.expireTime <= 0 && expireTime > 0) {// if expire time // was > 0 and is // now <=, do // nothing, just // leave the load // times in place, // won't hurt // anything... } this.expireTime = expireTime; } /** * return the current expire time for the cache elements * * @return The expire time for the cache elements */ public long getExpireTime() { return expireTime; } /** * Return whether or not the cache lines should use a soft reference to the * data */ public boolean getUseSoftReference() { return this.useSoftReference; } /** * Returns the number of elements currently in the cache * * @return The number of elements currently in the cache */ public long size() { return cacheLineTable.size(); } /** * Returns a boolean specifying whether or not an element with the specified * key is in the cache. If the requested element hasExpired, it is removed * before it is looked up which causes the function to return false. * * @param key * The key for the element, used to reference it in the hastables * and LRU linked list * @return True is the cache contains an element corresponding to the * specified key, otherwise false */ public boolean containsKey(Object key) { CacheLine line = (CacheLine) cacheLineTable.get(key); if (hasExpired(line)) { removeObject(key); line = null; } if (line != null) { return true; } else { return false; } } /** * Returns a boolean specifying whether or not the element corresponding to * the key has expired. Only returns true if element is in cache and has * expired. Error conditions return false, if no expireTable entry, returns * true. Always returns false if expireTime <= 0. Also, if SoftReference in * the CacheLine object has been cleared by the gc return true. * * @param key * The key for the element, used to reference it in the hastables * and LRU linked list * @return True is the element corresponding to the specified key has * expired, otherwise false */ public boolean hasExpired(Object key) { if (key == null) return false; CacheLine line = (CacheLine) cacheLineTable.get(key); return hasExpired(line); } protected boolean hasExpired(CacheLine line) { if (line == null) return false; // check this BEFORE checking to see if expireTime <= 0, ie if time // expiration is enabled // check to see if we are using softReference first, slight performance // increase if (this.useSoftReference && line.getValue() == null) { Debug.logWarning("SoftReference getValue() == null GC has done it. ", module); return true; } if (expireTime <= 0) return false; if (line.getLoadTime() <= 0) return true; if ((line.getLoadTime() + expireTime) < System.currentTimeMillis()) { return true; } else { return false; } } /** * Clears all expired cache entries; also clear any cache entries where the * SoftReference in the CacheLine object has been cleared by the gc */ public void clearExpired() { Iterator keys = cacheLineTable.keySet().iterator(); while (keys.hasNext()) { Object key = keys.next(); if (hasExpired(key)) { removeObject(key); } } } public void stop() { clear(); try { scheduExecStatic.shutdownNow(); } catch (Exception e) { } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy