This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-jcs.git
commit 593885cb5b97bedb58eccd9f864966ceda77d2c2 Author: Thomas Vandahl <[email protected]> AuthorDate: Fri Sep 4 22:41:12 2026 +0200 A bit of reorganization in basic structures --- .../jcs4/engine/control/CompositeCache.java | 7 +- .../AbstractDoubleLinkedListMemoryCache.java | 300 ++++++++------------- .../jcs4/engine/memory/AbstractMemoryCache.java | 255 ++++++++++++------ .../jcs4/engine/memory/behavior/IMemoryCache.java | 8 - .../jcs4/engine/memory/fifo/FIFOMemoryCache.java | 17 +- .../jcs4/engine/memory/lru/LHMLRUMemoryCache.java | 92 +++---- .../jcs4/engine/memory/lru/LRUMemoryCache.java | 17 +- .../jcs4/engine/memory/mru/MRUMemoryCache.java | 17 +- .../memory/soft/SoftReferenceMemoryCache.java | 139 +++++----- .../jcs4/utils/struct/DoubleLinkedList.java | 170 ++++-------- .../jcs4/engine/memory/MockMemoryCache.java | 26 -- .../engine/memory/mru/MRUMemoryCacheUnitTest.java | 31 ++- 12 files changed, 496 insertions(+), 583 deletions(-) diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java index e99b7947..4c471d5d 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java @@ -185,19 +185,16 @@ public class CompositeCache<K, V> { final Class<?> c = Class.forName(cattr.MemoryCacheName()); @SuppressWarnings("unchecked") // Need cast - final - IMemoryCache<K, V> newInstance = + final IMemoryCache<K, V> newInstance = (IMemoryCache<K, V>) c.getDeclaredConstructor().newInstance(); memCache = newInstance; - memCache.initialize(this); } catch (final Exception e) { log.warn("Failed to init mem cache, using: LRUMemoryCache", e); - this.memCache = new LRUMemoryCache<>(); - this.memCache.initialize(this); } + memCache.initialize(this); } else { diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java index 1de4c48b..848db553 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java @@ -20,8 +20,8 @@ package org.apache.commons.jcs4.engine.memory; */ import java.io.IOException; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.control.CompositeCache; @@ -44,67 +44,47 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract /** The logger. */ private static final Log log = Log.getLog(AbstractDoubleLinkedListMemoryCache.class); + static + { + cacheImplementationName = "Abstract DoubleLinkedList Memory Cache"; + } + /** Thread-safe double linked list for lru */ private DoubleLinkedList<MemoryElementDescriptor<K, V>> list; /** * Adds a new node to the start of the link list. - * <p> + * (guarded by the lock) * - * @param ce - * The feature to be added to the First - * @return MemoryElementDescriptor + * @param me The MemoryElementDescriptor to be added to the start of the list */ - protected MemoryElementDescriptor<K, V> addFirst(final ICacheElement<K, V> ce) + protected void addFirst(final MemoryElementDescriptor<K, V> me) { - lock.lock(); - try + list.addFirst(me); + if ( log.isTraceEnabled() ) { - final MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<>(ce); - list.addFirst(me); - if ( log.isTraceEnabled() ) - { - verifyCache(ce.key()); - } - return me; - } - finally - { - lock.unlock(); + verifyCache(me.getCacheElement().key()); } } /** * Adds a new node to the end of the link list. - * <p> + * (guarded by the lock) * - * @param ce - * The feature to be added to the First - * @return MemoryElementDescriptor + * @param me The feature to be added to the end of the list */ - protected MemoryElementDescriptor<K, V> addLast(final ICacheElement<K, V> ce) + protected void addLast(final MemoryElementDescriptor<K,V> me) { - lock.lock(); - try - { - final MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<>(ce); - list.addLast(me); - if ( log.isTraceEnabled() ) - { - verifyCache(ce.key()); - } - return me; - } - finally + list.addLast(me); + if ( log.isTraceEnabled() ) { - lock.unlock(); + verifyCache(me.getCacheElement().key()); } } /** * Adjust the list as needed for a get. This allows children to control the algorithm * (guarded by the lock) - * <p> * * @param list the node list * @param me the current cache element @@ -115,105 +95,40 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract * Children implement this to control the cache expiration algorithm * <p> * - * @param ce the current cache element - * @return MemoryElementDescriptor the new node - * @throws IOException + * @param me the current cache element */ - protected abstract MemoryElementDescriptor<K, V> adjustListForUpdate(ICacheElement<K, V> ce) throws IOException; + protected abstract void adjustListForUpdate(MemoryElementDescriptor<K, V> me); /** * This is called by super initialize. * - * NOTE: should return a thread safe map - * - * <p> - * - * @return new ConcurrentHashMap() + * @return new HashMap() */ @Override - public ConcurrentMap<K, MemoryElementDescriptor<K, V>> createMap() + protected Map<K, MemoryElementDescriptor<K, V>> createMap() { return new ConcurrentHashMap<>(); } - /** - * Dump the cache entries from first to list for debugging. - */ - private void dumpCacheEntries() - { - log.trace("dumpingCacheEntries"); - for (MemoryElementDescriptor<K, V> me = list.getFirst(); me != null; me = (MemoryElementDescriptor<K, V>) me.next) - { - log.trace("dumpCacheEntries> key={0}, val={1}", - me.getCacheElement().key(), me.getCacheElement().value()); - } - } - - /** - * This instructs the memory cache to remove the <em>numberToFree</em> according to its eviction - * policy. For example, the LRUMemoryCache will remove the <em>numberToFree</em> least recently - * used items. These will be spooled to disk if a disk auxiliary is available. - * <p> - * - * @param numberToFree - * @return The number that were removed. if you ask to free 5, but there are only 3, you will - * get 3. - */ - @Override - public int freeElements(final int numberToFree) - { - int freed = 0; - - lock.lock(); - - try - { - for (; freed < numberToFree; freed++) - { - final ICacheElement<K, V> element = spoolLastElement(); - if (element == null) - { - break; - } - } - } - finally - { - lock.unlock(); - } - - return freed; - } - /** * @see org.apache.commons.jcs4.engine.memory.AbstractMemoryCache#get(Object) */ @Override public ICacheElement<K, V> get(final K key) throws IOException { - lock.lock(); - - try - { - final ICacheElement<K, V> ce = super.get(key); - - if (log.isTraceEnabled()) - { - verifyCache(); - } + final ICacheElement<K, V> ce = super.get(key); - return ce; - } - finally + if (log.isTraceEnabled()) { - lock.unlock(); + verifyCache(); } + + return ce; } /** * This returns semi-structured information on the memory cache, such as the size, put count, * hit count, and miss count. - * <p> * * @see org.apache.commons.jcs4.engine.memory.behavior.IMemoryCache#getStatistics() */ @@ -221,7 +136,6 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract public IStats getStatistics() { final IStats stats = super.getStatistics(); - stats.setTypeName( /* add algorithm name */"Memory Cache"); stats.addStatElement("List Size", Integer.valueOf(list.size())); return stats; @@ -241,6 +155,18 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract log.info("initialized MemoryCache for {0}", this::getCacheName); } + /** + * Wrap the cache element into an appropriate memory element descriptor + * + * @param ce The cache element + * @return The memory element descriptor + */ + @Override + protected MemoryElementDescriptor<K, V> wrap(ICacheElement<K, V> ce) + { + return new MemoryElementDescriptor<>(ce); + } + /** * Update control structures after get * (guarded by the lock) @@ -253,6 +179,30 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract adjustListForGet(list, me); } + /** + * Update control structures after update + * (guarded by the lock) + * + * @param newNode The memory element descriptor of the current cache element + * @param oldNode The memory element descriptor of the previous cache element + * @throws IOException if spooling operation fails + */ + @Override + protected void lockedUpdateElement(MemoryElementDescriptor<K, V> newNode, + MemoryElementDescriptor<K, V> oldNode) throws IOException + { + adjustListForUpdate(newNode); + + // If the node was the same as an existing node, remove it. + if (oldNode != null && newNode.getCacheElement().key().equals(oldNode.getCacheElement().key())) + { + list.remove(oldNode); + } + + // If we are over the max spool some + spoolIfNeeded(); + } + /** * Removes all cached items from the cache control structures. * (guarded by the lock) @@ -275,62 +225,75 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract list.remove(me); } + /** + * This instructs the memory cache to remove the <em>numberToFree</em> according to its eviction + * policy. For example, the LRUMemoryCache will remove the <em>numberToFree</em> least recently + * used items. These will be spooled to disk if a disk auxiliary is available. + * (guarded by the lock) + * + * @param numberToFree + * @return The number that were removed. if you ask to free 5, but there are only 3, you will + * get 3. + */ + @Override + protected int lockedFreeElements(final int numberToFree) throws IOException + { + int freed = 0; + + for (; freed < numberToFree; freed++) + { + final ICacheElement<K, V> element = spoolLastElement(); + if (element == null) + { + break; + } + } + + return freed; + } + /** * If the max size has been reached, spool. - * <p> + * (guarded by the lock) * - * @throws Error + * @throws IOException */ - private void spoolIfNeeded() throws Error + private void spoolIfNeeded() throws IOException { // The spool will put them in a disk event queue, so there is no // need to pre-queue the queuing. This would be a bit wasteful // and wouldn't save much time in this synchronous call. - lock.lock(); - - try + final int size = getSize(); + // If the element limit is reached, we need to spool + if (size <= getCacheAttributes().MaxObjects()) { - final int size = map.size(); - // If the element limit is reached, we need to spool - - if (size <= getCacheAttributes().MaxObjects()) - { - return; - } + return; + } - log.debug("In memory limit reached, spooling"); + log.debug("In memory limit reached, spooling"); - // Write the last 'chunkSize' items to disk. - final int chunkSizeCorrected = Math.min(size, chunkSize); + // Write the last 'chunkSize' items to disk. + final int chunkSizeCorrected = Math.min(size, getCacheAttributes().SpoolChunkSize()); - log.debug("About to spool to disk cache, map size: {0}, max objects: {1}, " - + "maximum items to spool: {2}", () -> size, - getCacheAttributes()::MaxObjects, - () -> chunkSizeCorrected); + log.debug("About to spool to disk cache, map size: {0}, max objects: {1}, " + + "maximum items to spool: {2}", () -> size, + getCacheAttributes()::MaxObjects, + () -> chunkSizeCorrected); - freeElements(chunkSizeCorrected); + freeElements(chunkSizeCorrected); - // If this is out of the sync block it can detect a mismatch - // where there is none. - if (log.isDebugEnabled() && map.size() != list.size()) - { - log.debug("update: After spool, size mismatch: map.size() = {0}, " - + "linked list size = {1}", map.size(), list.size()); - } - } - finally + // If this is out of the sync block it can detect a mismatch + // where there is none. + if (log.isDebugEnabled() && getSize() != list.size()) { - lock.unlock(); + log.debug("update: After spool, size mismatch: map.size() = {0}, " + + "linked list size = {1}", getSize(), list.size()); } - - log.debug("update: After spool map size: {0} linked list size = {1}", - () -> map.size(), () -> list.size()); } /** * This spools the last element in the LRU, if one exists. - * The method is called guarded by the lock - * <p> + * (guarded by the lock) * * @return ICacheElement<K, V> if there was a last element, else null. * @throws Error @@ -365,41 +328,16 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract } /** - * Calls the abstract method updateList. - * <p> - * If the max size is reached, an element will be put to disk. - * <p> - * - * @param ce - * The cache element, or entry wrapper - * @throws IOException + * Dump the cache entries from first to list for debugging. */ - @Override - public final void update(final ICacheElement<K, V> ce) throws IOException + private void dumpCacheEntries() { - lock.lock(); - try - { - super.update(ce); - final MemoryElementDescriptor<K, V> newNode = adjustListForUpdate(ce); - - // this should be synchronized if we were not using a ConcurrentHashMap - final K key = newNode.getCacheElement().key(); - final MemoryElementDescriptor<K, V> oldNode = map.put(key, newNode); - - // If the node was the same as an existing node, remove it. - if (oldNode != null && key.equals(oldNode.getCacheElement().key())) - { - list.remove(oldNode); - } - } - finally + log.trace("dumpingCacheEntries"); + for (MemoryElementDescriptor<K, V> me = list.getFirst(); me != null; me = (MemoryElementDescriptor<K, V>) me.next) { - lock.unlock(); + log.trace("dumpCacheEntries> key={0}, val={1}", + me.getCacheElement().key(), me.getCacheElement().value()); } - - // If we are over the max spool some - spoolIfNeeded(); } /** @@ -410,7 +348,7 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract { boolean found = false; log.trace("verifycache[{0}]: map contains {1} elements, linked list " - + "contains {2} elements", getCacheName(), map.size(), + + "contains {2} elements", getCacheName(), getSize(), list.size()); log.trace("verifycache: checking linked list by key "); for (MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next) diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractMemoryCache.java index 2a34cbaf..1421f5c3 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractMemoryCache.java @@ -20,14 +20,14 @@ package org.apache.commons.jcs4.engine.memory; */ import java.io.IOException; +import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -52,6 +52,9 @@ public abstract class AbstractMemoryCache<K, V> /** Log instance */ private static final Log log = Log.getLog( AbstractMemoryCache.class ); + /** The cache implementation name */ + protected static String cacheImplementationName = "Abstract Memory Cache"; + /** Cache Attributes. Regions settings. */ private ICompositeCacheAttributes cacheAttributes; @@ -61,10 +64,8 @@ public abstract class AbstractMemoryCache<K, V> /** The cache region name this store is associated with */ private String cacheName; - /** How many to spool at a time. */ - protected int chunkSize; - - protected final Lock lock = new ReentrantLock(); + /** The lock */ + protected final ReadWriteLock lock = new ReentrantReadWriteLock(); /** Map where items are stored by key. This is created by the concrete child class. */ protected Map<K, MemoryElementDescriptor<K, V>> map; // TODO privatise @@ -80,11 +81,47 @@ public abstract class AbstractMemoryCache<K, V> /** * Children must implement this method. A FIFO implementation may use a tree map. An LRU might - * use a hashtable. The map returned should be threadsafe. + * use a hashtable. * - * @return A threadsafe Map + * @return A Map */ - public abstract Map<K, MemoryElementDescriptor<K, V>> createMap(); + protected abstract Map<K, MemoryElementDescriptor<K, V>> createMap(); + + /** + * Get a read-only map view + * @return a read-only map view + */ + protected Map<K, MemoryElementDescriptor<K, V>> getMapView() + { + return Collections.unmodifiableMap(map); + } + + /** + * This instructs the memory cache to remove the <em>numberToFree</em> according to its eviction + * policy. For example, the LRUMemoryCache will remove the <em>numberToFree</em> least recently + * used items. These will be spooled to disk if a disk auxiliary is available. + * + * @param numberToFree + * @return The number that were removed. if you ask to free 5, but there are only 3, you will + * get 3. + */ + @Override + public int freeElements(final int numberToFree) throws IOException + { + int freed = 0; + + lock.writeLock().lock(); + try + { + freed = lockedFreeElements(numberToFree); + } + finally + { + lock.writeLock().unlock(); + } + + return freed; + } /** * Prepares for shutdown. Reset statistics @@ -130,32 +167,31 @@ public abstract class AbstractMemoryCache<K, V> log.debug("{0}: getting item for key {1}", this::getCacheName, () -> key); - final MemoryElementDescriptor<K, V> me = map.get(key); - - if (me != null) + lock.writeLock().lock(); + try { - hitCnt.incrementAndGet(); - ce = me.getCacheElement(); + final MemoryElementDescriptor<K, V> me = map.get(key); - lock.lock(); - try + if (me != null) { + hitCnt.incrementAndGet(); lockedGetElement(me); + ce = me.getCacheElement(); + + log.debug("{0}: MemoryCache hit for {1}", this::getCacheName, + () -> key); } - finally + else { - lock.unlock(); - } + missCnt.incrementAndGet(); - log.debug("{0}: MemoryCache hit for {1}", this::getCacheName, - () -> key); + log.debug("{0}: MemoryCache miss for {1}", this::getCacheName, + () -> key); + } } - else + finally { - missCnt.incrementAndGet(); - - log.debug("{0}: MemoryCache miss for {1}", this::getCacheName, - () -> key); + lock.writeLock().unlock(); } return ce; @@ -190,7 +226,15 @@ public abstract class AbstractMemoryCache<K, V> @Override public Set<K> getKeySet() { - return new LinkedHashSet<>(map.keySet()); + lock.readLock().lock(); + try + { + return Collections.unmodifiableSet(map.keySet()); + } + finally + { + lock.readLock().unlock(); + } } /** @@ -266,7 +310,15 @@ public abstract class AbstractMemoryCache<K, V> @Override public int getSize() { - return this.map.size(); + lock.readLock().lock(); + try + { + return this.map.size(); + } + finally + { + lock.readLock().unlock(); + } } /** @@ -275,7 +327,7 @@ public abstract class AbstractMemoryCache<K, V> @Override public IStats getStatistics() { - final IStats stats = new Stats("Abstract Memory Cache"); + final IStats stats = new Stats(cacheImplementationName); stats.addStatElement("Put Count", putCnt); stats.addStatElement("Hit Count", hitCnt); @@ -298,13 +350,22 @@ public abstract class AbstractMemoryCache<K, V> putCnt = new AtomicLong(); this.cacheAttributes = hub.getCacheAttributes(); - this.chunkSize = cacheAttributes.SpoolChunkSize(); final String attributeCacheName = this.cacheAttributes.cacheName(); this.cacheName = attributeCacheName == null ? hub.getCacheName() : attributeCacheName; this.waterfall = ce -> hub.spoolToDisk(ce); this.map = createMap(); + + log.info("initialized {0} for {1}", cacheImplementationName, cacheName); } + /** + * Wrap the cache element into an appropriate memory element descriptor + * + * @param ce The cache element + * @return The memory element descriptor + */ + protected abstract MemoryElementDescriptor<K, V> wrap(ICacheElement<K, V> ce); + /** * Update control structures after get * (guarded by the lock) @@ -313,6 +374,17 @@ public abstract class AbstractMemoryCache<K, V> */ protected abstract void lockedGetElement(MemoryElementDescriptor<K, V> me); + /** + * Update control structures after update + * (guarded by the lock) + * + * @param newNode The memory element descriptor of the current cache element + * @param oldNode The memory element descriptor of the previous cache element + * @throws IOException if spooling operation fails + */ + protected abstract void lockedUpdateElement(MemoryElementDescriptor<K, V> newNode, + MemoryElementDescriptor<K, V> oldNode) throws IOException; + /** * Removes all cached items from the cache control structures. * (guarded by the lock) @@ -327,6 +399,18 @@ public abstract class AbstractMemoryCache<K, V> */ protected abstract void lockedRemoveElement(MemoryElementDescriptor<K, V> me); + /** + * This instructs the memory cache to remove the <em>numberToFree</em> according to its eviction + * policy. For example, the LRUMemoryCache will remove the <em>numberToFree</em> least recently + * used items. These will be spooled to disk if a disk auxiliary is available. + * (guarded by the lock) + * + * @param numberToFree + * @return The number that were removed. if you ask to free 5, but there are only 3, you will + * get 3. + */ + protected abstract int lockedFreeElements(final int numberToFree) throws IOException; + /** * Removes an item from the cache. This method handles hierarchical removal. If the key is a * String and ends with the CacheConstants.NAME_COMPONENT_DELIMITER, then all items with keys @@ -347,16 +431,16 @@ public abstract class AbstractMemoryCache<K, V> // handle partial removal if (key instanceof String s && s.endsWith(ICache.NAME_COMPONENT_DELIMITER)) { - removed = removeByHierarchy(key); + removed = removeByHierarchy(s); } else if (key instanceof GroupAttrName gan && gan.attrName() == null) { - removed = removeByGroup(key); + removed = removeByGroup(gan.groupId()); } else { // remove single item. - lock.lock(); + lock.writeLock().lock(); try { final MemoryElementDescriptor<K, V> me = map.remove(key); @@ -368,7 +452,7 @@ public abstract class AbstractMemoryCache<K, V> } finally { - lock.unlock(); + lock.writeLock().unlock(); } } @@ -383,90 +467,75 @@ public abstract class AbstractMemoryCache<K, V> @Override public void removeAll() throws IOException { - lock.lock(); + lock.writeLock().lock(); try { - lockedRemoveAll(); map.clear(); + lockedRemoveAll(); } finally { - lock.unlock(); + lock.writeLock().unlock(); } } /** * Remove all keys of the same group hierarchy. - * @param key The key + * @param groupId The group attribute id * @return true if something has been removed */ - protected boolean removeByGroup(final K key) + protected boolean removeByGroup(final GroupId groupId) { - final GroupId groupId = ((GroupAttrName<?>) key).groupId(); - - // remove all keys of the same group hierarchy. - return map.entrySet().removeIf(entry -> { - final K k = entry.getKey(); + lock.writeLock().lock(); + try + { + // remove all keys of the same group hierarchy. + return map.entrySet().removeIf(entry -> { + final K k = entry.getKey(); - if (k instanceof GroupAttrName gan && gan.groupId().equals(groupId)) - { - lock.lock(); - try - { - lockedRemoveElement(entry.getValue()); - return true; - } - finally + if (k instanceof GroupAttrName kgan && kgan.groupId().equals(groupId)) { - lock.unlock(); + lockedRemoveElement(entry.getValue()); + return true; } - } - return false; - }); + return false; + }); + } + finally + { + lock.writeLock().unlock(); + } } /** * Remove all keys of the same name hierarchy. * - * @param key The key + * @param keyString The key as string * @return true if something has been removed */ - protected boolean removeByHierarchy(final K key) + protected boolean removeByHierarchy(final String keyString) { - final String keyString = key.toString(); - - // remove all keys of the same name hierarchy. - return map.entrySet().removeIf(entry -> { - final K k = entry.getKey(); + lock.writeLock().lock(); + try + { + // remove all keys of the same name hierarchy. + return map.entrySet().removeIf(entry -> { + final K k = entry.getKey(); - if (k instanceof String s && s.startsWith(keyString)) - { - lock.lock(); - try + if (k instanceof String s && s.startsWith(keyString)) { lockedRemoveElement(entry.getValue()); return true; } - finally - { - lock.unlock(); - } - } - return false; - }); - } - - /** - * Sets the CacheAttributes. - * - * @param cattr The new CacheAttributes value - */ - @Override - public void setCacheAttributes( final ICompositeCacheAttributes cattr ) - { - this.cacheAttributes = cattr; + return false; + }); + } + finally + { + lock.writeLock().unlock(); + } } /** @@ -480,6 +549,18 @@ public abstract class AbstractMemoryCache<K, V> throws IOException { putCnt.incrementAndGet(); + final MemoryElementDescriptor<K, V> newNode = wrap(ce); + + lock.writeLock().lock(); + try + { + final MemoryElementDescriptor<K, V> oldNode = map.put(ce.key(), newNode); + lockedUpdateElement(newNode, oldNode); + } + finally + { + lock.writeLock().unlock(); + } } /** diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/behavior/IMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/behavior/IMemoryCache.java index e5d31f92..fd51976e 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/behavior/IMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/behavior/IMemoryCache.java @@ -148,14 +148,6 @@ public interface IMemoryCache<K, V> void removeAll() throws IOException; - /** - * Sets the CacheAttributes of the region. - * - * @param cattr - * The new cacheAttributes value - */ - void setCacheAttributes( ICompositeCacheAttributes cattr ); - /** * Puts an item to the cache. * diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/fifo/FIFOMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/fifo/FIFOMemoryCache.java index 0d188461..07e556eb 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/fifo/FIFOMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/fifo/FIFOMemoryCache.java @@ -19,9 +19,6 @@ package org.apache.commons.jcs4.engine.memory.fifo; * under the License. */ -import java.io.IOException; - -import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache; import org.apache.commons.jcs4.engine.memory.util.MemoryElementDescriptor; import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; @@ -32,6 +29,11 @@ import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; public class FIFOMemoryCache<K, V> extends AbstractDoubleLinkedListMemoryCache<K, V> { + static + { + cacheImplementationName = "FIFO Memory Cache"; + } + /** * Does nothing. * @@ -47,14 +49,11 @@ public class FIFOMemoryCache<K, V> * Puts an item to the cache. Removes any pre-existing entries of the same key from the linked * list and adds this one first. * - * @param ce The cache element, or entry wrapper - * @return MemoryElementDescriptor the new node - * @throws IOException + * @param me The cache element, or entry wrapper */ @Override - protected MemoryElementDescriptor<K, V> adjustListForUpdate( final ICacheElement<K, V> ce ) - throws IOException + protected void adjustListForUpdate(final MemoryElementDescriptor<K, V> me) { - return addFirst( ce ); + addFirst(me); } } diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LHMLRUMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LHMLRUMemoryCache.java index 68f6aa8e..d00e8153 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LHMLRUMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LHMLRUMemoryCache.java @@ -20,15 +20,12 @@ package org.apache.commons.jcs4.engine.memory.lru; */ import java.io.IOException; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.jcs4.engine.behavior.ICacheElement; -import org.apache.commons.jcs4.engine.control.CompositeCache; import org.apache.commons.jcs4.engine.memory.AbstractMemoryCache; import org.apache.commons.jcs4.engine.memory.util.MemoryElementDescriptor; -import org.apache.commons.jcs4.engine.stats.behavior.IStats; import org.apache.commons.jcs4.log.Log; /** @@ -37,6 +34,14 @@ import org.apache.commons.jcs4.log.Log; public class LHMLRUMemoryCache<K, V> extends AbstractMemoryCache<K, V> { + /** The Logger. */ + private static final Log log = Log.getLog( LRUMemoryCache.class ); + + static + { + cacheImplementationName = "LHMLRU Memory Cache"; + } + /** * Implements removeEldestEntry from {@link LinkedHashMap}. */ @@ -60,7 +65,6 @@ public class LHMLRUMemoryCache<K, V> * @param eldest * @return true if removed */ - @SuppressWarnings("synthetic-access") @Override protected boolean removeEldestEntry( final Map.Entry<K, MemoryElementDescriptor<K, V>> eldest ) { @@ -73,85 +77,58 @@ public class LHMLRUMemoryCache<K, V> log.debug( "LHMLRU max size: {0}. Spooling element, key: {1}", () -> getCacheAttributes().MaxObjects(), element::key); - waterfall( element ); + waterfall(element); - log.debug( "LHMLRU size: {0}", map::size ); + log.debug("LHMLRU size: {0}", getSize()); return true; } } - /** The Logger. */ - private static final Log log = Log.getLog( LRUMemoryCache.class ); - /** - * Returns a synchronized LHMSpooler + * Returns a LHMSpooler * - * @return Collections.synchronizedMap( new LHMSpooler() ) + * @return new LHMSpooler() */ @Override - public Map<K, MemoryElementDescriptor<K, V>> createMap() - { - return Collections.synchronizedMap( new LHMSpooler() ); - } - - /** - * Dump the cache entries from first to last for debugging. - */ - public void dumpCacheEntries() + protected Map<K, MemoryElementDescriptor<K, V>> createMap() { - dumpMap(); + return new LHMSpooler(); } /** - * This can't be implemented. + * Wrap the cache element into an appropriate memory element descriptor * - * @param numberToFree - * @return 0 - * @throws IOException + * @param ce The cache element + * @return The memory element descriptor */ @Override - public int freeElements( final int numberToFree ) - throws IOException + protected MemoryElementDescriptor<K, V> wrap(ICacheElement<K, V> ce) { - // can't be implemented using the LHM - return 0; + return new MemoryElementDescriptor<>(ce); } /** - * This returns semi-structured information on the memory cache, such as the size, put count, - * hit count, and miss count. - * - * @return IStats - */ - @Override - public IStats getStatistics() - { - final IStats stats = super.getStatistics(); - stats.setTypeName( "LHMLRU Memory Cache" ); - - return stats; - } - - /** - * For post reflection creation initialization + * Update control structures after get + * (guarded by the lock) * - * @param hub + * @param me The memory element descriptor */ @Override - public void initialize( final CompositeCache<K, V> hub ) + protected void lockedGetElement(final MemoryElementDescriptor<K, V> me) { - super.initialize( hub ); - log.info( "initialized LHMLRUMemoryCache for {0}", this::getCacheName ); + // empty } /** - * Update control structures after get + * Update control structures after update * (guarded by the lock) * - * @param me The memory element descriptor + * @param newNode The memory element descriptor of the current cache element + * @param oldNode The memory element descriptor of the previous cache element */ @Override - protected void lockedGetElement(final MemoryElementDescriptor<K, V> me) + protected void lockedUpdateElement(MemoryElementDescriptor<K, V> newNode, + MemoryElementDescriptor<K, V> oldNode) { // empty } @@ -179,16 +156,15 @@ public class LHMLRUMemoryCache<K, V> } /** - * Puts an item to the cache. + * This can't be implemented. * - * @param ce Description of the Parameter + * @param numberToFree + * @return 0 * @throws IOException */ @Override - public void update( final ICacheElement<K, V> ce ) - throws IOException + protected int lockedFreeElements(final int numberToFree) throws IOException { - super.update(ce); - map.put( ce.key(), new MemoryElementDescriptor<>(ce) ); + return 0; } } diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LRUMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LRUMemoryCache.java index 2c00917f..6bc6cf39 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LRUMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/lru/LRUMemoryCache.java @@ -19,9 +19,6 @@ package org.apache.commons.jcs4.engine.memory.lru; * under the License. */ -import java.io.IOException; - -import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache; import org.apache.commons.jcs4.engine.memory.util.MemoryElementDescriptor; import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; @@ -40,6 +37,11 @@ import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; public class LRUMemoryCache<K, V> extends AbstractDoubleLinkedListMemoryCache<K, V> { + static + { + cacheImplementationName = "LRU Memory Cache"; + } + /** * Makes the item the first in the list. * @@ -55,14 +57,11 @@ public class LRUMemoryCache<K, V> * Puts an item to the cache. Removes any pre-existing entries of the same key from the linked * list and adds this one first. * - * @param ce The cache element, or entry wrapper - * @return MemoryElementDescriptor the new node - * @throws IOException + * @param me The cache element, or entry wrapper */ @Override - protected MemoryElementDescriptor<K, V> adjustListForUpdate( final ICacheElement<K, V> ce ) - throws IOException + protected void adjustListForUpdate(final MemoryElementDescriptor<K, V> me) { - return addFirst( ce ); + addFirst(me); } } diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCache.java index e0a252bd..a6eca64a 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCache.java @@ -19,9 +19,6 @@ package org.apache.commons.jcs4.engine.memory.mru; * under the License. */ -import java.io.IOException; - -import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache; import org.apache.commons.jcs4.engine.memory.util.MemoryElementDescriptor; import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; @@ -33,6 +30,11 @@ import org.apache.commons.jcs4.utils.struct.DoubleLinkedList; public class MRUMemoryCache<K, V> extends AbstractDoubleLinkedListMemoryCache<K, V> { + static + { + cacheImplementationName = "MRU Memory Cache"; + } + /** * Makes the item the last in the list. * @@ -50,14 +52,11 @@ public class MRUMemoryCache<K, V> * It's not clear if the put operation should be different. Perhaps this should remove the oldest * if full, and then put. * - * @param ce - * @return MemoryElementDescriptor the new node - * @throws IOException + * @param me The cache element, or entry wrapper */ @Override - protected MemoryElementDescriptor<K, V> adjustListForUpdate( final ICacheElement<K, V> ce ) - throws IOException + protected void adjustListForUpdate(final MemoryElementDescriptor<K, V> me) { - return addFirst( ce ); + addFirst(me); } } diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/soft/SoftReferenceMemoryCache.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/soft/SoftReferenceMemoryCache.java index f2828723..22c32296 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/soft/SoftReferenceMemoryCache.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/soft/SoftReferenceMemoryCache.java @@ -21,12 +21,11 @@ package org.apache.commons.jcs4.engine.memory.soft; import java.io.IOException; import java.lang.ref.SoftReference; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; +import java.util.stream.Collectors; import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.behavior.ICompositeCacheAttributes; @@ -35,7 +34,6 @@ import org.apache.commons.jcs4.engine.memory.AbstractMemoryCache; import org.apache.commons.jcs4.engine.memory.util.MemoryElementDescriptor; import org.apache.commons.jcs4.engine.memory.util.SoftReferenceElementDescriptor; import org.apache.commons.jcs4.engine.stats.behavior.IStats; -import org.apache.commons.jcs4.log.Log; /** * A JCS IMemoryCache that has {@link SoftReference} to all its values. @@ -48,8 +46,10 @@ import org.apache.commons.jcs4.log.Log; */ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> { - /** The logger. */ - private static final Log log = Log.getLog(SoftReferenceMemoryCache.class); + static + { + cacheImplementationName = "SoftReference Memory Cache"; + } /** * Strong references to the maxObjects number of newest objects. @@ -64,41 +64,29 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> * @see org.apache.commons.jcs4.engine.memory.AbstractMemoryCache#createMap() */ @Override - public ConcurrentMap<K, MemoryElementDescriptor<K, V>> createMap() + protected Map<K, MemoryElementDescriptor<K, V>> createMap() { return new ConcurrentHashMap<>(); } - /** - * This can't be implemented. - * - * @param numberToFree - * @return 0 - * @throws IOException - */ - @Override - public int freeElements(final int numberToFree) throws IOException - { - return 0; - } - /** * @see org.apache.commons.jcs4.engine.memory.behavior.IMemoryCache#getKeySet() */ @Override public Set<K> getKeySet() { - final Set<K> keys = new HashSet<>(); - for (final Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet()) + lock.readLock().lock(); + try { - final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) e.getValue(); - if (sred.getCacheElement() != null) - { - keys.add(e.getKey()); - } + return map.entrySet().stream() + .filter(e -> e.getValue().getCacheElement() != null) + .map(e -> e.getKey()) + .collect(Collectors.toSet()); + } + finally + { + lock.readLock().unlock(); } - - return keys; } /** @@ -109,16 +97,19 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> @Override public int getSize() { - int size = 0; - for (final MemoryElementDescriptor<K, V> me : map.values()) + lock.readLock().lock(); + try { - final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) me; - if (sred.getCacheElement() != null) - { - size++; - } + long size = map.values().stream() + .filter(v -> v.getCacheElement() != null) + .count(); + + return (int) size; + } + finally + { + lock.readLock().unlock(); } - return size; } /** @@ -128,9 +119,8 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> public IStats getStatistics() { final IStats stats = super.getStatistics(); - stats.setTypeName("Soft Reference Memory Cache"); - final int emptyrefs = map.size() - getSize(); + final int emptyrefs = super.getSize() - getSize(); stats.addStatElement("Empty References", Integer.valueOf(emptyrefs)); stats.addStatElement("Strong References", Integer.valueOf(strongReferences.size())); @@ -145,10 +135,20 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> @Override public synchronized void initialize( final CompositeCache<K, V> hub ) { - super.initialize( hub ); strongReferences = new LinkedBlockingQueue<>(); - log.info( "initialized Soft Reference Memory Cache for {0}", - this::getCacheName ); + super.initialize( hub ); + } + + /** + * Wrap the cache element into an appropriate memory element descriptor + * + * @param ce The cache element + * @return The memory element descriptor + */ + @Override + protected MemoryElementDescriptor<K, V> wrap(ICacheElement<K, V> ce) + { + return new SoftReferenceElementDescriptor<>(ce); } /** @@ -168,6 +168,25 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> trimStrongReferences(); } + /** + * Update control structures after update + * (guarded by the lock) + * + * @param newNode The memory element descriptor of the current cache element + * @param oldNode The memory element descriptor of the previous cache element + */ + @Override + protected void lockedUpdateElement(MemoryElementDescriptor<K, V> newNode, + MemoryElementDescriptor<K, V> oldNode) + { + final ICacheElement<K, V> val = newNode.getCacheElement(); + val.elementAttributes().setLastAccessTimeNow(); + + // update the ordering of the strong references + strongReferences.add(val); + trimStrongReferences(); + } + /** * Removes all cached items from the cache control structures. * (guarded by the lock) @@ -190,6 +209,19 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> strongReferences.remove(me.getCacheElement()); } + /** + * This can't be implemented. + * + * @param numberToFree + * @return 0 + * @throws IOException + */ + @Override + protected int lockedFreeElements(final int numberToFree) throws IOException + { + return 0; + } + /** * Trim the number of strong references to equal or below the number given * by the maxObjects parameter. @@ -205,29 +237,4 @@ public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> waterfall(ce); } } - - /** - * Puts an item to the cache. - * - * @param ce Description of the Parameter - * @throws IOException Description of the Exception - */ - @Override - public void update(final ICacheElement<K, V> ce) throws IOException - { - lock.lock(); - - try - { - super.update(ce); - ce.elementAttributes().setLastAccessTimeNow(); - map.put(ce.key(), new SoftReferenceElementDescriptor<>(ce)); - strongReferences.add(ce); - trimStrongReferences(); - } - finally - { - lock.unlock(); - } - } } diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java index b9cc5c1b..c76b2f6d 100644 --- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java +++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java @@ -48,7 +48,7 @@ import org.apache.commons.jcs4.log.Log; * @see java.util.concurrent.locks.ReentrantLock * @see org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache */ -@SuppressWarnings({ "unchecked", "rawtypes" }) // Don't know how to resolve this with generics +@SuppressWarnings({"unchecked", "rawtypes"}) // Don't know how to resolve this with generics public class DoubleLinkedList<T extends DoubleLinkedListNode> { /** The logger */ @@ -63,6 +63,17 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> /** LRU double linked list tail node */ private T last; + /** + * Construct DoubleLinkedList + */ + public DoubleLinkedList() + { + this.first = (T) new DoubleLinkedListNode<T>(null); + this.last = (T) new DoubleLinkedListNode<T>(null); + this.first.next = this.last; + this.last.prev = this.first; + } + /** * Adds a new node to the start of the link list. * @@ -70,17 +81,10 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public void addFirst(final T me) { - if ( last == null ) - { - // empty list. - last = me; - } - else - { - first.prev = me; - me.next = first; - } - first = me; + me.prev = first; + me.next = first.next; + first.next.prev = me; + first.next = me; size++; } @@ -91,17 +95,10 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public void addLast(final T me) { - if ( first == null ) - { - // empty list. - first = me; - } - else - { - last.next = me; - me.prev = last; - } - last = me; + me.next = last; + me.prev = last.prev; + last.prev.next = me; + last.prev = me; size++; } @@ -114,7 +111,7 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> if ( log.isDebugEnabled() ) { log.debug( "dumping Entries" ); - for (T me = first; me != null; me = (T) me.next) + for (T me = (T) first.next; me != last; me = (T) me.next) { log.debug( "dump Entries> payload= \"{0}\"", me.getPayload() ); } @@ -128,8 +125,8 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public T getFirst() { - log.debug( "returning first node" ); - return first; + log.trace( "returning first node" ); + return (T) first.next; } /** @@ -139,8 +136,8 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public T getLast() { - log.debug( "returning last node" ); - return last; + log.trace( "returning last node" ); + return (T) last.prev; } /** @@ -150,29 +147,12 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public void makeFirst(final T ln) { - if ( ln.prev == null ) - { - // already the first node. or not a node - return; - } - // splice: remove it from the list ln.prev.next = ln.next; - - if ( ln.next == null ) - { - // last but not the first. - last = (T) ln.prev; - last.next = null; - } - else - { - // neither the last nor the first. - ln.next.prev = ln.prev; - } - first.prev = ln; - ln.next = first; - ln.prev = null; - first = ln; + ln.next.prev = ln.prev; + ln.prev = first; + ln.next = first.next; + first.next.prev = ln; + first.next = ln; } /** @@ -182,29 +162,12 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public void makeLast(final T ln) { - if ( ln.next == null ) - { - // already the last node. or not a node - return; - } - // splice: remove it from the list - if ( ln.prev != null ) - { - ln.prev.next = ln.next; - } - else - { - // first - first = last; - } - - if ( last != null ) - { - last.next = ln; - } - ln.prev = last; - ln.next = null; - last = ln; + ln.prev.next = ln.next; + ln.next.prev = ln.prev; + ln.next = last; + ln.prev = last.prev; + last.prev.next = ln; + last.prev = ln; } /** @@ -215,44 +178,10 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public boolean remove(final T me) { - log.debug( "removing node" ); - - if ( me.next == null ) - { - if ( me.prev == null ) - { - // Make sure it really is the only node before setting head and - // tail to null. It is possible that we will be passed a node - // which has already been removed from the list, in which case - // we should ignore it - - if ( me == first && me == last ) - { - first = last = null; - } - } - else - { - // last but not the first. - last = (T) me.prev; - last.next = null; - me.prev = null; - } - } - else if ( me.prev == null ) - { - // first but not the last. - first = (T) me.next; - first.prev = null; - me.next = null; - } - else - { - // neither the first nor the last. - me.prev.next = me.next; - me.next.prev = me.prev; - me.prev = me.next = null; - } + log.trace("removing node"); + me.prev.next = me.next; + me.next.prev = me.prev; + me.prev = me.next = null; size--; return true; @@ -263,15 +192,14 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public void removeAll() { - for (T me = first; me != null; ) + for (T me = (T) first.next; me != null;) { - if ( me.prev != null ) - { - me.prev = null; - } + me.prev = null; + me.next = null; me = (T) me.next; } - first = last = null; + first.next = last; + last.prev = first; size = 0; } @@ -282,11 +210,11 @@ public class DoubleLinkedList<T extends DoubleLinkedListNode> */ public T removeLast() { - log.debug( "removing last node" ); - final T temp = last; - if ( last != null ) + log.trace("removing last node"); + final T temp = (T) last.prev; + if (last != first) { - remove( last ); + remove(temp); } return temp; } diff --git a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/MockMemoryCache.java b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/MockMemoryCache.java index 2fc2b8fc..17bcbb7e 100644 --- a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/MockMemoryCache.java +++ b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/MockMemoryCache.java @@ -95,23 +95,6 @@ public class MockMemoryCache<K, V> return cacheAttr; } - /** - * @param group - * @return null - */ - public Set<K> getGroupKeys( final String group ) - { - return null; - } - - /** - * @return null - */ - public Set<String> getGroupNames() - { - return null; - } - /** * @return map.keySet().toArray( */ @Override @@ -204,15 +187,6 @@ public class MockMemoryCache<K, V> map.clear(); } - /** - * @param cattr - */ - @Override - public void setCacheAttributes( final ICompositeCacheAttributes cattr ) - { - this.cacheAttr = cattr; - } - /** * @param ce * @throws IOException diff --git a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCacheUnitTest.java b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCacheUnitTest.java index c49ee88f..812d0f66 100644 --- a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCacheUnitTest.java +++ b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/mru/MRUMemoryCacheUnitTest.java @@ -20,6 +20,7 @@ package org.apache.commons.jcs4.engine.memory.mru; */ import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -27,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.jcs4.JCS; import org.apache.commons.jcs4.access.CacheAccess; @@ -35,6 +37,10 @@ import org.apache.commons.jcs4.engine.CacheElement; import org.apache.commons.jcs4.engine.behavior.ICacheElement; import org.apache.commons.jcs4.engine.control.CompositeCache; import org.apache.commons.jcs4.engine.control.CompositeCacheManager; +import org.apache.commons.jcs4.engine.stats.behavior.ICacheStats; +import org.apache.commons.jcs4.engine.stats.behavior.IStatElement; +import org.apache.commons.jcs4.engine.stats.behavior.IStats; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -50,6 +56,12 @@ class MRUMemoryCacheUnitTest JCS.setConfigFilename( "/TestMRUCache.ccf" ); } + @AfterEach + void tearDown() + { + JCS.shutdown(); + } + /** * put the max and clear. verify that no elements remain. * @@ -130,10 +142,21 @@ class MRUMemoryCacheUnitTest cache.put( i + ":key", "myregion data " + i ); } - final String stats = cache.getStatistics().toString(); - - // TODO improve stats check - assertTrue( stats.indexOf( "2000" ) != -1, "Should have 200 puts" ); + final ICacheStats stats = cache.getStatistics(); + boolean found = false; + for (IStats s : stats.getAuxiliaryCacheStats()) + { + for (IStatElement<?> e : s.getStatElements()) + { + if ("Put Count".equals(e.name())) + { + found = true; + assertInstanceOf(AtomicLong.class, e.data()); + assertEquals(items, ((AtomicLong) e.data()).get(), "Should have " + items + " puts"); + } + } + } + assertTrue(found, "Stats should contain Put Count"); } /**
