Author: tv Date: Sun Feb 7 17:56:52 2016 New Revision: 1728995 URL: http://svn.apache.org/viewvc?rev=1728995&view=rev Log: JCS-54 Add soft reference memory cache
Added: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java (with props) commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html (with props) commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java (with props) commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/ commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java (with props) commons/proper/jcs/trunk/commons-jcs-core/src/test/test-conf/TestSoftReferenceCache.ccf Modified: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java commons/proper/jcs/trunk/src/changes/changes.xml Modified: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java?rev=1728995&r1=1728994&r2=1728995&view=diff ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java (original) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java Sun Feb 7 17:56:52 2016 @@ -26,7 +26,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.jcs.engine.CacheConstants; import org.apache.commons.jcs.engine.behavior.ICacheElement; @@ -58,13 +58,13 @@ public abstract class AbstractDoubleLink protected DoubleLinkedList<MemoryElementDescriptor<K, V>> list; // TODO privatise /** number of hits */ - private AtomicInteger hitCnt; + private AtomicLong hitCnt; /** number of misses */ - private AtomicInteger missCnt; + private AtomicLong missCnt; /** number of puts */ - private AtomicInteger putCnt; + private AtomicLong putCnt; /** * For post reflection creation initialization. @@ -79,9 +79,9 @@ public abstract class AbstractDoubleLink try { super.initialize(hub); - hitCnt = new AtomicInteger(0); - missCnt = new AtomicInteger(0); - putCnt = new AtomicInteger(0); + hitCnt = new AtomicLong(0); + missCnt = new AtomicLong(0); + putCnt = new AtomicLong(0); list = new DoubleLinkedList<MemoryElementDescriptor<K, V>>(); log.info("initialized MemoryCache for " + getCacheName()); } @@ -92,6 +92,21 @@ public abstract class AbstractDoubleLink } /** + * Reset statistics + * + * @see org.apache.commons.jcs.engine.memory.AbstractMemoryCache#dispose() + */ + @Override + public void dispose() throws IOException + { + super.dispose(); + removeAll(); + hitCnt.set(0); + missCnt.set(0); + putCnt.set(0); + } + + /** * This is called by super initialize. * * NOTE: should return a thread safe map @@ -127,10 +142,11 @@ public abstract class AbstractDoubleLink MemoryElementDescriptor<K, V> newNode = adjustListForUpdate(ce); // this should be synchronized if we were not using a ConcurrentHashMap - MemoryElementDescriptor<K, V> oldNode = map.put(newNode.ce.getKey(), newNode); + final K key = newNode.getCacheElement().getKey(); + MemoryElementDescriptor<K, V> oldNode = map.put(key, newNode); // If the node was the same as an existing node, remove it. - if (oldNode != null && newNode.ce.getKey().equals(oldNode.ce.getKey())) + if (oldNode != null && key.equals(oldNode.getCacheElement().getKey())) { list.remove(oldNode); } @@ -246,7 +262,7 @@ public abstract class AbstractDoubleLink lock.lock(); try { - ce = me.ce; + ce = me.getCacheElement(); // ABSTRACT adjustListForGet(me); } @@ -337,7 +353,7 @@ public abstract class AbstractDoubleLink final MemoryElementDescriptor<K, V> last = list.getLast(); if (last != null) { - toSpool = last.ce; + toSpool = last.getCacheElement(); if (toSpool != null) { getCompositeCache().spoolToDisk(toSpool); @@ -542,7 +558,7 @@ public abstract class AbstractDoubleLink log.debug("dumpingCacheEntries"); for (MemoryElementDescriptor<K, V> me = list.getFirst(); me != null; me = (MemoryElementDescriptor<K, V>) me.next) { - log.debug("dumpCacheEntries> key=" + me.ce.getKey() + ", val=" + me.ce.getVal()); + log.debug("dumpCacheEntries> key=" + me.getCacheElement().getKey() + ", val=" + me.getCacheElement().getVal()); } } @@ -560,11 +576,10 @@ public abstract class AbstractDoubleLink log.debug("verifycache: checking linked list by key "); for (MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next) { - Object key = li.ce.getKey(); + K key = li.getCacheElement().getKey(); if (!map.containsKey(key)) { - log.error("verifycache[" + getCacheName() + "]: map does not contain key : " + li.ce.getKey()); - log.error("li.hashcode=" + li.ce.getKey().hashCode()); + log.error("verifycache[" + getCacheName() + "]: map does not contain key : " + key); log.error("key class=" + key.getClass()); log.error("key hashcode=" + key.hashCode()); log.error("key toString=" + key.toString()); @@ -578,9 +593,9 @@ public abstract class AbstractDoubleLink } dumpMap(); } - else if (map.get(li.ce.getKey()) == null) + else if (map.get(key) == null) { - log.error("verifycache[" + getCacheName() + "]: linked list retrieval returned null for key: " + li.ce.getKey()); + log.error("verifycache[" + getCacheName() + "]: linked list retrieval returned null for key: " + key); } } @@ -601,7 +616,7 @@ public abstract class AbstractDoubleLink for (MemoryElementDescriptor<K, V> li2 = list.getFirst(); li2 != null; li2 = (MemoryElementDescriptor<K, V>) li2.next) { - if (val.equals(li2.ce.getKey())) + if (val.equals(li2.getCacheElement().getKey())) { found = true; break; @@ -638,7 +653,7 @@ public abstract class AbstractDoubleLink // go through the linked list looking for the key for (MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next) { - if (li.ce.getKey() == key) + if (li.getCacheElement().getKey() == key) { found = true; log.debug("verifycache(key) key match: " + key); @@ -682,9 +697,9 @@ public abstract class AbstractDoubleLink { elems.add(new StatElement<Integer>("List Size", Integer.valueOf(list.size()))); elems.add(new StatElement<Integer>("Map Size", Integer.valueOf(map.size()))); - elems.add(new StatElement<AtomicInteger>("Put Count", putCnt)); - elems.add(new StatElement<AtomicInteger>("Hit Count", hitCnt)); - elems.add(new StatElement<AtomicInteger>("Miss Count", missCnt)); + elems.add(new StatElement<AtomicLong>("Put Count", putCnt)); + elems.add(new StatElement<AtomicLong>("Hit Count", hitCnt)); + elems.add(new StatElement<AtomicLong>("Miss Count", missCnt)); } finally { Modified: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java?rev=1728995&r1=1728994&r2=1728995&view=diff ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java (original) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java Sun Feb 7 17:56:52 2016 @@ -176,7 +176,7 @@ public abstract class AbstractMemoryCach log.debug( cacheName + ": MemoryCache quiet hit for " + key ); } - ce = me.ce; + ce = me.getCacheElement(); } else if ( log.isDebugEnabled() ) { @@ -269,7 +269,9 @@ public abstract class AbstractMemoryCach { String attributeCacheName = this.cacheAttributes.getCacheName(); if(attributeCacheName != null) + { return attributeCacheName; + } return cacheName; } @@ -296,7 +298,7 @@ public abstract class AbstractMemoryCach for (Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet()) { MemoryElementDescriptor<K, V> me = e.getValue(); - log.debug( "dumpMap> key=" + e.getKey() + ", val=" + me.ce.getVal() ); + log.debug( "dumpMap> key=" + e.getKey() + ", val=" + me.getCacheElement().getVal() ); } } Modified: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java?rev=1728995&r1=1728994&r2=1728995&view=diff ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java (original) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java Sun Feb 7 17:56:52 2016 @@ -20,14 +20,13 @@ package org.apache.commons.jcs.engine.me */ import java.io.IOException; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.jcs.engine.CacheConstants; import org.apache.commons.jcs.engine.behavior.ICacheElement; @@ -45,20 +44,20 @@ import org.apache.commons.logging.LogFac /** * This is a test memory manager using the jdk1.4 LinkedHashMap. */ -public class LHMLRUMemoryCache<K extends Serializable, V extends Serializable> +public class LHMLRUMemoryCache<K, V> extends AbstractMemoryCache<K, V> { /** The Logger. */ private static final Log log = LogFactory.getLog( LRUMemoryCache.class ); /** number of hits */ - private AtomicInteger hitCnt; + private AtomicLong hitCnt; /** number of misses */ - private AtomicInteger missCnt; + private AtomicLong missCnt; /** number of puts */ - private AtomicInteger putCnt; + private AtomicLong putCnt; /** * For post reflection creation initialization @@ -66,12 +65,12 @@ public class LHMLRUMemoryCache<K extends * @param hub */ @Override - public synchronized void initialize( CompositeCache<K, V> hub ) + public void initialize( CompositeCache<K, V> hub ) { super.initialize( hub ); - hitCnt = new AtomicInteger(0); - missCnt = new AtomicInteger(0); - putCnt = new AtomicInteger(0); + hitCnt = new AtomicLong(0); + missCnt = new AtomicLong(0); + putCnt = new AtomicLong(0); log.info( "initialized LHMLRUMemoryCache for " + getCacheName() ); } @@ -112,7 +111,14 @@ public class LHMLRUMemoryCache<K extends public ICacheElement<K, V> getQuiet( K key ) throws IOException { - return map.get( key ).ce; + MemoryElementDescriptor<K, V> me = map.get( key ); + + if (me != null) + { + return me.getCacheElement(); + } + + return null; } /** @@ -126,14 +132,12 @@ public class LHMLRUMemoryCache<K extends public synchronized ICacheElement<K, V> get( K key ) throws IOException { - MemoryElementDescriptor<K, V> me = null; - if ( log.isDebugEnabled() ) { log.debug( "getting item from cache " + getCacheName() + " for key " + key ); } - me = map.get( key ); + MemoryElementDescriptor<K, V> me = map.get( key ); if ( me != null ) { @@ -142,7 +146,7 @@ public class LHMLRUMemoryCache<K extends { log.debug( getCacheName() + ": LHMLRUMemoryCache hit for " + key ); } - return me.ce; + return me.getCacheElement(); } else { @@ -258,9 +262,9 @@ public class LHMLRUMemoryCache<K extends ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>(); elems.add(new StatElement<Integer>( "Map Size", Integer.valueOf(map.size()) ) ); - elems.add(new StatElement<AtomicInteger>("Put Count", putCnt)); - elems.add(new StatElement<AtomicInteger>("Hit Count", hitCnt)); - elems.add(new StatElement<AtomicInteger>("Miss Count", missCnt)); + elems.add(new StatElement<AtomicLong>("Put Count", putCnt)); + elems.add(new StatElement<AtomicLong>("Hit Count", hitCnt)); + elems.add(new StatElement<AtomicLong>("Miss Count", missCnt)); stats.setStatElements( elems ); @@ -325,7 +329,7 @@ public class LHMLRUMemoryCache<K extends @Override protected boolean removeEldestEntry( Map.Entry<K, MemoryElementDescriptor<K, V>> eldest ) { - ICacheElement<K, V> element = eldest.getValue().ce; + ICacheElement<K, V> element = eldest.getValue().getCacheElement(); if ( size() <= getCacheAttributes().getMaxObjects() ) { Added: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java?rev=1728995&view=auto ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java (added) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java Sun Feb 7 17:56:52 2016 @@ -0,0 +1,388 @@ +package org.apache.commons.jcs.engine.memory.soft; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.jcs.engine.CacheConstants; +import org.apache.commons.jcs.engine.behavior.ICacheElement; +import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes; +import org.apache.commons.jcs.engine.control.CompositeCache; +import org.apache.commons.jcs.engine.control.group.GroupAttrName; +import org.apache.commons.jcs.engine.memory.AbstractMemoryCache; +import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor; +import org.apache.commons.jcs.engine.memory.util.SoftReferenceElementDescriptor; +import org.apache.commons.jcs.engine.stats.StatElement; +import org.apache.commons.jcs.engine.stats.Stats; +import org.apache.commons.jcs.engine.stats.behavior.IStatElement; +import org.apache.commons.jcs.engine.stats.behavior.IStats; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A JCS {@link MemoryCache} that has {@link SoftReference} too all its values. + * This cache does not respect {@link ICompositeCacheAttributes#getMaxObjects()} + * as overflowing is handled by java gc. + * <p> + * The cache also has strong references to a maximum number of objects given by + * the maxObjects parameter + * + * @author halset + */ +public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> +{ + /** The logger. */ + private static final Log log = LogFactory.getLog(SoftReferenceMemoryCache.class); + + /** + * Strong references to the maxObjects number of newest objects. + * <p> + * Trimming is done by {@link #trimStrongReferences()} instead of by + * overriding removeEldestEntry to be able to control waterfalling as easy + * as possible + */ + private LinkedBlockingQueue<ICacheElement<K, V>> strongReferences; + + /** number of hits */ + private AtomicLong hitCnt; + + /** number of misses */ + private AtomicLong missCnt; + + /** number of puts */ + private AtomicLong putCnt; + + /** + * For post reflection creation initialization + * <p> + * @param hub + */ + @Override + public synchronized void initialize( CompositeCache<K, V> hub ) + { + super.initialize( hub ); + strongReferences = new LinkedBlockingQueue<ICacheElement<K, V>>(); + hitCnt = new AtomicLong(0); + missCnt = new AtomicLong(0); + putCnt = new AtomicLong(0); + log.info( "initialized Soft Reference Memory Cache for " + getCacheName() ); + } + + /** + * @see org.apache.commons.jcs.engine.memory.AbstractMemoryCache#createMap() + */ + @Override + public Map<K, MemoryElementDescriptor<K, V>> createMap() + { + return new ConcurrentHashMap<K, MemoryElementDescriptor<K, V>>(); + } + + /** + * @see org.apache.commons.jcs.engine.memory.behavior.IMemoryCache#getKeySet() + */ + @Override + public Set<K> getKeySet() + { + Set<K> keys = new HashSet<K>(); + for (Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet()) + { + SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) e.getValue(); + if (sred.getCacheElement() != null) + { + keys.add(e.getKey()); + } + } + + return keys; + } + + /** + * Returns the current cache size. + * <p> + * @return The size value + */ + @Override + public int getSize() + { + int size = 0; + for (MemoryElementDescriptor<K, V> me : map.values()) + { + SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) me; + if (sred.getCacheElement() != null) + { + size++; + } + } + return size; + } + + /** + * @return statistics about the cache + */ + @Override + public IStats getStatistics() + { + ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>(); + + int size = getSize(); + int emptyrefs = map.size() - size; + + elems.add(new StatElement<Integer>("Size", Integer.valueOf(size))); + elems.add(new StatElement<Integer>("Empty References", Integer.valueOf(emptyrefs))); + elems.add(new StatElement<Integer>("Strong References", Integer.valueOf(strongReferences.size()))); + elems.add(new StatElement<AtomicLong>("Put Count", putCnt)); + elems.add(new StatElement<AtomicLong>("Hit Count", hitCnt)); + elems.add(new StatElement<AtomicLong>("Miss Count", missCnt)); + + IStats stats = new Stats(); + stats.setTypeName("Soft Reference Memory Cache"); + stats.setStatElements(elems); + + return stats; + } + + /** + * 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 + * starting with the argument String will be removed. + * <p> + * + * @param key + * @return true if the removal was successful + * @throws IOException + */ + @Override + public boolean remove(K key) throws IOException + { + if (log.isDebugEnabled()) + { + log.debug("removing item for key: " + key); + } + + boolean removed = false; + + // handle partial removal + if (key instanceof String && ((String) key).endsWith(CacheConstants.NAME_COMPONENT_DELIMITER)) + { + // remove all keys of the same name hierarchy. + for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); + itr.hasNext();) + { + Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next(); + K k = entry.getKey(); + + if (k instanceof String && ((String) k).startsWith(key.toString())) + { + lock.lock(); + try + { + strongReferences.remove(entry.getValue().getCacheElement()); + itr.remove(); + removed = true; + } + finally + { + lock.unlock(); + } + } + } + } + else if (key instanceof GroupAttrName && ((GroupAttrName<?>) key).attrName == null) + { + // remove all keys of the same name hierarchy. + for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); + itr.hasNext();) + { + Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next(); + K k = entry.getKey(); + + if (k instanceof GroupAttrName && ((GroupAttrName<?>) k).groupId.equals(((GroupAttrName<?>) key).groupId)) + { + lock.lock(); + try + { + strongReferences.remove(entry.getValue().getCacheElement()); + itr.remove(); + removed = true; + } + finally + { + lock.unlock(); + } + } + } + } + else + { + // remove single item. + lock.lock(); + try + { + MemoryElementDescriptor<K, V> me = map.remove(key); + if (me != null) + { + strongReferences.remove(me.getCacheElement()); + removed = true; + } + } + finally + { + lock.unlock(); + } + } + + return removed; + } + + /** + * Removes all cached items from the cache. + * <p> + * @throws IOException + */ + @Override + public void removeAll() throws IOException + { + super.removeAll(); + strongReferences.clear(); + } + + /** + * Puts an item to the cache. + * <p> + * @param ce Description of the Parameter + * @throws IOException Description of the Exception + */ + @Override + public void update(ICacheElement<K, V> ce) throws IOException + { + putCnt.incrementAndGet(); + ce.getElementAttributes().setLastAccessTimeNow(); + + lock.lock(); + + try + { + map.put(ce.getKey(), new SoftReferenceElementDescriptor<K, V>(ce)); + strongReferences.add(ce); + trimStrongReferences(); + } + finally + { + lock.unlock(); + } + } + + /** + * Trim the number of strong references to equal or below the number given + * by the maxObjects parameter. + * + * @throws IOException + */ + private void trimStrongReferences() throws IOException + { + int max = getCacheAttributes().getMaxObjects(); + int startsize = strongReferences.size(); + + for (int cursize = startsize; cursize > max; cursize--) + { + ICacheElement<K, V> ce = strongReferences.poll(); + waterfal(ce); + } + } + + /** + * Get an item from the cache + * <p> + * @param key Description of the Parameter + * @return Description of the Return Value + * @throws IOException Description of the Exception + */ + @Override + public ICacheElement<K, V> get(K key) throws IOException + { + ICacheElement<K, V> val = null; + lock.lock(); + + try + { + val = getQuiet(key); + if (val != null) + { + val.getElementAttributes().setLastAccessTimeNow(); + + // update the ordering of the strong references + strongReferences.add(val); + trimStrongReferences(); + } + } + finally + { + lock.unlock(); + } + + if (val == null) + { + missCnt.incrementAndGet(); + } + else + { + hitCnt.incrementAndGet(); + } + + return val; + } + + /** + * Prepares for shutdown. + * <p> + * @throws IOException + */ + @Override + public void dispose() throws IOException + { + super.dispose(); + removeAll(); + hitCnt.set(0); + missCnt.set(0); + putCnt.set(0); + } + + /** + * This can't be implemented. + * <p> + * @param numberToFree + * @return 0 + * @throws IOException + */ + @Override + public int freeElements(int numberToFree) throws IOException + { + return 0; + } +} Propchange: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCache.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html?rev=1728995&view=auto ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html (added) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html Sun Feb 7 17:56:52 2016 @@ -0,0 +1,25 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + 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. +--> +<html> + <head> + </head> + <body> + A memory plugin implemented using soft references. + </body> +</html> Propchange: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/soft/package.html ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java?rev=1728995&r1=1728994&r2=1728995&view=diff ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java (original) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java Sun Feb 7 17:56:52 2016 @@ -32,7 +32,7 @@ public class MemoryElementDescriptor<K, private static final long serialVersionUID = -1905161209035522460L; /** The CacheElement wrapped by this descriptor */ - public final ICacheElement<K, V> ce; // TODO privatise + private final ICacheElement<K, V> ce; /** * Constructs a usable MemoryElementDescriptor. @@ -44,4 +44,12 @@ public class MemoryElementDescriptor<K, super( ce ); this.ce = ce; } + + /** + * @return the ce + */ + public ICacheElement<K, V> getCacheElement() + { + return ce; + } } Added: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java?rev=1728995&view=auto ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java (added) +++ commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java Sun Feb 7 17:56:52 2016 @@ -0,0 +1,62 @@ +package org.apache.commons.jcs.engine.memory.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.lang.ref.SoftReference; + +import org.apache.commons.jcs.engine.behavior.ICacheElement; + +/** + * This wrapper is needed for double linked lists. + */ +public class SoftReferenceElementDescriptor<K, V> + extends MemoryElementDescriptor<K, V> +{ + /** Don't change */ + private static final long serialVersionUID = -1905161209035522460L; + + /** The CacheElement wrapped by this descriptor */ + private final SoftReference<ICacheElement<K, V>> srce; + + /** + * Constructs a usable MemoryElementDescriptor. + * <p> + * @param ce + */ + public SoftReferenceElementDescriptor( ICacheElement<K, V> ce ) + { + super( ce ); + this.srce = new SoftReference<ICacheElement<K, V>>(ce); + } + + /** + * @return the ce + */ + @Override + public ICacheElement<K, V> getCacheElement() + { + if (srce != null) + { + return srce.get(); + } + + return null; + } +} Propchange: commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/SoftReferenceElementDescriptor.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java?rev=1728995&view=auto ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java (added) +++ commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java Sun Feb 7 17:56:52 2016 @@ -0,0 +1,247 @@ +package org.apache.commons.jcs.engine.memory.soft; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.apache.commons.jcs.access.exception.CacheException; +import org.apache.commons.jcs.engine.CacheElement; +import org.apache.commons.jcs.engine.behavior.ICacheElement; +import org.apache.commons.jcs.engine.control.CompositeCache; +import org.apache.commons.jcs.engine.control.CompositeCacheManager; + +/** + * Tests for the test Soft reference implementation. + * <p> + * @author Aaron Smuts + */ +public class SoftReferenceMemoryCacheUnitTest + extends TestCase +{ + /** Test setup */ + @Override + public void setUp() + { + JCS.setConfigFilename( "/TestSoftReferenceCache.ccf" ); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + CompositeCacheManager.getInstance().shutDown(); + } + + /** + * Verify that the cache gets used by a non-defined region when it is set as the default in the + * default region. + * <p> + * @throws CacheException + */ + public void testLoadFromCCF() + throws CacheException + { + CacheAccess<String, String> cache = JCS.getInstance( "testPutGet" ); + String memoryCacheName = cache.getCacheAttributes().getMemoryCacheName(); + assertTrue( "Cache name should have SoftReference in it.", + memoryCacheName.indexOf( "SoftReferenceMemoryCache" ) != -1 ); + } + + /** + * put twice as many as the max. verify that all are in the cache. + * <p> + * @throws CacheException + */ + public void testPutGetThroughHub() + throws CacheException + { + CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" ); + + int max = cache.getCacheAttributes().getMaxObjects(); + int items = max * 2; + + for ( int i = 0; i < items; i++ ) + { + cache.put( i + ":key", "myregion" + " data " + i ); + } + + // Test that all items are in cache + for ( int i = 0; i < items; i++ ) + { + String value = cache.get( i + ":key" ); + assertEquals( "myregion" + " data " + i, value ); + } + + // Test that getMultiple returns all the items remaining in cache and none of the missing ones + Set<String> keys = new HashSet<String>(); + for ( int i = 0; i < items; i++ ) + { + keys.add( i + ":key" ); + } + + Map<String, ICacheElement<String, String>> elements = cache.getCacheElements( keys ); + for ( int i = 0; i < items; i++ ) + { + ICacheElement<String, String> element = elements.get( i + ":key" ); + assertNotNull( "element " + i + ":key is missing", element ); + assertEquals( "value " + i + ":key", "myregion" + " data " + i, element.getVal() ); + } + + // System.out.println(cache.getStats()); + } + + /** + * put the max and remove each. verify that they are all null. + * <p> + * @throws CacheException + */ + public void testPutRemoveThroughHub() + throws CacheException + { + CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" ); + + int max = cache.getCacheAttributes().getMaxObjects(); + int items = max * 2; + + for ( int i = 0; i < items; i++ ) + { + cache.put( i + ":key", "myregion" + " data " + i ); + } + + for ( int i = 0; i < items; i++ ) + { + cache.remove( i + ":key" ); + } + + // Test that first items are not in the cache + for ( int i = max; i >= 0; i-- ) + { + String value = cache.get( i + ":key" ); + assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value ); + } + } + + /** + * put the max and clear. verify that no elements remain. + * <p> + * @throws CacheException + */ + public void testClearThroughHub() + throws CacheException + { + CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" ); + + int max = cache.getCacheAttributes().getMaxObjects(); + int items = max * 2; + + for ( int i = 0; i < items; i++ ) + { + cache.put( i + ":key", "myregion" + " data " + i ); + } + + cache.clear(); + + // Test that first items are not in the cache + for ( int i = max; i >= 0; i-- ) + { + String value = cache.get( i + ":key" ); + assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value ); + } + } + + /** + * Put half the max and clear. get the key array and verify that it has the correct number of + * items. + * <p> + * @throws Exception + */ + public void testGetKeyArray() + throws Exception + { + CompositeCacheManager cacheMgr = CompositeCacheManager.getUnconfiguredInstance(); + cacheMgr.configure( "/TestSoftReferenceCache.ccf" ); + CompositeCache<String, String> cache = cacheMgr.getCache( "testGetKeyArray" ); + + SoftReferenceMemoryCache<String, String> srmc = new SoftReferenceMemoryCache<String, String>(); + srmc.initialize( cache ); + + int max = cache.getCacheAttributes().getMaxObjects(); + int items = max / 2; + + for ( int i = 0; i < items; i++ ) + { + ICacheElement<String, String> ice = new CacheElement<String, String>( cache.getCacheName(), i + ":key", cache.getCacheName() + " data " + i ); + ice.setElementAttributes( cache.getElementAttributes() ); + srmc.update( ice ); + } + + Set<String> keys = srmc.getKeySet(); + + assertEquals( "Wrong number of keys.", items, keys.size() ); + } + + /** + * Add a few keys with the delimiter. Remove them. + * <p> + * @throws CacheException + */ + public void testRemovePartialThroughHub() + throws CacheException + { + CacheAccess<String, String> cache = JCS.getInstance( "testGetStatsThroughHub" ); + + int max = cache.getCacheAttributes().getMaxObjects(); + int items = max / 2; + + cache.put( "test", "data" ); + + String root = "myroot"; + + for ( int i = 0; i < items; i++ ) + { + cache.put( root + ":" + i + ":key", "myregion" + " data " + i ); + } + + // Test that last items are in cache + for ( int i = 0; i < items; i++ ) + { + String value = cache.get( root + ":" + i + ":key" ); + assertEquals( "myregion" + " data " + i, value ); + } + + // remove partial + cache.remove( root + ":" ); + + for ( int i = 0; i < items; i++ ) + { + assertNull( "Should have been removed by partial loop.", cache.get( root + ":" + i + ":key" ) ); + } + + assertNotNull( "Other item should be in the cache.", cache.get( "test" ) ); + } +} Propchange: commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/soft/SoftReferenceMemoryCacheUnitTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: commons/proper/jcs/trunk/commons-jcs-core/src/test/test-conf/TestSoftReferenceCache.ccf URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/test/test-conf/TestSoftReferenceCache.ccf?rev=1728995&view=auto ============================================================================== --- commons/proper/jcs/trunk/commons-jcs-core/src/test/test-conf/TestSoftReferenceCache.ccf (added) +++ commons/proper/jcs/trunk/commons-jcs-core/src/test/test-conf/TestSoftReferenceCache.ccf Sun Feb 7 17:56:52 2016 @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. +# JCS Config for unit testing + +jcs.default= +jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.default.cacheattributes.MaxObjects=100 +jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.soft.SoftReferenceMemoryCache +jcs.default.cacheattributes.UseMemoryShrinker=false +jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600 +jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes +jcs.default.elementattributes.IsEternal=false +jcs.default.elementattributes.MaxLife=600 +jcs.default.elementattributes.IdleTime=1800 +jcs.default.elementattributes.IsSpool=true +jcs.default.elementattributes.IsRemote=true +jcs.default.elementattributes.IsLateral=true + +# Region defined that uses the Soft Reference +jcs.region.srDefined= +jcs.region.srDefined.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.region.srDefined.cacheattributes.MaxObjects=100000 +jcs.region.srDefined.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.soft.SoftReferenceMemoryCache Modified: commons/proper/jcs/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/jcs/trunk/src/changes/changes.xml?rev=1728995&r1=1728994&r2=1728995&view=diff ============================================================================== --- commons/proper/jcs/trunk/src/changes/changes.xml (original) +++ commons/proper/jcs/trunk/src/changes/changes.xml Sun Feb 7 17:56:52 2016 @@ -20,6 +20,9 @@ </properties> <body> <release version="2.0" date="unreleased" description="JDK 1.6 based major release"> + <action issue="JCS-54" dev="tv" type="add" due-to="Tore Halset"> + Add soft reference memory cache + </action> <action issue="JCS-78" dev="tv" type="fix" due-to="Marko Stipanov"> Fix: RemoteCacheStartupServlet can't start with config outside classpath </action>