View Javadoc
1   package org.apache.commons.jcs3.engine.memory.soft;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.lang.ref.SoftReference;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  import java.util.concurrent.LinkedBlockingQueue;
31  
32  import org.apache.commons.jcs3.engine.behavior.ICacheElement;
33  import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
34  import org.apache.commons.jcs3.engine.control.CompositeCache;
35  import org.apache.commons.jcs3.engine.memory.AbstractMemoryCache;
36  import org.apache.commons.jcs3.engine.memory.util.MemoryElementDescriptor;
37  import org.apache.commons.jcs3.engine.memory.util.SoftReferenceElementDescriptor;
38  import org.apache.commons.jcs3.engine.stats.StatElement;
39  import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
40  import org.apache.commons.jcs3.engine.stats.behavior.IStats;
41  import org.apache.commons.jcs3.log.Log;
42  import org.apache.commons.jcs3.log.LogManager;
43  
44  /**
45   * A JCS IMemoryCache that has {@link SoftReference} to all its values.
46   * This cache does not respect {@link ICompositeCacheAttributes#getMaxObjects()}
47   * as overflowing is handled by Java GC.
48   * <p>
49   * The cache also has strong references to a maximum number of objects given by
50   * the maxObjects parameter
51   * </p>
52   */
53  public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V>
54  {
55      /** The logger. */
56      private static final Log log = LogManager.getLog(SoftReferenceMemoryCache.class);
57  
58      /**
59       * Strong references to the maxObjects number of newest objects.
60       * <p>
61       * Trimming is done by {@link #trimStrongReferences()} instead of by
62       * overriding removeEldestEntry to be able to control waterfalling as easy
63       * as possible
64       */
65      private LinkedBlockingQueue<ICacheElement<K, V>> strongReferences;
66  
67      /**
68       * For post reflection creation initialization
69       * <p>
70       * @param hub
71       */
72      @Override
73      public synchronized void initialize( final CompositeCache<K, V> hub )
74      {
75          super.initialize( hub );
76          strongReferences = new LinkedBlockingQueue<>();
77          log.info( "initialized Soft Reference Memory Cache for {0}",
78                  this::getCacheName );
79      }
80  
81      /**
82       * @see org.apache.commons.jcs3.engine.memory.AbstractMemoryCache#createMap()
83       */
84      @Override
85      public ConcurrentMap<K, MemoryElementDescriptor<K, V>> createMap()
86      {
87          return new ConcurrentHashMap<>();
88      }
89  
90      /**
91       * @see org.apache.commons.jcs3.engine.memory.behavior.IMemoryCache#getKeySet()
92       */
93      @Override
94      public Set<K> getKeySet()
95      {
96          final Set<K> keys = new HashSet<>();
97          for (final Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet())
98          {
99              final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) e.getValue();
100             if (sred.getCacheElement() != null)
101             {
102                 keys.add(e.getKey());
103             }
104         }
105 
106         return keys;
107     }
108 
109     /**
110      * Returns the current cache size.
111      * <p>
112      * @return The size value
113      */
114     @Override
115     public int getSize()
116     {
117         int size = 0;
118         for (final MemoryElementDescriptor<K, V> me : map.values())
119         {
120             final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) me;
121             if (sred.getCacheElement() != null)
122             {
123                 size++;
124             }
125         }
126         return size;
127     }
128 
129     /**
130      * @return statistics about the cache
131      */
132     @Override
133     public IStats getStatistics()
134     {
135         final IStats stats = super.getStatistics();
136         stats.setTypeName("Soft Reference Memory Cache");
137 
138         final List<IStatElement<?>> elems = stats.getStatElements();
139         final int emptyrefs = map.size() - getSize();
140         elems.add(new StatElement<>("Empty References", Integer.valueOf(emptyrefs)));
141         elems.add(new StatElement<>("Strong References", Integer.valueOf(strongReferences.size())));
142 
143         return stats;
144     }
145 
146     /**
147      * Update control structures after get
148      * (guarded by the lock)
149      *
150      * @param me the memory element descriptor
151      */
152     @Override
153     protected void lockedGetElement(final MemoryElementDescriptor<K, V> me)
154     {
155         final ICacheElement<K, V> val = me.getCacheElement();
156         val.getElementAttributes().setLastAccessTimeNow();
157 
158         // update the ordering of the strong references
159         strongReferences.add(val);
160         trimStrongReferences();
161     }
162 
163     /**
164      * Remove element from control structure
165      * (guarded by the lock)
166      *
167      * @param me the memory element descriptor
168      */
169     @Override
170     protected void lockedRemoveElement(final MemoryElementDescriptor<K, V> me)
171     {
172         strongReferences.remove(me.getCacheElement());
173     }
174 
175     /**
176      * Removes all cached items from the cache control structures.
177      * (guarded by the lock)
178      */
179     @Override
180     protected void lockedRemoveAll()
181     {
182         strongReferences.clear();
183     }
184 
185     /**
186      * Puts an item to the cache.
187      * <p>
188      * @param ce Description of the Parameter
189      * @throws IOException Description of the Exception
190      */
191     @Override
192     public void update(final ICacheElement<K, V> ce) throws IOException
193     {
194         putCnt.incrementAndGet();
195         ce.getElementAttributes().setLastAccessTimeNow();
196 
197         lock.lock();
198 
199         try
200         {
201             map.put(ce.getKey(), new SoftReferenceElementDescriptor<>(ce));
202             strongReferences.add(ce);
203             trimStrongReferences();
204         }
205         finally
206         {
207             lock.unlock();
208         }
209     }
210 
211     /**
212      * Trim the number of strong references to equal or below the number given
213      * by the maxObjects parameter.
214      */
215     private void trimStrongReferences()
216     {
217         final int max = getCacheAttributes().getMaxObjects();
218         final int startsize = strongReferences.size();
219 
220         for (int cursize = startsize; cursize > max; cursize--)
221         {
222             final ICacheElement<K, V> ce = strongReferences.poll();
223             waterfal(ce);
224         }
225     }
226 
227     /**
228      * This can't be implemented.
229      * <p>
230      * @param numberToFree
231      * @return 0
232      * @throws IOException
233      */
234     @Override
235     public int freeElements(final int numberToFree) throws IOException
236     {
237         return 0;
238     }
239 }