1 package org.apache.jcs.engine.control;
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.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.jcs.access.exception.CacheException;
34 import org.apache.jcs.access.exception.ObjectNotFoundException;
35 import org.apache.jcs.auxiliary.AuxiliaryCache;
36 import org.apache.jcs.engine.CacheConstants;
37 import org.apache.jcs.engine.CacheElement;
38 import org.apache.jcs.engine.behavior.ICache;
39 import org.apache.jcs.engine.behavior.ICacheElement;
40 import org.apache.jcs.engine.behavior.ICacheType;
41 import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
42 import org.apache.jcs.engine.behavior.IElementAttributes;
43 import org.apache.jcs.engine.control.event.ElementEvent;
44 import org.apache.jcs.engine.control.event.ElementEventQueue;
45 import org.apache.jcs.engine.control.event.behavior.IElementEvent;
46 import org.apache.jcs.engine.control.event.behavior.IElementEventConstants;
47 import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;
48 import org.apache.jcs.engine.control.event.behavior.IElementEventQueue;
49 import org.apache.jcs.engine.control.group.GroupId;
50 import org.apache.jcs.engine.match.KeyMatcherPatternImpl;
51 import org.apache.jcs.engine.match.behavior.IKeyMatcher;
52 import org.apache.jcs.engine.memory.MemoryCache;
53 import org.apache.jcs.engine.memory.lru.LRUMemoryCache;
54 import org.apache.jcs.engine.memory.util.MemoryElementDescriptor;
55 import org.apache.jcs.engine.stats.CacheStats;
56 import org.apache.jcs.engine.stats.StatElement;
57 import org.apache.jcs.engine.stats.Stats;
58 import org.apache.jcs.engine.stats.behavior.ICacheStats;
59 import org.apache.jcs.engine.stats.behavior.IStatElement;
60 import org.apache.jcs.engine.stats.behavior.IStats;
61
62 /**
63 * This is the primary hub for a single cache/region. It controls the flow of items through the
64 * cache. The auxiliary and memory caches are plugged in here.
65 * <p>
66 * This is the core of a JCS region. Hence, this simple class is the core of JCS.
67 */
68 public class CompositeCache
69 implements ICache, Serializable
70 {
71 /** For serialization. Don't change. */
72 private static final long serialVersionUID = -2838097410378294960L;
73
74 /** log instance */
75 private final static Log log = LogFactory.getLog( CompositeCache.class );
76
77 /**
78 * EventQueue for handling element events. Lazy initialized. One for each region. To be more eficient, the manager
79 * should pass a shared queue in.
80 */
81 public IElementEventQueue elementEventQ;
82
83 /** Auxiliary caches. */
84 private AuxiliaryCache[] auxCaches = new AuxiliaryCache[0];
85
86 /** is this alive? */
87 private boolean alive = true;
88
89 /** TODO - this is in the cacheAttr, shouldn't be used, remove */
90 final String cacheName;
91
92 /** Region Elemental Attributes, default. */
93 private IElementAttributes attr;
94
95 /** Cache Attributes, for hub and memory auxiliary. */
96 private ICompositeCacheAttributes cacheAttr;
97
98 /** How many times update was called. */
99 private int updateCount;
100
101 /** How many times remove was called. */
102 private int removeCount;
103
104 /** Memory cache hit count */
105 private int hitCountRam;
106
107 /** Auxiliary cache hit count (number of times found in ANY auxiliary) */
108 private int hitCountAux;
109
110 /** Auxiliary hit counts broken down by auxiliary. */
111 private int[] auxHitCountByIndex;
112
113 /** Count of misses where element was not found. */
114 private int missCountNotFound = 0;
115
116 /** Count of misses where element was expired. */
117 private int missCountExpired = 0;
118
119 /**
120 * The cache hub can only have one memory cache. This could be made more flexible in the future,
121 * but they are tied closely together. More than one doesn't make much sense.
122 */
123 private MemoryCache memCache;
124
125 /** Key matcher used by the getMatching API */
126 protected IKeyMatcher keyMatcher = new KeyMatcherPatternImpl();
127
128 /**
129 * Constructor for the Cache object
130 * <p>
131 * @param cacheName The name of the region
132 * @param cattr The cache attribute
133 * @param attr The default element attributes
134 */
135 public CompositeCache( String cacheName, ICompositeCacheAttributes cattr, IElementAttributes attr )
136 {
137 this.cacheName = cacheName;
138 this.attr = attr;
139 this.cacheAttr = cattr;
140
141 createMemoryCache( cattr );
142
143 if ( log.isInfoEnabled() )
144 {
145 log.info( "Constructed cache with name [" + cacheName + "] and cache attributes " + cattr );
146 }
147 }
148
149 /**
150 * This sets the list of auxiliary caches for this region.
151 * <p>
152 * @param auxCaches
153 */
154 public void setAuxCaches( AuxiliaryCache[] auxCaches )
155 {
156 this.auxCaches = auxCaches;
157
158 if ( auxCaches != null )
159 {
160 this.auxHitCountByIndex = new int[auxCaches.length];
161 }
162 }
163
164 /**
165 * Standard update method.
166 * <p>
167 * @param ce
168 * @exception IOException
169 */
170 public void update( ICacheElement ce )
171 throws IOException
172 {
173 update( ce, false );
174 }
175
176 /**
177 * Standard update method.
178 * <p>
179 * @param ce
180 * @exception IOException
181 */
182 public void localUpdate( ICacheElement ce )
183 throws IOException
184 {
185 update( ce, true );
186 }
187
188 /**
189 * Put an item into the cache. If it is localOnly, then do no notify remote or lateral
190 * auxiliaries.
191 * <p>
192 * @param cacheElement the ICacheElement
193 * @param localOnly Whether the operation should be restricted to local auxiliaries.
194 * @exception IOException
195 */
196 protected void update( ICacheElement cacheElement, boolean localOnly )
197 throws IOException
198 {
199
200 if ( cacheElement.getKey() instanceof String
201 && cacheElement.getKey().toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
202 {
203 throw new IllegalArgumentException( "key must not end with " + CacheConstants.NAME_COMPONENT_DELIMITER
204 + " for a put operation" );
205 }
206 else if ( cacheElement.getKey() instanceof GroupId )
207 {
208 throw new IllegalArgumentException( "key cannot be a GroupId " + " for a put operation" );
209 }
210
211 if ( log.isDebugEnabled() )
212 {
213 log.debug( "Updating memory cache" );
214 }
215
216 synchronized ( this )
217 {
218 updateCount++;
219
220 memCache.update( cacheElement );
221
222 updateAuxiliaries( cacheElement, localOnly );
223 }
224 }
225
226 /**
227 * This method is responsible for updating the auxiliaries if they are present. If it is local
228 * only, any lateral and remote auxiliaries will not be updated.
229 * <p>
230 * Before updating an auxiliary it checks to see if the element attributes permit the operation.
231 * <p>
232 * Disk auxiliaries are only updated if the disk cache is not merely used as a swap. If the disk
233 * cache is merely a swap, then items will only go to disk when they overflow from memory.
234 * <p>
235 * This is called by update( cacheElement, localOnly ) after it updates the memory cache.
236 * <p>
237 * This is protected to make it testable.
238 * <p>
239 * @param cacheElement
240 * @param localOnly
241 * @throws IOException
242 */
243 protected void updateAuxiliaries( ICacheElement cacheElement, boolean localOnly )
244 throws IOException
245 {
246 // UPDATE AUXILLIARY CACHES
247 // There are 3 types of auxiliary caches: remote, lateral, and disk
248 // more can be added if future auxiliary caches don't fit the model
249 // You could run a database cache as either a remote or a local disk.
250 // The types would describe the purpose.
251
252 if ( log.isDebugEnabled() )
253 {
254 if ( auxCaches.length > 0 )
255 {
256 log.debug( "Updating auxilliary caches" );
257 }
258 else
259 {
260 log.debug( "No auxilliary cache to update" );
261 }
262 }
263
264 for ( int i = 0; i < auxCaches.length; i++ )
265 {
266 ICache aux = auxCaches[i];
267
268 if ( log.isDebugEnabled() )
269 {
270 log.debug( "Auxilliary cache type: " + aux.getCacheType() );
271 }
272
273 if ( aux == null )
274 {
275 continue;
276 }
277
278 // SEND TO REMOTE STORE
279 if ( aux.getCacheType() == ICache.REMOTE_CACHE )
280 {
281 if ( log.isDebugEnabled() )
282 {
283 log.debug( "ce.getElementAttributes().getIsRemote() = "
284 + cacheElement.getElementAttributes().getIsRemote() );
285 }
286
287 if ( cacheElement.getElementAttributes().getIsRemote() && !localOnly )
288 {
289 try
290 {
291 // need to make sure the group cache understands that
292 // the key is a group attribute on update
293 aux.update( cacheElement );
294 if ( log.isDebugEnabled() )
295 {
296 log.debug( "Updated remote store for " + cacheElement.getKey() + cacheElement );
297 }
298 }
299 catch ( IOException ex )
300 {
301 log.error( "Failure in updateExclude", ex );
302 }
303 }
304 // SEND LATERALLY
305 }
306 else if ( aux.getCacheType() == ICache.LATERAL_CACHE )
307 {
308 // lateral can't do the checking since it is dependent on the
309 // cache region restrictions
310 if ( log.isDebugEnabled() )
311 {
312 log.debug( "lateralcache in aux list: cattr " + cacheAttr.getUseLateral() );
313 }
314 if ( cacheAttr.getUseLateral() && cacheElement.getElementAttributes().getIsLateral() && !localOnly )
315 {
316 // DISTRIBUTE LATERALLY
317 // Currently always multicast even if the value is
318 // unchanged, to cause the cache item to move to the front.
319 aux.update( cacheElement );
320 if ( log.isDebugEnabled() )
321 {
322 log.debug( "updated lateral cache for " + cacheElement.getKey() );
323 }
324 }
325 }
326 // update disk if the usage pattern permits
327 else if ( aux.getCacheType() == ICache.DISK_CACHE )
328 {
329 if ( log.isDebugEnabled() )
330 {
331 log.debug( "diskcache in aux list: cattr " + cacheAttr.getUseDisk() );
332 }
333 if ( cacheAttr.getUseDisk()
334 && ( cacheAttr.getDiskUsagePattern() == ICompositeCacheAttributes.DISK_USAGE_PATTERN_UPDATE )
335 && cacheElement.getElementAttributes().getIsSpool() )
336 {
337 aux.update( cacheElement );
338 if ( log.isDebugEnabled() )
339 {
340 log.debug( "updated disk cache for " + cacheElement.getKey() );
341 }
342 }
343 }
344 }
345 }
346
347 /**
348 * Writes the specified element to any disk auxiliaries. Might want to rename this "overflow" in
349 * case the hub wants to do something else.
350 * <p>
351 * If JCS is not configured to use the disk as a swap, that is if the the
352 * CompositeCacheAttribute diskUsagePattern is not SWAP_ONLY, then the item will not be spooled.
353 * <p>
354 * @param ce The CacheElement
355 */
356 public void spoolToDisk( ICacheElement ce )
357 {
358 // if the item is not spoolable, return
359 if ( !ce.getElementAttributes().getIsSpool() )
360 {
361 // there is an event defined for this.
362 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_NOT_ALLOWED );
363 return;
364 }
365
366 boolean diskAvailable = false;
367
368 // SPOOL TO DISK.
369 for ( int i = 0; i < auxCaches.length; i++ )
370 {
371 ICache aux = auxCaches[i];
372
373 if ( aux != null && aux.getCacheType() == ICache.DISK_CACHE )
374 {
375 diskAvailable = true;
376
377 if ( cacheAttr.getDiskUsagePattern() == ICompositeCacheAttributes.DISK_USAGE_PATTERN_SWAP )
378 {
379 // write the last items to disk.2
380 try
381 {
382 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE );
383
384 aux.update( ce );
385 }
386 catch ( IOException ex )
387 {
388 // impossible case.
389 log.error( "Problem spooling item to disk cache.", ex );
390 throw new IllegalStateException( ex.getMessage() );
391 }
392 catch ( Exception oee )
393 {
394 // swallow
395 }
396 if ( log.isDebugEnabled() )
397 {
398 log.debug( "spoolToDisk done for: " + ce.getKey() + " on disk cache[" + i + "]" );
399 }
400 }
401 else
402 {
403 if ( log.isDebugEnabled() )
404 {
405 log.debug( "DiskCache avaialbe, but JCS is not configured to use the DiskCache as a swap." );
406 }
407 }
408 }
409 }
410
411 if ( !diskAvailable )
412 {
413 try
414 {
415 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE );
416 }
417 catch ( Exception e )
418 {
419 log.error( "Trouble handling the ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE element event", e );
420 }
421 }
422 }
423
424 /**
425 * Gets an item from the cache.
426 * <p>
427 * @param key
428 * @return element from the cache, or null if not present
429 * @see org.apache.jcs.engine.behavior.ICache#get(java.io.Serializable)
430 */
431 public ICacheElement get( Serializable key )
432 {
433 return get( key, false );
434 }
435
436 /**
437 * Do not try to go remote or laterally for this get.
438 * <p>
439 * @param key
440 * @return ICacheElement
441 */
442 public ICacheElement localGet( Serializable key )
443 {
444 return get( key, true );
445 }
446
447 /**
448 * Look in memory, then disk, remote, or laterally for this item. The order is dependent on the
449 * order in the cache.ccf file.
450 * <p>
451 * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
452 * remote or lateral if such an auxiliary is configured for this region.
453 * <p>
454 * @param key
455 * @param localOnly
456 * @return ICacheElement
457 */
458 protected ICacheElement get( Serializable key, boolean localOnly )
459 {
460 ICacheElement element = null;
461
462 boolean found = false;
463
464 if ( log.isDebugEnabled() )
465 {
466 log.debug( "get: key = " + key + ", localOnly = " + localOnly );
467 }
468
469 try
470 {
471 // First look in memory cache
472 element = memCache.get( key );
473
474 if ( element != null )
475 {
476 // Found in memory cache
477 if ( isExpired( element ) )
478 {
479 if ( log.isDebugEnabled() )
480 {
481 log.debug( cacheName + " - Memory cache hit, but element expired" );
482 }
483
484 missCountExpired++;
485
486 remove( key );
487
488 element = null;
489 }
490 else
491 {
492 if ( log.isDebugEnabled() )
493 {
494 log.debug( cacheName + " - Memory cache hit" );
495 }
496
497 // Update counters
498 hitCountRam++;
499 }
500
501 found = true;
502 }
503 else
504 {
505 // Item not found in memory. If local invocation look in aux
506 // caches, even if not local look in disk auxiliaries
507
508 for ( int i = 0; i < auxCaches.length; i++ )
509 {
510 AuxiliaryCache aux = auxCaches[i];
511
512 if ( aux != null )
513 {
514 long cacheType = aux.getCacheType();
515
516 if ( !localOnly || cacheType == AuxiliaryCache.DISK_CACHE )
517 {
518 if ( log.isDebugEnabled() )
519 {
520 log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
521 + cacheType );
522 }
523
524 try
525 {
526 element = aux.get( key );
527 }
528 catch ( IOException e )
529 {
530 log.error( "Error getting from aux", e );
531 }
532 }
533
534 if ( log.isDebugEnabled() )
535 {
536 log.debug( "Got CacheElement: " + element );
537 }
538
539 // Item found in one of the auxiliary caches.
540 if ( element != null )
541 {
542 if ( isExpired( element ) )
543 {
544 if ( log.isDebugEnabled() )
545 {
546 log.debug( cacheName + " - Aux cache[" + i + "] hit, but element expired." );
547 }
548
549 missCountExpired++;
550
551 // This will tell the remotes to remove the item
552 // based on the element's expiration policy. The elements attributes
553 // associated with the item when it created govern its behavior
554 // everywhere.
555 remove( key );
556
557 element = null;
558 }
559 else
560 {
561 if ( log.isDebugEnabled() )
562 {
563 log.debug( cacheName + " - Aux cache[" + i + "] hit" );
564 }
565
566 // Update counters
567 hitCountAux++;
568 auxHitCountByIndex[i]++;
569
570 copyAuxiliaryRetrievedItemToMemory( element );
571 }
572
573 found = true;
574
575 break;
576 }
577 }
578 }
579 }
580 }
581 catch ( Exception e )
582 {
583 log.error( "Problem encountered getting element.", e );
584 }
585
586 if ( !found )
587 {
588 missCountNotFound++;
589
590 if ( log.isDebugEnabled() )
591 {
592 log.debug( cacheName + " - Miss" );
593 }
594 }
595
596 return element;
597 }
598
599 /**
600 * Gets multiple items from the cache based on the given set of keys.
601 * <p>
602 * @param keys
603 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
604 * data in cache for any of these keys
605 */
606 public Map<Serializable, ICacheElement> getMultiple( Set<Serializable> keys )
607 {
608 return getMultiple( keys, false );
609 }
610
611 /**
612 * Gets multiple items from the cache based on the given set of keys. Do not try to go remote or
613 * laterally for this data.
614 * <p>
615 * @param keys
616 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
617 * data in cache for any of these keys
618 */
619 public Map<Serializable, ICacheElement> localGetMultiple( Set<Serializable> keys )
620 {
621 return getMultiple( keys, true );
622 }
623
624 /**
625 * Look in memory, then disk, remote, or laterally for these items. The order is dependent on
626 * the order in the cache.ccf file. Keep looking in each cache location until either the element
627 * is found, or the method runs out of places to look.
628 * <p>
629 * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
630 * remote or lateral if such an auxiliary is configured for this region.
631 * <p>
632 * @param keys
633 * @param localOnly
634 * @return ICacheElement
635 */
636 protected Map<Serializable, ICacheElement> getMultiple( Set<Serializable> keys, boolean localOnly )
637 {
638 Map<Serializable, ICacheElement> elements = new HashMap<Serializable, ICacheElement>();
639
640 if ( log.isDebugEnabled() )
641 {
642 log.debug( "get: key = " + keys + ", localOnly = " + localOnly );
643 }
644
645 try
646 {
647 // First look in memory cache
648 elements.putAll( getMultipleFromMemory( keys ) );
649
650 // If fewer than all items were found in memory, then keep looking.
651 if ( elements.size() != keys.size() )
652 {
653 Set<Serializable> remainingKeys = pruneKeysFound( keys, elements );
654 elements.putAll( getMultipleFromAuxiliaryCaches( remainingKeys, localOnly ) );
655 }
656 }
657 catch ( Exception e )
658 {
659 log.error( "Problem encountered getting elements.", e );
660 }
661
662 // if we didn't find all the elements, increment the miss count by the number of elements not found
663 if ( elements.size() != keys.size() )
664 {
665 missCountNotFound += ( keys.size() - elements.size() );
666
667 if ( log.isDebugEnabled() )
668 {
669 log.debug( cacheName + " - " + ( keys.size() - elements.size() ) + " Misses" );
670 }
671 }
672
673 return elements;
674 }
675
676 /**
677 * Gets items for the keys in the set. Returns a map: key -> result.
678 * <p>
679 * @param keys
680 * @return the elements found in the memory cache
681 * @throws IOException
682 */
683 private Map<Serializable, ICacheElement> getMultipleFromMemory( Set<Serializable> keys )
684 throws IOException
685 {
686 Map<Serializable, ICacheElement> elementsFromMemory = memCache.getMultiple( keys );
687
688 Iterator<ICacheElement> elementFromMemoryIterator = new HashMap<Serializable, ICacheElement>( elementsFromMemory ).values().iterator();
689
690 while ( elementFromMemoryIterator.hasNext() )
691 {
692 ICacheElement element = elementFromMemoryIterator.next();
693
694 if ( element != null )
695 {
696 // Found in memory cache
697 if ( isExpired( element ) )
698 {
699 if ( log.isDebugEnabled() )
700 {
701 log.debug( cacheName + " - Memory cache hit, but element expired" );
702 }
703
704 missCountExpired++;
705
706 remove( element.getKey() );
707 elementsFromMemory.remove( element.getKey() );
708 }
709 else
710 {
711 if ( log.isDebugEnabled() )
712 {
713 log.debug( cacheName + " - Memory cache hit" );
714 }
715
716 // Update counters
717 hitCountRam++;
718 }
719 }
720 }
721 return elementsFromMemory;
722 }
723
724 /**
725 * If local invocation look in aux caches, even if not local look in disk auxiliaries.
726 * <p>
727 * @param keys
728 * @param localOnly
729 * @return the elements found in the auxiliary caches
730 * @throws IOException
731 */
732 private Map<Serializable, ICacheElement> getMultipleFromAuxiliaryCaches( Set<Serializable> keys, boolean localOnly )
733 throws IOException
734 {
735 Map<Serializable, ICacheElement> elements = new HashMap<Serializable, ICacheElement>();
736 Set<Serializable> remainingKeys = new HashSet<Serializable>( keys );
737
738 for ( int i = 0; i < auxCaches.length; i++ )
739 {
740 AuxiliaryCache aux = auxCaches[i];
741
742 if ( aux != null )
743 {
744 Map<Serializable, ICacheElement> elementsFromAuxiliary =
745 new HashMap<Serializable, ICacheElement>();
746
747 long cacheType = aux.getCacheType();
748
749 if ( !localOnly || cacheType == AuxiliaryCache.DISK_CACHE )
750 {
751 if ( log.isDebugEnabled() )
752 {
753 log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
754 + cacheType );
755 }
756
757 try
758 {
759 elementsFromAuxiliary.putAll( aux.getMultiple( remainingKeys ) );
760 }
761 catch ( IOException e )
762 {
763 log.error( "Error getting from aux", e );
764 }
765 }
766
767 if ( log.isDebugEnabled() )
768 {
769 log.debug( "Got CacheElements: " + elementsFromAuxiliary );
770 }
771
772 processRetrievedElements( i, elementsFromAuxiliary );
773
774 elements.putAll( elementsFromAuxiliary );
775
776 if ( elements.size() == keys.size() )
777 {
778 break;
779 }
780 else
781 {
782 remainingKeys = pruneKeysFound( keys, elements );
783 }
784 }
785 }
786
787 return elements;
788 }
789
790 /**
791 * Build a map of all the matching elements in all of the auxiliaries and memory.
792 * <p>
793 * @param pattern
794 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
795 * data in cache for any matching keys
796 */
797 public Map<Serializable, ICacheElement> getMatching( String pattern )
798 {
799 return getMatching( pattern, false );
800 }
801
802 /**
803 * Build a map of all the matching elements in all of the auxiliaries and memory. Do not try to
804 * go remote or laterally for this data.
805 * <p>
806 * @param pattern
807 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
808 * data in cache for any matching keys
809 */
810 public Map<Serializable, ICacheElement> localGetMatching( String pattern )
811 {
812 return getMatching( pattern, true );
813 }
814
815 /**
816 * Build a map of all the matching elements in all of the auxiliaries and memory. Items in
817 * memory will replace from the auxiliaries in the returned map. The auxiliaries are accessed in
818 * opposite order. It's assumed that those closer to home are better.
819 * <p>
820 * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
821 * remote or lateral if such an auxiliary is configured for this region.
822 * <p>
823 * @param pattern
824 * @param localOnly
825 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
826 * data in cache for any matching keys
827 */
828 protected Map<Serializable, ICacheElement> getMatching( String pattern, boolean localOnly )
829 {
830 Map<Serializable, ICacheElement> elements = new HashMap<Serializable, ICacheElement>();
831
832 if ( log.isDebugEnabled() )
833 {
834 log.debug( "get: pattern [" + pattern + "], localOnly = " + localOnly );
835 }
836
837 try
838 {
839 // First look in auxiliaries
840 elements.putAll( getMatchingFromAuxiliaryCaches( pattern, localOnly ) );
841
842 // then look in memory, override aux with newer memory items.
843 elements.putAll( getMatchingFromMemory( pattern ) );
844 }
845 catch ( Exception e )
846 {
847 log.error( "Problem encountered getting elements.", e );
848 }
849
850 return elements;
851 }
852
853 /**
854 * Gets the key array from the memcache. Builds a set of matches. Calls getMultiple with the
855 * set. Returns a map: key -> result.
856 * <p>
857 * @param pattern
858 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
859 * data in cache for any matching keys
860 * @throws IOException
861 */
862 protected Map<Serializable, ICacheElement> getMatchingFromMemory( String pattern )
863 throws IOException
864 {
865 // find matches in key array
866 // this avoids locking the memory cache, but it uses more memory
867 Object[] keyArray = memCache.getKeyArray();
868
869 Set<Serializable> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray );
870
871 // call get multiple
872 return getMultipleFromMemory( matchingKeys );
873 }
874
875 /**
876 * If local invocation look in aux caches, even if not local look in disk auxiliaries.
877 * <p>
878 * Moves in reverse order of definition. This will allow you to override those that are fromthe
879 * remote with those on disk.
880 * <p>
881 * @param pattern
882 * @param localOnly
883 * @return a map of Serializable key to ICacheElement element, or an empty map if there is no
884 * data in cache for any matching keys
885 * @throws IOException
886 */
887 private Map<Serializable, ICacheElement> getMatchingFromAuxiliaryCaches( String pattern, boolean localOnly )
888 throws IOException
889 {
890 Map<Serializable, ICacheElement> elements = new HashMap<Serializable, ICacheElement>();
891
892 for ( int i = auxCaches.length - 1; i >= 0; i-- )
893 {
894 AuxiliaryCache aux = auxCaches[i];
895
896 if ( aux != null )
897 {
898 Map<Serializable, ICacheElement> elementsFromAuxiliary =
899 new HashMap<Serializable, ICacheElement>();
900
901 long cacheType = aux.getCacheType();
902
903 if ( !localOnly || cacheType == AuxiliaryCache.DISK_CACHE )
904 {
905 if ( log.isDebugEnabled() )
906 {
907 log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
908 + cacheType );
909 }
910
911 try
912 {
913 elementsFromAuxiliary.putAll( aux.getMatching( pattern ) );
914 }
915 catch ( IOException e )
916 {
917 log.error( "Error getting from aux", e );
918 }
919
920 if ( log.isDebugEnabled() )
921 {
922 log.debug( "Got CacheElements: " + elementsFromAuxiliary );
923 }
924
925 processRetrievedElements( i, elementsFromAuxiliary );
926
927 elements.putAll( elementsFromAuxiliary );
928 }
929 }
930 }
931
932 return elements;
933 }
934
935 /**
936 * Remove expired elements retrieved from an auxiliary. Update memory with good items.
937 * <p>
938 * @param i - the aux index
939 * @param elementsFromAuxiliary
940 * @throws IOException
941 */
942 private void processRetrievedElements( int i, Map<Serializable, ICacheElement> elementsFromAuxiliary )
943 throws IOException
944 {
945 Iterator<ICacheElement> elementFromAuxiliaryIterator = new HashMap<Serializable, ICacheElement>( elementsFromAuxiliary ).values().iterator();
946
947 while ( elementFromAuxiliaryIterator.hasNext() )
948 {
949 ICacheElement element = elementFromAuxiliaryIterator.next();
950
951 // Item found in one of the auxiliary caches.
952 if ( element != null )
953 {
954 if ( isExpired( element ) )
955 {
956 if ( log.isDebugEnabled() )
957 {
958 log.debug( cacheName + " - Aux cache[" + i + "] hit, but element expired." );
959 }
960
961 missCountExpired++;
962
963 // This will tell the remote caches to remove the item
964 // based on the element's expiration policy. The elements attributes
965 // associated with the item when it created govern its behavior
966 // everywhere.
967 remove( element.getKey() );
968 elementsFromAuxiliary.remove( element.getKey() );
969 }
970 else
971 {
972 if ( log.isDebugEnabled() )
973 {
974 log.debug( cacheName + " - Aux cache[" + i + "] hit" );
975 }
976
977 // Update counters
978 hitCountAux++;
979 auxHitCountByIndex[i]++;
980
981 copyAuxiliaryRetrievedItemToMemory( element );
982 }
983 }
984 }
985 }
986
987 /**
988 * Copies the item to memory if the memory size is greater than 0. Only spool if the memory
989 * cache size is greater than 0, else the item will immediately get put into purgatory.
990 * <p>
991 * @param element
992 * @throws IOException
993 */
994 private void copyAuxiliaryRetrievedItemToMemory( ICacheElement element )
995 throws IOException
996 {
997 if ( memCache.getCacheAttributes().getMaxObjects() > 0 )
998 {
999 memCache.update( element );
1000 }
1001 else
1002 {
1003 if ( log.isDebugEnabled() )
1004 {
1005 log.debug( "Skipping memory update since no items are allowed in memory" );
1006 }
1007 }
1008 }
1009
1010 /**
1011 * Returns a set of keys that were not found.
1012 * <p>
1013 * @param keys
1014 * @param foundElements
1015 * @return the original set of cache keys, minus any cache keys present in the map keys of the
1016 * foundElements map
1017 */
1018 private Set<Serializable> pruneKeysFound( Set<Serializable> keys, Map<Serializable, ICacheElement> foundElements )
1019 {
1020 Set<Serializable> remainingKeys = new HashSet<Serializable>( keys );
1021
1022 for (Serializable key : foundElements.keySet())
1023 {
1024 remainingKeys.remove( key );
1025 }
1026
1027 return remainingKeys;
1028 }
1029
1030 /**
1031 * Determine if the element has exceeded its max life.
1032 * <p>
1033 * @param element
1034 * @return true if the element is expired, else false.
1035 */
1036 private boolean isExpired( ICacheElement element )
1037 {
1038 try
1039 {
1040 IElementAttributes attributes = element.getElementAttributes();
1041
1042 if ( !attributes.getIsEternal() )
1043 {
1044 long now = System.currentTimeMillis();
1045
1046 // Remove if maxLifeSeconds exceeded
1047
1048 long maxLifeSeconds = attributes.getMaxLifeSeconds();
1049 long createTime = attributes.getCreateTime();
1050
1051 if ( maxLifeSeconds != -1 && ( now - createTime ) > ( maxLifeSeconds * 1000 ) )
1052 {
1053 if ( log.isDebugEnabled() )
1054 {
1055 log.debug( "Exceeded maxLife: " + element.getKey() );
1056 }
1057
1058 handleElementEvent( element, IElementEventConstants.ELEMENT_EVENT_EXCEEDED_MAXLIFE_ONREQUEST );
1059
1060 return true;
1061 }
1062 long idleTime = attributes.getIdleTime();
1063 long lastAccessTime = attributes.getLastAccessTime();
1064
1065 // Remove if maxIdleTime exceeded
1066 // If you have a 0 size memory cache, then the last access will
1067 // not get updated.
1068 // you will need to set the idle time to -1.
1069
1070 if ( ( idleTime != -1 ) && ( now - lastAccessTime ) > ( idleTime * 1000 ) )
1071 {
1072 if ( log.isDebugEnabled() )
1073 {
1074 log.info( "Exceeded maxIdle: " + element.getKey() );
1075 }
1076
1077 handleElementEvent( element, IElementEventConstants.ELEMENT_EVENT_EXCEEDED_IDLETIME_ONREQUEST );
1078
1079 return true;
1080 }
1081 }
1082 }
1083 catch ( Exception e )
1084 {
1085 log.error( "Error determining expiration period, expiring", e );
1086
1087 return true;
1088 }
1089
1090 return false;
1091 }
1092
1093 /**
1094 * Gets the set of keys of objects currently in the group.
1095 * <p>
1096 * @param group
1097 * @return A Set of keys, or null.
1098 */
1099 public Set<Serializable> getGroupKeys( String group )
1100 {
1101 HashSet<Serializable> allKeys = new HashSet<Serializable>();
1102 allKeys.addAll( memCache.getGroupKeys( group ) );
1103 for ( int i = 0; i < auxCaches.length; i++ )
1104 {
1105 AuxiliaryCache aux = auxCaches[i];
1106 if ( aux != null )
1107 {
1108 try
1109 {
1110 allKeys.addAll( aux.getGroupKeys( group ) );
1111 }
1112 catch ( IOException e )
1113 {
1114 // ignore
1115 }
1116 }
1117 }
1118 return allKeys;
1119 }
1120
1121 /**
1122 * Removes an item from the cache.
1123 * <p>
1124 * @param key
1125 * @return true is it was removed
1126 * @see org.apache.jcs.engine.behavior.ICache#remove(java.io.Serializable)
1127 */
1128 public boolean remove( Serializable key )
1129 {
1130 return remove( key, false );
1131 }
1132
1133 /**
1134 * Do not propagate removeall laterally or remotely.
1135 * <p>
1136 * @param key
1137 * @return true if the item was already in the cache.
1138 */
1139 public boolean localRemove( Serializable key )
1140 {
1141 return remove( key, true );
1142 }
1143
1144 /**
1145 * fromRemote: If a remove call was made on a cache with both, then the remote should have been
1146 * called. If it wasn't then the remote is down. we'll assume it is down for all. If it did come
1147 * from the remote then the cache is remotely configured and lateral removal is unnecessary. If
1148 * it came laterally then lateral removal is unnecessary. Does this assume that there is only
1149 * one lateral and remote for the cache? Not really, the initial removal should take care of the
1150 * problem if the source cache was similarly configured. Otherwise the remote cache, if it had
1151 * no laterals, would remove all the elements from remotely configured caches, but if those
1152 * caches had some other weird laterals that were not remotely configured, only laterally
1153 * propagated then they would go out of synch. The same could happen for multiple remotes. If
1154 * this looks necessary we will need to build in an identifier to specify the source of a
1155 * removal.
1156 * <p>
1157 * @param key
1158 * @param localOnly
1159 * @return true if the item was in the cache, else false
1160 */
1161 protected synchronized boolean remove( Serializable key, boolean localOnly )
1162 {
1163 // not thread safe, but just for debugging and testing.
1164 removeCount++;
1165
1166 boolean removed = false;
1167
1168 try
1169 {
1170 removed = memCache.remove( key );
1171 }
1172 catch ( IOException e )
1173 {
1174 log.error( e );
1175 }
1176
1177 // Removes from all auxiliary caches.
1178 for ( int i = 0; i < auxCaches.length; i++ )
1179 {
1180 ICache aux = auxCaches[i];
1181
1182 if ( aux == null )
1183 {
1184 continue;
1185 }
1186
1187 int cacheType = aux.getCacheType();
1188
1189 // for now let laterals call remote remove but not vice versa
1190
1191 if ( localOnly && ( cacheType == REMOTE_CACHE || cacheType == LATERAL_CACHE ) )
1192 {
1193 continue;
1194 }
1195 try
1196 {
1197 if ( log.isDebugEnabled() )
1198 {
1199 log.debug( "Removing " + key + " from cacheType" + cacheType );
1200 }
1201
1202 boolean b = aux.remove( key );
1203
1204 // Don't take the remote removal into account.
1205 if ( !removed && cacheType != REMOTE_CACHE )
1206 {
1207 removed = b;
1208 }
1209 }
1210 catch ( IOException ex )
1211 {
1212 log.error( "Failure removing from aux", ex );
1213 }
1214 }
1215 return removed;
1216 }
1217
1218 /**
1219 * Clears the region. This command will be sent to all auxiliaries. Some auxiliaries, such as
1220 * the JDBC disk cache, can be configured to not honor removeAll requests.
1221 * <p>
1222 * @see org.apache.jcs.engine.behavior.ICache#removeAll()
1223 */
1224 public void removeAll()
1225 throws IOException
1226 {
1227 removeAll( false );
1228 }
1229
1230 /**
1231 * Will not pass the remove message remotely.
1232 * <p>
1233 * @throws IOException
1234 */
1235 public void localRemoveAll()
1236 throws IOException
1237 {
1238 removeAll( true );
1239 }
1240
1241 /**
1242 * Removes all cached items.
1243 * <p>
1244 * @param localOnly must pass in false to get remote and lateral aux's updated. This prevents
1245 * looping.
1246 * @throws IOException
1247 */
1248 protected synchronized void removeAll( boolean localOnly )
1249 throws IOException
1250 {
1251 try
1252 {
1253 memCache.removeAll();
1254
1255 if ( log.isDebugEnabled() )
1256 {
1257 log.debug( "Removed All keys from the memory cache." );
1258 }
1259 }
1260 catch ( IOException ex )
1261 {
1262 log.error( "Trouble updating memory cache.", ex );
1263 }
1264
1265 // Removes from all auxiliary disk caches.
1266 for ( int i = 0; i < auxCaches.length; i++ )
1267 {
1268 ICache aux = auxCaches[i];
1269
1270 if ( aux != null && ( aux.getCacheType() == ICache.DISK_CACHE || !localOnly ) )
1271 {
1272 try
1273 {
1274 if ( log.isDebugEnabled() )
1275 {
1276 log.debug( "Removing All keys from cacheType" + aux.getCacheType() );
1277 }
1278
1279 aux.removeAll();
1280 }
1281 catch ( IOException ex )
1282 {
1283 log.error( "Failure removing all from aux", ex );
1284 }
1285 }
1286 }
1287 }
1288
1289 /**
1290 * Flushes all cache items from memory to auxiliary caches and close the auxiliary caches.
1291 */
1292 public void dispose()
1293 {
1294 dispose( false );
1295 }
1296
1297 /**
1298 * Invoked only by CacheManager. This method disposes of the auxiliaries one by one. For the
1299 * disk cache, the items in memory are freed, meaning that they will be sent through the
1300 * overflow channel to disk. After the auxiliaries are disposed, the memory cache is disposed.
1301 * <p>
1302 * @param fromRemote
1303 */
1304 public synchronized void dispose( boolean fromRemote )
1305 {
1306 if ( log.isInfoEnabled() )
1307 {
1308 log.info( "In DISPOSE, [" + this.cacheName + "] fromRemote [" + fromRemote + "] \n" + this.getStats() );
1309 }
1310
1311 // If already disposed, return immediately
1312 if ( !alive )
1313 {
1314 return;
1315 }
1316 alive = false;
1317
1318 // Dispose of each auxiliary cache, Remote auxiliaries will be
1319 // skipped if 'fromRemote' is true.
1320
1321 for ( int i = 0; i < auxCaches.length; i++ )
1322 {
1323 try
1324 {
1325 ICache aux = auxCaches[i];
1326
1327 // Skip this auxiliary if:
1328 // - The auxiliary is null
1329 // - The auxiliary is not alive
1330 // - The auxiliary is remote and the invocation was remote
1331
1332 if ( aux == null || aux.getStatus() != CacheConstants.STATUS_ALIVE
1333 || ( fromRemote && aux.getCacheType() == REMOTE_CACHE ) )
1334 {
1335 if ( log.isInfoEnabled() )
1336 {
1337 log.info( "In DISPOSE, [" + this.cacheName + "] SKIPPING auxiliary [" + aux + "] fromRemote ["
1338 + fromRemote + "]" );
1339 }
1340 continue;
1341 }
1342
1343 if ( log.isInfoEnabled() )
1344 {
1345 log.info( "In DISPOSE, [" + this.cacheName + "] auxiliary [" + aux + "]" );
1346 }
1347
1348 // IT USED TO BE THE CASE THAT (If the auxiliary is not a lateral, or the cache
1349 // attributes
1350 // have 'getUseLateral' set, all the elements currently in
1351 // memory are written to the lateral before disposing)
1352 // I changed this. It was excessive. Only the disk cache needs the items, since only
1353 // the disk cache
1354 // is in a situation to not get items on a put.
1355
1356 if ( aux.getCacheType() == ICacheType.DISK_CACHE )
1357 {
1358 int numToFree = memCache.getSize();
1359 memCache.freeElements( numToFree );
1360
1361 if ( log.isInfoEnabled() )
1362 {
1363 log.info( "In DISPOSE, [" + this.cacheName + "] put " + numToFree + " into auxiliary " + aux );
1364 }
1365 }
1366
1367 // Dispose of the auxiliary
1368 aux.dispose();
1369 }
1370 catch ( IOException ex )
1371 {
1372 log.error( "Failure disposing of aux.", ex );
1373 }
1374 }
1375
1376 if ( log.isInfoEnabled() )
1377 {
1378 log.info( "In DISPOSE, [" + this.cacheName + "] disposing of memory cache." );
1379 }
1380 try
1381 {
1382 memCache.dispose();
1383 }
1384 catch ( IOException ex )
1385 {
1386 log.error( "Failure disposing of memCache", ex );
1387 }
1388 }
1389
1390 /**
1391 * Calling save cause the entire contents of the memory cache to be flushed to all auxiliaries.
1392 * Though this put is extremely fast, this could bog the cache and should be avoided. The
1393 * dispose method should call a version of this. Good for testing.
1394 */
1395 public void save()
1396 {
1397 synchronized ( this )
1398 {
1399 if ( !alive )
1400 {
1401 return;
1402 }
1403 alive = false;
1404
1405 for ( int i = 0; i < auxCaches.length; i++ )
1406 {
1407 try
1408 {
1409 ICache aux = auxCaches[i];
1410
1411 if ( aux.getStatus() == CacheConstants.STATUS_ALIVE )
1412 {
1413
1414 Iterator<Map.Entry<Serializable, MemoryElementDescriptor>> itr =
1415 memCache.getIterator();
1416
1417 while (itr.hasNext())
1418 {
1419 Map.Entry<Serializable, MemoryElementDescriptor> entry = itr.next();
1420
1421 ICacheElement ce = entry.getValue().ce;
1422
1423 aux.update( ce );
1424 }
1425 }
1426 }
1427 catch ( IOException ex )
1428 {
1429 log.error( "Failure saving aux caches.", ex );
1430 }
1431 }
1432 }
1433 if ( log.isDebugEnabled() )
1434 {
1435 log.debug( "Called save for [" + cacheName + "]" );
1436 }
1437 }
1438
1439 /**
1440 * Gets the size attribute of the Cache object. This return the number of elements, not the byte
1441 * size.
1442 * <p>
1443 * @return The size value
1444 */
1445 public int getSize()
1446 {
1447 return memCache.getSize();
1448 }
1449
1450 /**
1451 * Gets the cacheType attribute of the Cache object.
1452 * <p>
1453 * @return The cacheType value
1454 */
1455 public int getCacheType()
1456 {
1457 return CACHE_HUB;
1458 }
1459
1460 /**
1461 * Gets the status attribute of the Cache object.
1462 * <p>
1463 * @return The status value
1464 */
1465 public synchronized int getStatus()
1466 {
1467 return alive ? CacheConstants.STATUS_ALIVE : CacheConstants.STATUS_DISPOSED;
1468 }
1469
1470 /**
1471 * Gets stats for debugging.
1472 * <p>
1473 * @return String
1474 */
1475 public String getStats()
1476 {
1477 return getStatistics().toString();
1478 }
1479
1480 /**
1481 * This returns data gathered for this region and all the auxiliaries it currently uses.
1482 * <p>
1483 * @return Statistics and Info on the Region.
1484 */
1485 public ICacheStats getStatistics()
1486 {
1487 ICacheStats stats = new CacheStats();
1488 stats.setRegionName( this.getCacheName() );
1489
1490 // store the composite cache stats first
1491 IStatElement[] elems = new StatElement[2];
1492 elems[0] = new StatElement();
1493 elems[0].setName( "HitCountRam" );
1494 elems[0].setData( "" + getHitCountRam() );
1495
1496 elems[1] = new StatElement();
1497 elems[1].setName( "HitCountAux" );
1498 elems[1].setData( "" + getHitCountAux() );
1499
1500 // store these local stats
1501 stats.setStatElements( elems );
1502
1503 // memory + aux, memory is not considered an auxiliary internally
1504 int total = auxCaches.length + 1;
1505 IStats[] auxStats = new Stats[total];
1506
1507 auxStats[0] = getMemoryCache().getStatistics();
1508
1509 for ( int i = 0; i < auxCaches.length; i++ )
1510 {
1511 AuxiliaryCache aux = auxCaches[i];
1512 auxStats[i + 1] = aux.getStatistics();
1513 }
1514
1515 // sore the auxiliary stats
1516 stats.setAuxiliaryCacheStats( auxStats );
1517
1518 return stats;
1519 }
1520
1521 /**
1522 * Gets the cacheName attribute of the Cache object. This is also known as the region name.
1523 * <p>
1524 * @return The cacheName value
1525 */
1526 public String getCacheName()
1527 {
1528 return cacheName;
1529 }
1530
1531 /**
1532 * Gets the default element attribute of the Cache object This returna a copy. It does not
1533 * return a reference to the attributes.
1534 * <p>
1535 * @return The attributes value
1536 */
1537 public IElementAttributes getElementAttributes()
1538 {
1539 if ( attr != null )
1540 {
1541 return attr.copy();
1542 }
1543 return null;
1544 }
1545
1546 /**
1547 * Sets the default element attribute of the Cache object.
1548 * <p>
1549 * @param attr
1550 */
1551 public void setElementAttributes( IElementAttributes attr )
1552 {
1553 this.attr = attr;
1554 }
1555
1556 /**
1557 * Gets the ICompositeCacheAttributes attribute of the Cache object.
1558 * <p>
1559 * @return The ICompositeCacheAttributes value
1560 */
1561 public ICompositeCacheAttributes getCacheAttributes()
1562 {
1563 return this.cacheAttr;
1564 }
1565
1566 /**
1567 * Sets the ICompositeCacheAttributes attribute of the Cache object.
1568 * <p>
1569 * @param cattr The new ICompositeCacheAttributes value
1570 */
1571 public void setCacheAttributes( ICompositeCacheAttributes cattr )
1572 {
1573 this.cacheAttr = cattr;
1574 // need a better way to do this, what if it is in error
1575 this.memCache.initialize( this );
1576 }
1577
1578 /**
1579 * Gets the elementAttributes attribute of the Cache object.
1580 * <p>
1581 * @param key
1582 * @return The elementAttributes value
1583 * @exception CacheException
1584 * @exception IOException
1585 */
1586 public IElementAttributes getElementAttributes( Serializable key )
1587 throws CacheException, IOException
1588 {
1589 CacheElement ce = (CacheElement) get( key );
1590 if ( ce == null )
1591 {
1592 throw new ObjectNotFoundException( "key " + key + " is not found" );
1593 }
1594 return ce.getElementAttributes();
1595 }
1596
1597 /**
1598 * Create the MemoryCache based on the config parameters. TODO: consider making this an
1599 * auxiliary, despite its close tie to the CacheHub. TODO: might want to create a memory cache
1600 * config file separate from that of the hub -- ICompositeCacheAttributes
1601 * <p>
1602 * @param cattr
1603 */
1604 private void createMemoryCache( ICompositeCacheAttributes cattr )
1605 {
1606 if ( memCache == null )
1607 {
1608 try
1609 {
1610 Class<?> c = Class.forName( cattr.getMemoryCacheName() );
1611 memCache = (MemoryCache) c.newInstance();
1612 memCache.initialize( this );
1613 }
1614 catch ( Exception e )
1615 {
1616 log.warn( "Failed to init mem cache, using: LRUMemoryCache", e );
1617
1618 this.memCache = new LRUMemoryCache();
1619 this.memCache.initialize( this );
1620 }
1621 }
1622 else
1623 {
1624 log.warn( "Refusing to create memory cache -- already exists." );
1625 }
1626 }
1627
1628 /**
1629 * Access to the memory cache for instrumentation.
1630 * <p>
1631 * @return the MemoryCache implementation
1632 */
1633 public MemoryCache getMemoryCache()
1634 {
1635 return memCache;
1636 }
1637
1638 /**
1639 * Number of times a requested item was found in the memory cache.
1640 * <p>
1641 * @return number of hits in memory
1642 */
1643 public int getHitCountRam()
1644 {
1645 return hitCountRam;
1646 }
1647
1648 /**
1649 * Number of times a requested item was found in and auxiliary cache.
1650 * @return number of auxiliary hits.
1651 */
1652 public int getHitCountAux()
1653 {
1654 return hitCountAux;
1655 }
1656
1657 /**
1658 * Number of times a requested element was not found.
1659 * @return number of misses.
1660 */
1661 public int getMissCountNotFound()
1662 {
1663 return missCountNotFound;
1664 }
1665
1666 /**
1667 * Number of times a requested element was found but was expired.
1668 * @return number of found but expired gets.
1669 */
1670 public int getMissCountExpired()
1671 {
1672 return missCountExpired;
1673 }
1674
1675 /**
1676 * If there are event handlers for the item, then create an event and queue it up.
1677 * <p>
1678 * This does not call handle directly; instead the handler and the event are put into a queue.
1679 * This prevents the event handling from blocking normal cache operations.
1680 * <p>
1681 * @param ce
1682 * @param eventType
1683 */
1684 private void handleElementEvent( ICacheElement ce, int eventType )
1685 {
1686 // handle event, might move to a new method
1687 ArrayList<IElementEventHandler> eventHandlers = ce.getElementAttributes().getElementEventHandlers();
1688 if ( eventHandlers != null )
1689 {
1690 if ( log.isDebugEnabled() )
1691 {
1692 log.debug( "Element Handlers are registered. Create event type " + eventType );
1693 }
1694 IElementEvent event = new ElementEvent( ce, eventType );
1695 for (IElementEventHandler hand : eventHandlers)
1696 {
1697 try
1698 {
1699 addElementEvent( hand, event );
1700 }
1701 catch ( Exception e )
1702 {
1703 log.error( "Trouble adding element event to queue", e );
1704 }
1705 }
1706 }
1707 }
1708
1709 /**
1710 * Adds an ElementEvent to be handled to the queue.
1711 * <p>
1712 * @param hand The IElementEventHandler
1713 * @param event The IElementEventHandler IElementEvent event
1714 * @exception IOException Description of the Exception
1715 */
1716 public void addElementEvent( IElementEventHandler hand, IElementEvent event )
1717 throws IOException
1718 {
1719 if ( log.isDebugEnabled() )
1720 {
1721 log.debug( "Adding event to Element Event Queue" );
1722 }
1723 // lazy init
1724 synchronized ( this )
1725 {
1726 if ( elementEventQ == null )
1727 {
1728 elementEventQ = new ElementEventQueue( this.getCacheName() );
1729 }
1730 }
1731 elementEventQ.addElementEvent( hand, event );
1732 }
1733
1734 /**
1735 * Sets the key matcher used by get matching.
1736 * <p>
1737 * @param keyMatcher
1738 */
1739 public void setKeyMatcher( IKeyMatcher keyMatcher )
1740 {
1741 if ( keyMatcher != null )
1742 {
1743 this.keyMatcher = keyMatcher;
1744 }
1745 }
1746
1747 /**
1748 * Returns the key matcher used by get matching.
1749 * <p>
1750 * @return keyMatcher
1751 */
1752 public IKeyMatcher getKeyMatcher()
1753 {
1754 return this.keyMatcher;
1755 }
1756
1757 /**
1758 * @param updateCount The updateCount to set.
1759 */
1760 public synchronized void setUpdateCount( int updateCount )
1761 {
1762 this.updateCount = updateCount;
1763 }
1764
1765 /**
1766 * @return Returns the updateCount.
1767 */
1768 public synchronized int getUpdateCount()
1769 {
1770 return updateCount;
1771 }
1772
1773 /**
1774 * @param removeCount The removeCount to set.
1775 */
1776 public synchronized void setRemoveCount( int removeCount )
1777 {
1778 this.removeCount = removeCount;
1779 }
1780
1781 /**
1782 * @return Returns the removeCount.
1783 */
1784 public synchronized int getRemoveCount()
1785 {
1786 return removeCount;
1787 }
1788
1789 /**
1790 * This returns the stats.
1791 * <p>
1792 * @return getStats()
1793 */
1794 @Override
1795 public String toString()
1796 {
1797 return getStats();
1798 }
1799 }