%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jcs.engine.memory.mru.MRUMemoryCache |
|
|
1 | package org.apache.jcs.engine.memory.mru; |
|
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.io.Serializable; |
|
24 | import java.util.ArrayList; |
|
25 | import java.util.Iterator; |
|
26 | import java.util.LinkedList; |
|
27 | import java.util.ListIterator; |
|
28 | import java.util.Map; |
|
29 | ||
30 | import org.apache.commons.logging.Log; |
|
31 | import org.apache.commons.logging.LogFactory; |
|
32 | import org.apache.jcs.engine.CacheConstants; |
|
33 | import org.apache.jcs.engine.behavior.ICacheElement; |
|
34 | import org.apache.jcs.engine.control.CompositeCache; |
|
35 | import org.apache.jcs.engine.control.group.GroupAttrName; |
|
36 | import org.apache.jcs.engine.control.group.GroupId; |
|
37 | import org.apache.jcs.engine.memory.AbstractMemoryCache; |
|
38 | import org.apache.jcs.engine.stats.StatElement; |
|
39 | import org.apache.jcs.engine.stats.Stats; |
|
40 | import org.apache.jcs.engine.stats.behavior.IStatElement; |
|
41 | import org.apache.jcs.engine.stats.behavior.IStats; |
|
42 | ||
43 | /** |
|
44 | * A SLOW reference management system. The most recently used items move to the |
|
45 | * front of the list and get spooled to disk if the cache hub is configured to |
|
46 | * use a disk cache. |
|
47 | * <p> |
|
48 | * This class is mainly for testing the hub. It also shows that use of the |
|
49 | * Collection LinkedList is far slower than JCS' own double linked list. |
|
50 | */ |
|
51 | 64 | public class MRUMemoryCache |
52 | extends AbstractMemoryCache |
|
53 | { |
|
54 | private static final long serialVersionUID = 5013101678192336129L; |
|
55 | ||
56 | 15 | private final static Log log = LogFactory.getLog( MRUMemoryCache.class ); |
57 | ||
58 | 56 | private int hitCnt = 0; |
59 | ||
60 | 56 | private int missCnt = 0; |
61 | ||
62 | 56 | private int putCnt = 0; |
63 | ||
64 | /** |
|
65 | * Object to lock on the Field |
|
66 | */ |
|
67 | 56 | private int[] lockMe = new class="keyword">int[0]; |
68 | ||
69 | /** |
|
70 | * MRU list. |
|
71 | */ |
|
72 | 56 | private LinkedList mrulist = new LinkedList(); |
73 | ||
74 | /** |
|
75 | * For post reflection creation initialization |
|
76 | * @param hub |
|
77 | */ |
|
78 | public synchronized void initialize( CompositeCache hub ) |
|
79 | { |
|
80 | 56 | super.initialize( hub ); |
81 | 56 | log.info( "Initialized MRUMemoryCache for " + cacheName ); |
82 | 56 | } |
83 | ||
84 | /** |
|
85 | * Puts an item to the cache. |
|
86 | * @param ce |
|
87 | * @exception IOException |
|
88 | */ |
|
89 | public void update( ICacheElement ce ) |
|
90 | throws IOException |
|
91 | { |
|
92 | 104008 | putCnt++; |
93 | ||
94 | 104008 | Serializable key = ce.getKey(); |
95 | 104008 | ce.getElementAttributes().setLastAccessTimeNow(); |
96 | ||
97 | // need a more fine grained locking here |
|
98 | 104008 | boolean replace = false; |
99 | 104008 | if ( map.containsKey( key ) ) |
100 | { |
|
101 | 0 | replace = true; |
102 | } |
|
103 | 117009 | synchronized ( lockMe ) |
104 | { |
|
105 | 104008 | map.put( key, ce ); |
106 | 104008 | if ( replace ) |
107 | { |
|
108 | // the slowest method I've ever seen |
|
109 | 0 | mrulist.remove( key ); |
110 | } |
|
111 | 104008 | mrulist.addFirst( key ); |
112 | 91007 | } |
113 | ||
114 | // save a microsecond on the second call. |
|
115 | 104008 | int size = map.size(); |
116 | // need to spool at a certain percentage synchronously |
|
117 | 104008 | if ( size < this.cattr.getMaxObjects() ) |
118 | { |
|
119 | 65984 | return; |
120 | } |
|
121 | // SPOOL LAST -- need to make this a grouping in a queue |
|
122 | ||
123 | 38024 | if ( log.isDebugEnabled() ) |
124 | { |
|
125 | 0 | log.debug( "In RAM overflow" ); |
126 | } |
|
127 | ||
128 | // write the last item to disk. |
|
129 | try |
|
130 | { |
|
131 | // PUSH more than 1 TO DISK TO MINIMIZE THE TYPICAL spool at each |
|
132 | // put. |
|
133 | 38024 | int chunkSizeCorrected = Math.min( size, chunkSize ); |
134 | ||
135 | 38024 | if ( log.isDebugEnabled() ) |
136 | { |
|
137 | 0 | log.debug( "update: About to spool to disk cache, map.size() = " + size |
138 | + ", this.cattr.getMaxObjects() = " + this.cattr.getMaxObjects() + ", chunkSizeCorrected = " |
|
139 | + chunkSizeCorrected ); |
|
140 | } |
|
141 | ||
142 | // The spool will put them in a disk event queue, so there is no |
|
143 | // need to pre-queue the queuing. This would be a bit wasteful |
|
144 | // and wouldn't save much time in this synchronous call. |
|
145 | 114072 | for ( int i = 0; i < chunkSizeCorrected; i++ ) |
146 | { |
|
147 | // remove the last |
|
148 | 76048 | spoolLastElement(); |
149 | } |
|
150 | ||
151 | 38024 | if ( log.isDebugEnabled() ) |
152 | { |
|
153 | 0 | log.debug( "update: After spool, map.size() = " + size + ", this.cattr.getMaxObjects() = " |
154 | + this.cattr.getMaxObjects() + ", chunkSizeCorrected = " + chunkSizeCorrected ); |
|
155 | } |
|
156 | ||
157 | } |
|
158 | 0 | catch ( Exception ex ) |
159 | { |
|
160 | // impossible case. |
|
161 | 0 | log.error( "Problem updating MRU.", ex ); |
162 | 0 | throw new IllegalStateException( ex.getMessage() ); |
163 | 33271 | } |
164 | 38024 | } |
165 | ||
166 | /** |
|
167 | * This removes the last elemement in the list. |
|
168 | * <p> |
|
169 | * @return ICacheElement if there was a last element, else null. |
|
170 | */ |
|
171 | protected ICacheElement spoolLastElement() |
|
172 | { |
|
173 | 76048 | ICacheElement toSpool = null; |
174 | ||
175 | // need a more fine grained locking here |
|
176 | 85554 | synchronized ( lockMe ) |
177 | { |
|
178 | 76048 | Serializable last = (Serializable) mrulist.removeLast(); |
179 | 76048 | if ( last != null ) |
180 | { |
|
181 | 76048 | toSpool = (ICacheElement) map.get( last ); |
182 | 76048 | map.remove( last ); |
183 | } |
|
184 | 66542 | } |
185 | // Might want to rename this "overflow" incase the hub |
|
186 | // wants to do something else. |
|
187 | 76048 | if ( toSpool != null ) |
188 | { |
|
189 | 76048 | cache.spoolToDisk( toSpool ); |
190 | } |
|
191 | ||
192 | 76048 | return toSpool; |
193 | } |
|
194 | ||
195 | /** |
|
196 | * This instructs the memory cache to remove the <i>numberToFree</i> |
|
197 | * according to its eviction policy. For example, the LRUMemoryCache will |
|
198 | * remove the <i>numberToFree</i> least recently used items. These will be |
|
199 | * spooled to disk if a disk auxiliary is available. |
|
200 | * <p> |
|
201 | * @param numberToFree |
|
202 | * @return the number that were removed. if you ask to free 5, but there are |
|
203 | * only 3, you will get 3. |
|
204 | * @throws IOException |
|
205 | */ |
|
206 | public int freeElements( class="keyword">int numberToFree ) |
|
207 | throws IOException |
|
208 | { |
|
209 | 0 | int freed = 0; |
210 | 0 | for ( ; freed < numberToFree; freed++ ) |
211 | { |
|
212 | 0 | ICacheElement element = spoolLastElement(); |
213 | 0 | if ( element == null ) |
214 | { |
|
215 | 0 | break; |
216 | } |
|
217 | } |
|
218 | 0 | return freed; |
219 | } |
|
220 | ||
221 | /** |
|
222 | * Get an item from the cache without affecting its last access time or |
|
223 | * position. |
|
224 | * @return Element matching key if found, or null |
|
225 | * @param key |
|
226 | * Identifies item to find |
|
227 | * @exception IOException |
|
228 | */ |
|
229 | public ICacheElement getQuiet( Serializable key ) |
|
230 | throws IOException |
|
231 | { |
|
232 | 13280 | ICacheElement ce = null; |
233 | ||
234 | try |
|
235 | { |
|
236 | 13280 | ce = (ICacheElement) map.get( key ); |
237 | 13280 | if ( ce != null ) |
238 | { |
|
239 | 11136 | if ( log.isDebugEnabled() ) |
240 | { |
|
241 | 0 | log.debug( cacheName + ": MRUMemoryCache quiet hit for " + key ); |
242 | 0 | } |
243 | } |
|
244 | else |
|
245 | { |
|
246 | 2144 | log.debug( cacheName + ": MRUMemoryCache quiet miss for " + key ); |
247 | } |
|
248 | } |
|
249 | 0 | catch ( Exception e ) |
250 | { |
|
251 | 0 | log.error( "Problem getting quietly from MRU.", e ); |
252 | 12281 | } |
253 | ||
254 | 13280 | return ce; |
255 | } |
|
256 | ||
257 | /** |
|
258 | * Gets an item out of the map. If it finds an item, it is removed from the |
|
259 | * list and then added to the first position in the linked list. |
|
260 | * @return |
|
261 | * @param key |
|
262 | * @exception IOException |
|
263 | */ |
|
264 | public ICacheElement get( Serializable key ) |
|
265 | throws IOException |
|
266 | { |
|
267 | 56008 | ICacheElement ce = null; |
268 | 56008 | boolean found = false; |
269 | ||
270 | try |
|
271 | { |
|
272 | 56008 | if ( log.isDebugEnabled() ) |
273 | { |
|
274 | 0 | log.debug( "get> key=" + key ); |
275 | 0 | log.debug( "get> key=" + key.toString() ); |
276 | } |
|
277 | ||
278 | 56008 | ce = (ICacheElement) map.get( key ); |
279 | 56008 | if ( log.isDebugEnabled() ) |
280 | { |
|
281 | 0 | log.debug( "ce =" + ce ); |
282 | } |
|
283 | ||
284 | 56008 | if ( ce != null ) |
285 | { |
|
286 | 19976 | found = true; |
287 | 19976 | ce.getElementAttributes().setLastAccessTimeNow(); |
288 | 19976 | hitCnt++; |
289 | 19976 | if ( log.isDebugEnabled() ) |
290 | { |
|
291 | 0 | log.debug( cacheName + " -- RAM-HIT for " + key ); |
292 | } |
|
293 | } |
|
294 | } |
|
295 | 0 | catch ( Exception e ) |
296 | { |
|
297 | 0 | log.error( "Problem getting element.", e ); |
298 | 49007 | } |
299 | ||
300 | try |
|
301 | { |
|
302 | 56008 | if ( !found ) |
303 | { |
|
304 | // Item not found in cache. |
|
305 | 36032 | missCnt++; |
306 | 36032 | if ( log.isDebugEnabled() ) |
307 | { |
|
308 | 0 | log.debug( cacheName + " -- MISS for " + key ); |
309 | } |
|
310 | 36032 | return null; |
311 | } |
|
312 | } |
|
313 | 0 | catch ( Exception e ) |
314 | { |
|
315 | 0 | log.error( "Error handling miss", e ); |
316 | 0 | return null; |
317 | 17479 | } |
318 | ||
319 | try |
|
320 | { |
|
321 | 22473 | synchronized ( lockMe ) |
322 | { |
|
323 | 19976 | mrulist.remove( ce.getKey() ); |
324 | 19976 | mrulist.addFirst( ce.getKey() ); |
325 | 17479 | } |
326 | } |
|
327 | 0 | catch ( Exception e ) |
328 | { |
|
329 | 0 | log.error( "Error making first", e ); |
330 | 0 | return null; |
331 | 17479 | } |
332 | ||
333 | 19976 | return ce; |
334 | } |
|
335 | ||
336 | /** |
|
337 | * Removes an item from the cache. |
|
338 | * @return |
|
339 | * @param key |
|
340 | * @exception IOException |
|
341 | */ |
|
342 | public boolean remove( Serializable key ) |
|
343 | throws IOException |
|
344 | { |
|
345 | 16008 | if ( log.isDebugEnabled() ) |
346 | { |
|
347 | 0 | log.debug( "remove> key=" + key ); |
348 | } |
|
349 | ||
350 | 16008 | boolean removed = false; |
351 | ||
352 | // handle partial removal |
|
353 | 16008 | if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) ) |
354 | { |
|
355 | // remove all keys of the same name hierarchy. |
|
356 | 9 | synchronized ( lockMe ) |
357 | { |
|
358 | 1008 | for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); ) |
359 | { |
|
360 | 7992 | Map.Entry entry = (Map.Entry) itr.next(); |
361 | 7992 | Object k = entry.getKey(); |
362 | 7992 | if ( k instanceof String && k.toString().startsWith( key.toString() ) ) |
363 | { |
|
364 | 4000 | itr.remove(); |
365 | 4000 | Serializable keyR = (Serializable) entry.getKey(); |
366 | // map.remove( keyR ); |
|
367 | 4000 | mrulist.remove( keyR ); |
368 | 4000 | removed = true; |
369 | } |
|
370 | 6993 | } |
371 | 7 | } |
372 | 7 | } |
373 | 16000 | else if ( key instanceof GroupId ) |
374 | { |
|
375 | // remove all keys of the same name hierarchy. |
|
376 | 0 | synchronized ( lockMe ) |
377 | { |
|
378 | 0 | for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); ) |
379 | { |
|
380 | 0 | Map.Entry entry = (Map.Entry) itr.next(); |
381 | 0 | Object k = entry.getKey(); |
382 | ||
383 | 0 | if ( k instanceof GroupAttrName && ( (GroupAttrName) k ).groupId.equals( key ) ) |
384 | { |
|
385 | 0 | itr.remove(); |
386 | 0 | mrulist.remove( k ); |
387 | 0 | removed = true; |
388 | } |
|
389 | 0 | } |
390 | 0 | } |
391 | 0 | } |
392 | else |
|
393 | { |
|
394 | // remove single item. |
|
395 | 16000 | if ( map.containsKey( key ) ) |
396 | { |
|
397 | 8982 | synchronized ( lockMe ) |
398 | { |
|
399 | 7984 | map.remove( key ); |
400 | 7984 | mrulist.remove( key ); |
401 | 6986 | } |
402 | 7984 | removed = true; |
403 | } |
|
404 | } |
|
405 | // end else not hierarchical removal |
|
406 | 16008 | return removed; |
407 | } |
|
408 | ||
409 | /** |
|
410 | * Get an Array of the keys for all elements in the memory cache |
|
411 | * @return Object[] |
|
412 | */ |
|
413 | public Object[] getKeyArray() |
|
414 | { |
|
415 | // need to lock to map here? |
|
416 | 38 | synchronized ( lockMe ) |
417 | { |
|
418 | 38 | return map.keySet().toArray(); |
419 | 0 | } |
420 | } |
|
421 | ||
422 | /** |
|
423 | * Dump the cache map for debugging. |
|
424 | */ |
|
425 | public void dumpMap() |
|
426 | { |
|
427 | 0 | log.debug( "dumpingMap" ); |
428 | 0 | for ( Iterator itr = map.entrySet().iterator(); itr.hasNext(); ) |
429 | { |
|
430 | // for ( Iterator itr = memCache.getIterator(); itr.hasNext();) { |
|
431 | 0 | Map.Entry e = (Map.Entry) itr.next(); |
432 | 0 | ICacheElement ce = (ICacheElement) e.getValue(); |
433 | 0 | log.debug( "dumpMap> key=" + e.getKey() + ", val=" + ce.getVal() ); |
434 | 0 | } |
435 | 0 | } |
436 | ||
437 | /** |
|
438 | * Dump the cache entries from first to list for debugging. |
|
439 | */ |
|
440 | public void dumpCacheEntries() |
|
441 | { |
|
442 | 0 | log.debug( "dumpingCacheEntries" ); |
443 | 0 | ListIterator li = mrulist.listIterator(); |
444 | 0 | while ( li.hasNext() ) |
445 | { |
|
446 | 0 | Serializable key = (Serializable) li.next(); |
447 | 0 | log.debug( "dumpCacheEntries> key=" + key + ", val=" + ( (ICacheElement) map.get( key ) ).getVal() ); |
448 | 0 | } |
449 | 0 | } |
450 | ||
451 | /* |
|
452 | * (non-Javadoc) |
|
453 | * @see org.apache.jcs.engine.memory.MemoryCache#getStatistics() |
|
454 | */ |
|
455 | public IStats getStatistics() |
|
456 | { |
|
457 | 8 | IStats stats = new Stats(); |
458 | 8 | stats.setTypeName( "MRU Memory Cache" ); |
459 | ||
460 | 8 | ArrayList elems = new ArrayList(); |
461 | ||
462 | 8 | IStatElement se = null; |
463 | ||
464 | 8 | se = new StatElement(); |
465 | 8 | se.setName( "List Size" ); |
466 | 8 | se.setData( "" + mrulist.size() ); |
467 | 8 | elems.add( se ); |
468 | ||
469 | 8 | se = new StatElement(); |
470 | 8 | se.setName( "Map Size" ); |
471 | 8 | se.setData( "" + map.size() ); |
472 | 8 | elems.add( se ); |
473 | ||
474 | 8 | se = new StatElement(); |
475 | 8 | se.setName( "Put Count" ); |
476 | 8 | se.setData( "" + putCnt ); |
477 | 8 | elems.add( se ); |
478 | ||
479 | 8 | se = new StatElement(); |
480 | 8 | se.setName( "Hit Count" ); |
481 | 8 | se.setData( "" + hitCnt ); |
482 | 8 | elems.add( se ); |
483 | ||
484 | 8 | se = new StatElement(); |
485 | 8 | se.setName( "Miss Count" ); |
486 | 8 | se.setData( "" + missCnt ); |
487 | 8 | elems.add( se ); |
488 | ||
489 | // get an array and put them in the Stats object |
|
490 | 8 | IStatElement[] ses = (IStatElement[]) elems.toArray( new StatElement[0] ); |
491 | 8 | stats.setStatElements( ses ); |
492 | ||
493 | 8 | return stats; |
494 | } |
|
495 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |