View Javadoc
1   package org.apache.commons.jcs.engine.memory.lru;
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.util.Collections;
24  import java.util.Iterator;
25  import java.util.LinkedHashSet;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.commons.jcs.engine.CacheConstants;
30  import org.apache.commons.jcs.engine.behavior.ICacheElement;
31  import org.apache.commons.jcs.engine.control.CompositeCache;
32  import org.apache.commons.jcs.engine.control.group.GroupAttrName;
33  import org.apache.commons.jcs.engine.memory.AbstractMemoryCache;
34  import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
35  import org.apache.commons.jcs.engine.stats.behavior.IStats;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * This is a test memory manager using the jdk1.4 LinkedHashMap.
41   */
42  public class LHMLRUMemoryCache<K, V>
43      extends AbstractMemoryCache<K, V>
44  {
45      /** The Logger. */
46      private static final Log log = LogFactory.getLog( LRUMemoryCache.class );
47  
48      /**
49       * For post reflection creation initialization
50       * <p>
51       * @param hub
52       */
53      @Override
54      public void initialize( CompositeCache<K, V> hub )
55      {
56          super.initialize( hub );
57          log.info( "initialized LHMLRUMemoryCache for " + getCacheName() );
58      }
59  
60      /**
61       * Returns a synchronized LHMSpooler
62       * <p>
63       * @return Collections.synchronizedMap( new LHMSpooler() )
64       */
65      @Override
66      public Map<K, MemoryElementDescriptor<K, V>> createMap()
67      {
68          return Collections.synchronizedMap( new LHMSpooler() );
69      }
70  
71      /**
72       * Puts an item to the cache.
73       * <p>
74       * @param ce Description of the Parameter
75       * @throws IOException
76       */
77      @Override
78      public void update( ICacheElement<K, V> ce )
79          throws IOException
80      {
81          putCnt.incrementAndGet();
82          map.put( ce.getKey(), new MemoryElementDescriptor<K, V>(ce) );
83      }
84  
85      /**
86       * Get an item from the cache
87       * <p>
88       * @param key Identifies item to find
89       * @return ICacheElement&lt;K, V&gt; if found, else null
90       * @throws IOException
91       */
92      @Override
93      public ICacheElement<K, V> get( K key )
94          throws IOException
95      {
96          if ( log.isDebugEnabled() )
97          {
98              log.debug( "getting item from cache " + getCacheName() + " for key " + key );
99          }
100 
101         MemoryElementDescriptor<K, V> me = map.get( key );
102 
103         if ( me != null )
104         {
105             hitCnt.incrementAndGet();
106             if ( log.isDebugEnabled() )
107             {
108                 log.debug( getCacheName() + ": LHMLRUMemoryCache hit for " + key );
109             }
110             return me.getCacheElement();
111         }
112         else
113         {
114             missCnt.incrementAndGet();
115             if ( log.isDebugEnabled() )
116             {
117                 log.debug( getCacheName() + ": LHMLRUMemoryCache miss for " + key );
118             }
119         }
120 
121         return null;
122     }
123 
124     /**
125      * Removes an item from the cache. This method handles hierarchical removal. If the key is a
126      * String and ends with the CacheConstants.NAME_COMPONENT_DELIMITER, then all items with keys
127      * starting with the argument String will be removed.
128      * <p>
129      * @param key
130      * @return true if removed
131      * @throws IOException
132      */
133     @Override
134     public boolean remove( K key )
135         throws IOException
136     {
137         if ( log.isDebugEnabled() )
138         {
139             log.debug( "removing item for key: " + key );
140         }
141 
142         boolean removed = false;
143 
144         // handle partial removal
145         if ( key instanceof String && ( (String) key ).endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
146         {
147             // remove all keys of the same name hierarchy.
148             synchronized ( map )
149             {
150                 for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
151                 {
152                     Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
153                     K k = entry.getKey();
154 
155                     if ( k instanceof String && ( (String) k ).startsWith( key.toString() ) )
156                     {
157                         itr.remove();
158                         removed = true;
159                     }
160                 }
161             }
162         }
163         else if ( key instanceof GroupAttrName && ((GroupAttrName<?>)key).attrName == null )
164         {
165             // remove all keys of the same name hierarchy.
166             synchronized ( map )
167             {
168                 for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
169                 {
170                     Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
171                     K k = entry.getKey();
172 
173                     if ( k instanceof GroupAttrName &&
174                         ((GroupAttrName<?>)k).groupId.equals(((GroupAttrName<?>)key).groupId) )
175                     {
176                         itr.remove();
177                         removed = true;
178                     }
179                 }
180             }
181         }
182         else
183         {
184             // remove single item.
185             MemoryElementDescriptor<K, V> me = map.remove( key );
186             if ( me != null )
187             {
188                 removed = true;
189             }
190         }
191 
192         return removed;
193     }
194 
195     /**
196      * Get an Array of the keys for all elements in the memory cache
197      * <p>
198      * @return An Object[]
199      */
200     @Override
201     public Set<K> getKeySet()
202     {
203         return new LinkedHashSet<K>(map.keySet());
204     }
205 
206     /**
207      * This returns semi-structured information on the memory cache, such as the size, put count,
208      * hit count, and miss count.
209      * <p>
210      * @return IStats
211      */
212     @Override
213     public IStats getStatistics()
214     {
215         IStats stats = super.getStatistics();
216         stats.setTypeName( "LHMLRU Memory Cache" );
217 
218         return stats;
219     }
220 
221     // ---------------------------------------------------------- debug methods
222 
223     /**
224      * Dump the cache entries from first to last for debugging.
225      */
226     public void dumpCacheEntries()
227     {
228         dumpMap();
229     }
230 
231     /**
232      * This can't be implemented.
233      * <p>
234      * @param numberToFree
235      * @return 0
236      * @throws IOException
237      */
238     @Override
239     public int freeElements( int numberToFree )
240         throws IOException
241     {
242         // can't be implemented using the LHM
243         return 0;
244     }
245 
246     // ---------------------------------------------------------- extended map
247 
248     /**
249      * Implementation of removeEldestEntry in LinkedHashMap
250      */
251     protected class LHMSpooler
252         extends java.util.LinkedHashMap<K, MemoryElementDescriptor<K, V>>
253     {
254         /** Don't change. */
255         private static final long serialVersionUID = -1255907868906762484L;
256 
257         /**
258          * Initialize to a small size--for now, 1/2 of max 3rd variable "true" indicates that it
259          * should be access and not time governed. This could be configurable.
260          */
261         public LHMSpooler()
262         {
263             super( (int) ( getCacheAttributes().getMaxObjects() * .5 ), .75F, true );
264         }
265 
266         /**
267          * Remove eldest. Automatically called by LinkedHashMap.
268          * <p>
269          * @param eldest
270          * @return true if removed
271          */
272         @SuppressWarnings("synthetic-access")
273         @Override
274         protected boolean removeEldestEntry( Map.Entry<K, MemoryElementDescriptor<K, V>> eldest )
275         {
276             ICacheElement<K, V> element = eldest.getValue().getCacheElement();
277 
278             if ( size() <= getCacheAttributes().getMaxObjects() )
279             {
280                 return false;
281             }
282             else
283             {
284 
285                 if ( log.isDebugEnabled() )
286                 {
287                     log.debug( "LHMLRU max size: " + getCacheAttributes().getMaxObjects()
288                         + ".  Spooling element, key: " + element.getKey() );
289                 }
290 
291                 waterfal( element );
292 
293                 if ( log.isDebugEnabled() )
294                 {
295                     log.debug( "LHMLRU size: " + map.size() );
296                 }
297             }
298             return true;
299         }
300     }
301 }