You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tv...@apache.org on 2016/02/07 18:56:53 UTC

svn commit: r1728995 - in /commons/proper/jcs/trunk: commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/ commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/ commons-jcs-core/src/main/java/org/apache/commons/jcs/eng...

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>