| /* |
| * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.appserv.util.cache; |
| |
| import com.sun.enterprise.util.CULoggerInfo; |
| import java.text.MessageFormat; |
| |
| import java.util.Properties; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| import java.util.Locale; |
| |
| /** |
| * MultiLruCache -- in-memory bounded LRU cache with multiple LRU lists |
| * Underlying Hashtable is made into logical segments, with each segment |
| * having its own LRU list. |
| */ |
| public class BoundedMultiLruCache extends MultiLruCache { |
| |
| // upper bound on the cache size |
| protected long maxSize = Constants.DEFAULT_MAX_CACHE_SIZE; |
| protected long currentSize; |
| private Object currentSizeLk = new Object(); |
| |
| /** |
| * initialize the LRU cache |
| * @param maxCapacity maximum number of entries this cache may hold |
| */ |
| public void init(int maxCapacity, Properties props) throws Exception { |
| super.init(maxCapacity, props); |
| currentSize = 0; |
| |
| if (props != null) { |
| String strMaxSize = props.getProperty("MaxSize"); |
| int multiplier = 1; |
| long size = -1; |
| |
| String prop = strMaxSize; |
| if (prop != null) { |
| int index; |
| |
| // upper case the string |
| prop = prop.toUpperCase(Locale.ENGLISH); |
| |
| // look for 200KB or 80Kb or 1MB or 2Mb like suffixes |
| if ((index = prop.indexOf("KB")) != -1) { |
| multiplier = Constants.KB; |
| prop = prop.substring(0, index); |
| } else if ((index = prop.indexOf("MB")) != -1) { |
| multiplier = Constants.MB; |
| prop = prop.substring(0, index); |
| } |
| |
| try { |
| size = Long.parseLong(prop.trim()); |
| } catch (NumberFormatException nfe) {} |
| } |
| |
| // sanity check and convert |
| if (size > 0) |
| maxSize = (size * multiplier); |
| else { |
| String msg = CULoggerInfo.getString(CULoggerInfo.boundedMultiLruCacheIllegalMaxSize); |
| |
| Object[] params = { strMaxSize }; |
| msg = MessageFormat.format(msg, params); |
| |
| throw new IllegalArgumentException(msg); |
| } |
| } |
| } |
| |
| /** |
| * this item is just added to the cache |
| * @param item <code>CacheItem</code> that was created |
| * @return a overflow item; may be null |
| * |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected CacheItem itemAdded(CacheItem item) { |
| LruCacheItem overflow = (LruCacheItem) super.itemAdded(item); |
| |
| // update the size |
| if (overflow != null) { |
| decrementCurrentSize(overflow.getSize()); |
| } |
| incrementCurrentSize(item.getSize()); |
| |
| return overflow; |
| } |
| |
| /** |
| * item value has been refreshed |
| * @param item <code>CacheItem</code> that was refreshed |
| * @param oldSize size of the previous value that was refreshed |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected void itemRefreshed(CacheItem item, int oldSize) { |
| super.itemRefreshed(item, oldSize); |
| |
| /** reduce the cache by the size of the size of the previous value |
| * and increment by the value being refreshed with. |
| */ |
| decrementCurrentSize(oldSize); |
| incrementCurrentSize(item.getSize()); |
| } |
| |
| /** |
| * item value has been removed from the cache |
| * @param item <code>CacheItem</code> that was just removed |
| * |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected void itemRemoved(CacheItem item) { |
| super.itemRemoved(item); |
| |
| // update the size |
| decrementCurrentSize(item.getSize()); |
| } |
| |
| /** |
| * has cache reached its threshold |
| * @return true when the cache reached its threshold |
| */ |
| protected boolean isThresholdReached() { |
| return (currentSize > maxSize || super.isThresholdReached()); |
| } |
| |
| /** |
| * synchronized counter updates |
| */ |
| protected final void incrementCurrentSize(int size) { |
| synchronized(currentSizeLk) { |
| currentSize += size; |
| } |
| } |
| |
| protected final void decrementCurrentSize(int size) { |
| synchronized(currentSizeLk) { |
| currentSize -= size; |
| } |
| } |
| |
| /** |
| * get generic stats from subclasses |
| */ |
| |
| /** |
| * get the desired statistic counter |
| * @param key to corresponding stat |
| * @return an Object corresponding to the stat |
| * See also: Constant.java for the key |
| */ |
| public Object getStatByName(String key) { |
| Object stat = super.getStatByName(key); |
| |
| if (stat == null && key != null) { |
| if (key.equals(Constants.STAT_BOUNDEDMULTILRUCACHE_CURRENT_SIZE)) |
| stat = Long.valueOf(currentSize); |
| else if (key.equals(Constants.STAT_BOUNDEDMULTILRUCACHE_MAX_SIZE)) { |
| if (maxSize == Constants.DEFAULT_MAX_CACHE_SIZE) |
| stat = Constants.STAT_DEFAULT; |
| else |
| stat = Long.valueOf(maxSize); |
| } |
| } |
| |
| return stat; |
| } |
| |
| public Map getStats() { |
| Map stats = super.getStats(); |
| |
| // cache size in KB |
| stats.put(Constants.STAT_BOUNDEDMULTILRUCACHE_CURRENT_SIZE, |
| Long.valueOf(currentSize)); |
| if (maxSize == Constants.DEFAULT_MAX_CACHE_SIZE) { |
| stats.put(Constants.STAT_BOUNDEDMULTILRUCACHE_MAX_SIZE, |
| Constants.STAT_DEFAULT); |
| } |
| else { |
| stats.put(Constants.STAT_BOUNDEDMULTILRUCACHE_MAX_SIZE, |
| Long.valueOf(maxSize)); |
| } |
| return stats; |
| } |
| } |