1 package org.apache.jcs.engine.control;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Serializable;
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.jcs.access.exception.CacheException;
33 import org.apache.jcs.access.exception.ObjectNotFoundException;
34 import org.apache.jcs.auxiliary.AuxiliaryCache;
35 import org.apache.jcs.engine.CacheConstants;
36 import org.apache.jcs.engine.CacheElement;
37 import org.apache.jcs.engine.behavior.ICache;
38 import org.apache.jcs.engine.behavior.ICacheElement;
39 import org.apache.jcs.engine.behavior.ICacheType;
40 import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
41 import org.apache.jcs.engine.behavior.IElementAttributes;
42 import org.apache.jcs.engine.control.event.ElementEvent;
43 import org.apache.jcs.engine.control.event.ElementEventQueue;
44 import org.apache.jcs.engine.control.event.behavior.IElementEvent;
45 import org.apache.jcs.engine.control.event.behavior.IElementEventConstants;
46 import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;
47 import org.apache.jcs.engine.control.event.behavior.IElementEventQueue;
48 import org.apache.jcs.engine.control.group.GroupId;
49 import org.apache.jcs.engine.memory.MemoryCache;
50 import org.apache.jcs.engine.memory.lru.LRUMemoryCache;
51 import org.apache.jcs.engine.stats.CacheStats;
52 import org.apache.jcs.engine.stats.StatElement;
53 import org.apache.jcs.engine.stats.Stats;
54 import org.apache.jcs.engine.stats.behavior.ICacheStats;
55 import org.apache.jcs.engine.stats.behavior.IStatElement;
56 import org.apache.jcs.engine.stats.behavior.IStats;
57
58 /***
59 * This is the primary hub for a single cache/region. It controls the flow of items through the
60 * cache. The auxiliary and memory caches are plugged in here.
61 * <p>
62 * This is the core of a JCS region. Hence, this simple class is the core of JCS.
63 */
64 public class CompositeCache
65 implements ICache, Serializable
66 {
67 private static final long serialVersionUID = -2838097410378294960L;
68
69 private final static Log log = LogFactory.getLog( CompositeCache.class );
70
71 /***
72 * EventQueue for handling element events. 1 should be enough for all the regions. Else should
73 * create as needed per region.
74 */
75 public static IElementEventQueue elementEventQ = new ElementEventQueue( "AllRegionQueue" );
76
77
78 private AuxiliaryCache[] auxCaches = new AuxiliaryCache[0];
79
80 private boolean alive = true;
81
82
83 final String cacheName;
84
85 /*** Region Elemental Attributes, default. */
86 private IElementAttributes attr;
87
88 /*** Cache Attributes, for hub and memory auxiliary. */
89 private ICompositeCacheAttributes cacheAttr;
90
91
92 private int updateCount;
93
94 private int removeCount;
95
96 /*** Memory cache hit count */
97 private int hitCountRam;
98
99 /*** Auxiliary cache hit count (number of times found in ANY auxiliary) */
100 private int hitCountAux;
101
102 /*** Auxiliary hit counts broken down by auxiliary. */
103 private int[] auxHitCountByIndex;
104
105 /*** Count of misses where element was not found. */
106 private int missCountNotFound = 0;
107
108 /*** Count of misses where element was expired. */
109 private int missCountExpired = 0;
110
111 /***
112 * The cache hub can only have one memory cache. This could be made more flexible in the future,
113 * but they are tied closely together. More than one doesn't make much sense.
114 */
115 private MemoryCache memCache;
116
117 /***
118 * Constructor for the Cache object
119 * <p>
120 * @param cacheName The name of the region
121 * @param cattr The cache attribute
122 * @param attr The default element attributes
123 */
124 public CompositeCache( String cacheName, ICompositeCacheAttributes cattr, IElementAttributes attr )
125 {
126 this.cacheName = cacheName;
127 this.attr = attr;
128 this.cacheAttr = cattr;
129
130 createMemoryCache( cattr );
131
132 if ( log.isInfoEnabled() )
133 {
134 log.info( "Constructed cache with name [" + cacheName + "] and cache attributes " + cattr );
135 }
136 }
137
138 /***
139 * This sets the list of auxiliary caches for this region.
140 * <p>
141 * @param auxCaches
142 */
143 public void setAuxCaches( AuxiliaryCache[] auxCaches )
144 {
145 this.auxCaches = auxCaches;
146
147 if ( auxCaches != null )
148 {
149 this.auxHitCountByIndex = new int[auxCaches.length];
150 }
151 }
152
153 /***
154 * Standard update method.
155 * <p>
156 * @param ce
157 * @exception IOException
158 */
159 public synchronized void update( ICacheElement ce )
160 throws IOException
161 {
162 update( ce, false );
163 }
164
165 /***
166 * Standard update method.
167 * <p>
168 * @param ce
169 * @exception IOException
170 */
171 public synchronized void localUpdate( ICacheElement ce )
172 throws IOException
173 {
174 update( ce, true );
175 }
176
177 /***
178 * Put an item into the cache. If it is localOnly, then do no notify remote or lateral
179 * auxiliaries.
180 * <p>
181 * @param cacheElement the ICacheElement
182 * @param localOnly Whether the operation should be restricted to local auxiliaries.
183 * @exception IOException
184 */
185 protected synchronized void update( ICacheElement cacheElement, boolean localOnly )
186 throws IOException
187 {
188
189 updateCount++;
190
191 if ( cacheElement.getKey() instanceof String
192 && cacheElement.getKey().toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
193 {
194 throw new IllegalArgumentException( "key must not end with " + CacheConstants.NAME_COMPONENT_DELIMITER
195 + " for a put operation" );
196 }
197 else if ( cacheElement.getKey() instanceof GroupId )
198 {
199 throw new IllegalArgumentException( "key cannot be a GroupId " + " for a put operation" );
200 }
201
202 if ( log.isDebugEnabled() )
203 {
204 log.debug( "Updating memory cache" );
205 }
206
207 memCache.update( cacheElement );
208
209 updateAuxiliaries( cacheElement, localOnly );
210 }
211
212 /***
213 * This method is responsible for updating the auxiliaries if they are present. If it is local
214 * only, any lateral and remote auxiliaries will not be updated.
215 * <p>
216 * Before updating an auxiliary it checks to see if the element attributes permit the operation.
217 * <p>
218 * Disk auxiliaries are only updated if the disk cache is not merely used as a swap. If the disk
219 * cache is merely a swap, then items will only go to disk when they overflow from memory.
220 * <p>
221 * This is called by update( cacheElement, localOnly ) after it updates the memory cache.
222 * <p>
223 * This is protected to make it testable.
224 * <p>
225 * @param cacheElement
226 * @param localOnly
227 * @throws IOException
228 */
229 protected void updateAuxiliaries( ICacheElement cacheElement, boolean localOnly )
230 throws IOException
231 {
232
233
234
235
236
237
238 if ( log.isDebugEnabled() )
239 {
240 if ( auxCaches.length > 0 )
241 {
242 log.debug( "Updating auxilliary caches" );
243 }
244 else
245 {
246 log.debug( "No auxilliary cache to update" );
247 }
248 }
249
250 for ( int i = 0; i < auxCaches.length; i++ )
251 {
252 ICache aux = auxCaches[i];
253
254 if ( log.isDebugEnabled() )
255 {
256 log.debug( "Auxilliary cache type: " + aux.getCacheType() );
257 }
258
259 if ( aux == null )
260 {
261 continue;
262 }
263
264
265 if ( aux.getCacheType() == ICache.REMOTE_CACHE )
266 {
267 if ( log.isDebugEnabled() )
268 {
269 log.debug( "ce.getElementAttributes().getIsRemote() = "
270 + cacheElement.getElementAttributes().getIsRemote() );
271 }
272
273 if ( cacheElement.getElementAttributes().getIsRemote() && !localOnly )
274 {
275 try
276 {
277
278
279 aux.update( cacheElement );
280 if ( log.isDebugEnabled() )
281 {
282 log.debug( "Updated remote store for " + cacheElement.getKey() + cacheElement );
283 }
284 }
285 catch ( IOException ex )
286 {
287 log.error( "Failure in updateExclude", ex );
288 }
289 }
290
291 }
292 else if ( aux.getCacheType() == ICache.LATERAL_CACHE )
293 {
294
295
296 if ( log.isDebugEnabled() )
297 {
298 log.debug( "lateralcache in aux list: cattr " + cacheAttr.getUseLateral() );
299 }
300 if ( cacheAttr.getUseLateral() && cacheElement.getElementAttributes().getIsLateral() && !localOnly )
301 {
302
303
304
305
306
307
308 aux.update( cacheElement );
309 if ( log.isDebugEnabled() )
310 {
311 log.debug( "updated lateral cache for " + cacheElement.getKey() );
312 }
313 }
314 }
315
316 else if ( aux.getCacheType() == ICache.DISK_CACHE )
317 {
318 if ( log.isDebugEnabled() )
319 {
320 log.debug( "diskcache in aux list: cattr " + cacheAttr.getUseDisk() );
321 }
322 if ( cacheAttr.getUseDisk()
323 && ( cacheAttr.getDiskUsagePattern() == ICompositeCacheAttributes.DISK_USAGE_PATTERN_UPDATE )
324 && cacheElement.getElementAttributes().getIsSpool() )
325 {
326 aux.update( cacheElement );
327 if ( log.isDebugEnabled() )
328 {
329 log.debug( "updated disk cache for " + cacheElement.getKey() );
330 }
331 }
332 }
333 }
334 }
335
336 /***
337 * Writes the specified element to any disk auxilliaries. Might want to rename this "overflow"
338 * incase the hub wants to do something else.
339 * <p>
340 * If JCS is not configured to use the disk as a swap, that is if the the
341 * CompositeCacheAttribute diskUsagePattern is not SWAP_ONLY, then the item will not be spooled.
342 * <p>
343 * @param ce The CacheElement
344 */
345 public void spoolToDisk( ICacheElement ce )
346 {
347
348 if ( !ce.getElementAttributes().getIsSpool() )
349 {
350
351 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_NOT_ALLOWED );
352 return;
353 }
354
355 boolean diskAvailable = false;
356
357
358 for ( int i = 0; i < auxCaches.length; i++ )
359 {
360 ICache aux = auxCaches[i];
361
362 if ( aux != null && aux.getCacheType() == ICache.DISK_CACHE )
363 {
364 diskAvailable = true;
365
366 if ( cacheAttr.getDiskUsagePattern() == ICompositeCacheAttributes.DISK_USAGE_PATTERN_SWAP )
367 {
368
369 try
370 {
371 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE );
372
373 aux.update( ce );
374 }
375 catch ( IOException ex )
376 {
377
378 log.error( "Problem spooling item to disk cache.", ex );
379 throw new IllegalStateException( ex.getMessage() );
380 }
381 catch ( Exception oee )
382 {
383
384 }
385 if ( log.isDebugEnabled() )
386 {
387 log.debug( "spoolToDisk done for: " + ce.getKey() + " on disk cache[" + i + "]" );
388 }
389 }
390 else
391 {
392 if ( log.isDebugEnabled() )
393 {
394 log.debug( "DiskCache avaialbe, but JCS is not configured to use the DiskCache as a swap." );
395 }
396 }
397 }
398 }
399
400 if ( !diskAvailable )
401 {
402 try
403 {
404 handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE );
405 }
406 catch ( Exception e )
407 {
408 log.error( "Trouble handling the ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE element event", e );
409 }
410 }
411 }
412
413 /***
414 * Gets an item from the cache.
415 * <p>
416 * @param key
417 * @return
418 * @throws IOException
419 * @see org.apache.jcs.engine.behavior.ICache#get(java.io.Serializable)
420 */
421 public ICacheElement get( Serializable key )
422 {
423 return get( key, false );
424 }
425
426 /***
427 * Do not try to go remote or laterally for this get.
428 * <p>
429 * @param key
430 * @return ICacheElement
431 */
432 public ICacheElement localGet( Serializable key )
433 {
434 return get( key, true );
435 }
436
437 /***
438 * Look in memory, then disk, remote, or laterally for this item. The order is dependent on the
439 * order in the cache.ccf file.
440 * <p>
441 * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
442 * remote or lateral if such an auxiliary is configured for this region.
443 * <p>
444 * @param key
445 * @param localOnly
446 * @return ICacheElement
447 */
448 protected ICacheElement get( Serializable key, boolean localOnly )
449 {
450 ICacheElement element = null;
451
452 boolean found = false;
453
454 if ( log.isDebugEnabled() )
455 {
456 log.debug( "get: key = " + key + ", localOnly = " + localOnly );
457 }
458
459 try
460 {
461
462 element = memCache.get( key );
463
464 if ( element != null )
465 {
466
467 if ( isExpired( element ) )
468 {
469 if ( log.isDebugEnabled() )
470 {
471 log.debug( cacheName + " - Memory cache hit, but element expired" );
472 }
473
474 missCountExpired++;
475
476 remove( key );
477
478 element = null;
479 }
480 else
481 {
482 if ( log.isDebugEnabled() )
483 {
484 log.debug( cacheName + " - Memory cache hit" );
485 }
486
487
488 hitCountRam++;
489 }
490
491 found = true;
492 }
493 else
494 {
495
496
497
498 for ( int i = 0; i < auxCaches.length; i++ )
499 {
500 AuxiliaryCache aux = auxCaches[i];
501
502 if ( aux != null )
503 {
504 long cacheType = aux.getCacheType();
505
506 if ( !localOnly || cacheType == AuxiliaryCache.DISK_CACHE )
507 {
508 if ( log.isDebugEnabled() )
509 {
510 log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
511 + cacheType );
512 }
513
514 try
515 {
516 element = aux.get( key );
517 }
518 catch ( IOException ex )
519 {
520 log.error( "Error getting from aux", ex );
521 }
522 }
523
524 if ( log.isDebugEnabled() )
525 {
526 log.debug( "Got CacheElement: " + element );
527 }
528
529 if ( element != null )
530 {
531
532
533 if ( isExpired( element ) )
534 {
535 if ( log.isDebugEnabled() )
536 {
537 log.debug( cacheName + " - Aux cache[" + i + "] hit, but element expired." );
538 }
539
540 missCountExpired++;
541
542
543
544
545
546 remove( key );
547
548 element = null;
549 }
550 else
551 {
552 if ( log.isDebugEnabled() )
553 {
554 log.debug( cacheName + " - Aux cache[" + i + "] hit" );
555 }
556
557
558
559 hitCountAux++;
560 auxHitCountByIndex[i]++;
561
562
563
564
565
566 if ( memCache.getCacheAttributes().getMaxObjects() > 0 )
567 {
568 memCache.update( element );
569 }
570 else
571 {
572 if ( log.isDebugEnabled() )
573 {
574 log.debug( "Skipping memory update since no items are allowed in memory" );
575 }
576 }
577 }
578
579 found = true;
580
581 break;
582 }
583 }
584 }
585 }
586 }
587 catch ( Exception e )
588 {
589 log.error( "Problem encountered getting element.", e );
590 }
591
592 if ( !found )
593 {
594 missCountNotFound++;
595
596 if ( log.isDebugEnabled() )
597 {
598 log.debug( cacheName + " - Miss" );
599 }
600 }
601
602 return element;
603 }
604
605 /***
606 * Determine if the element has exceeded its max life.
607 * <p>
608 * @param element
609 * @return true if the element is expired, else false.
610 */
611 private boolean isExpired( ICacheElement element )
612 {
613 try
614 {
615 IElementAttributes attributes = element.getElementAttributes();
616
617 if ( !attributes.getIsEternal() )
618 {
619 long now = System.currentTimeMillis();
620
621
622
623 long maxLifeSeconds = attributes.getMaxLifeSeconds();
624 long createTime = attributes.getCreateTime();
625
626 if ( maxLifeSeconds != -1 && ( now - createTime ) > ( maxLifeSeconds * 1000 ) )
627 {
628 if ( log.isDebugEnabled() )
629 {
630 log.debug( "Exceeded maxLife: " + element.getKey() );
631 }
632
633 return true;
634 }
635 long idleTime = attributes.getIdleTime();
636 long lastAccessTime = attributes.getLastAccessTime();
637
638
639
640
641
642
643 if ( ( idleTime != -1 ) && ( now - lastAccessTime ) > ( idleTime * 1000 ) )
644 {
645 if ( log.isDebugEnabled() )
646 {
647 log.info( "Exceeded maxIdle: " + element.getKey() );
648 }
649
650 return true;
651 }
652 }
653 }
654 catch ( Exception e )
655 {
656 log.error( "Error determining expiration period, expiring", e );
657
658 return true;
659 }
660
661 return false;
662 }
663
664 /***
665 * Gets the set of keys of objects currently in the group.
666 * <p>
667 * @param group
668 * @return A Set of keys, or null.
669 */
670 public Set getGroupKeys( String group )
671 {
672 HashSet allKeys = new HashSet();
673 allKeys.addAll( memCache.getGroupKeys( group ) );
674 for ( int i = 0; i < auxCaches.length; i++ )
675 {
676 AuxiliaryCache aux = auxCaches[i];
677 if ( aux != null )
678 {
679 try
680 {
681 allKeys.addAll( aux.getGroupKeys( group ) );
682 }
683 catch ( IOException e )
684 {
685
686 }
687 }
688 }
689 return allKeys;
690 }
691
692 /***
693 * Removes an item from the cache.
694 * <p>
695 * @param key
696 * @return
697 * @throws IOException
698 * @see org.apache.jcs.engine.behavior.ICache#remove(java.io.Serializable)
699 */
700 public boolean remove( Serializable key )
701 {
702 return remove( key, false );
703 }
704
705 /***
706 * Do not propogate removeall laterally or remotely.
707 * <p>
708 * @param key
709 * @return true if the item was already in the cache.
710 */
711 public boolean localRemove( Serializable key )
712 {
713 return remove( key, true );
714 }
715
716 /***
717 * fromRemote: If a remove call was made on a cache with both, then the remote should have been
718 * called. If it wasn't then the remote is down. we'll assume it is down for all. If it did come
719 * from the remote then the cache is remotely configured and lateral removal is unncessary. If
720 * it came laterally then lateral removal is unnecessary. Does this assume that there is only
721 * one lateral and remote for the cache? Not really, the intial removal should take care of the
722 * problem if the source cache was similiarly configured. Otherwise the remote cache, if it had
723 * no laterals, would remove all the elements from remotely configured caches, but if those
724 * caches had some other wierd laterals that were not remotely configured, only laterally
725 * propagated then they would go out of synch. The same could happen for multiple remotes. If
726 * this looks necessary we will need to build in an identifier to specify the source of a
727 * removal.
728 * <p>
729 * @param key
730 * @param localOnly
731 * @return true if the item was in the cache, else false
732 */
733 protected synchronized boolean remove( Serializable key, boolean localOnly )
734 {
735
736 removeCount++;
737
738 boolean removed = false;
739
740 try
741 {
742 removed = memCache.remove( key );
743 }
744 catch ( IOException e )
745 {
746 log.error( e );
747 }
748
749
750 for ( int i = 0; i < auxCaches.length; i++ )
751 {
752 ICache aux = auxCaches[i];
753
754 if ( aux == null )
755 {
756 continue;
757 }
758
759 int cacheType = aux.getCacheType();
760
761
762
763 if ( localOnly && ( cacheType == REMOTE_CACHE || cacheType == LATERAL_CACHE ) )
764 {
765 continue;
766 }
767 try
768 {
769 if ( log.isDebugEnabled() )
770 {
771 log.debug( "Removing " + key + " from cacheType" + cacheType );
772 }
773
774 boolean b = aux.remove( key );
775
776
777 if ( !removed && cacheType != REMOTE_CACHE )
778 {
779 removed = b;
780 }
781 }
782 catch ( IOException ex )
783 {
784 log.error( "Failure removing from aux", ex );
785 }
786 }
787 return removed;
788 }
789
790 /***
791 * Clears the region. This command will be sent to all auxiliaries. Some auxiliaries, such as
792 * the JDBC disk cache, can be configured to not honor removeAll requests.
793 * <p>
794 * @see org.apache.jcs.engine.behavior.ICache#removeAll()
795 */
796 public void removeAll()
797 throws IOException
798 {
799 removeAll( false );
800 }
801
802 /***
803 * Will not pass the remove message remotely.
804 * <p>
805 * @throws IOException
806 */
807 public void localRemoveAll()
808 throws IOException
809 {
810 removeAll( true );
811 }
812
813 /***
814 * Removes all cached items.
815 * <p>
816 * @param localOnly must pass in false to get remote and lateral aux's updated. This prevents
817 * looping.
818 * @throws IOException
819 */
820 protected synchronized void removeAll( boolean localOnly )
821 throws IOException
822 {
823 try
824 {
825 memCache.removeAll();
826
827 if ( log.isDebugEnabled() )
828 {
829 log.debug( "Removed All keys from the memory cache." );
830 }
831 }
832 catch ( IOException ex )
833 {
834 log.error( "Trouble updating memory cache.", ex );
835 }
836
837
838 for ( int i = 0; i < auxCaches.length; i++ )
839 {
840 ICache aux = auxCaches[i];
841
842 int cacheType = aux.getCacheType();
843
844 if ( aux != null && ( cacheType == ICache.DISK_CACHE || !localOnly ) )
845 {
846 try
847 {
848 if ( log.isDebugEnabled() )
849 {
850 log.debug( "Removing All keys from cacheType" + cacheType );
851 }
852
853 aux.removeAll();
854 }
855 catch ( IOException ex )
856 {
857 log.error( "Failure removing all from aux", ex );
858 }
859 }
860 }
861 return;
862 }
863
864 /***
865 * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches.
866 */
867 public void dispose()
868 {
869 dispose( false );
870 }
871
872 /***
873 * Invoked only by CacheManager. This method disposes of the auxiliaries one by one. For the disk cache, the items in memory
874 * are freed, meaning that they will be sent through the overflow chanel to disk. After the
875 * auxiliaries are disposed, the memory cache is dispposed.
876 * <p>
877 * @param fromRemote
878 */
879 public synchronized void dispose( boolean fromRemote )
880 {
881 if ( log.isInfoEnabled() )
882 {
883 log.info( "In DISPOSE, [" + this.cacheName + "] fromRemote [" + fromRemote + "] \n" + this.getStats() );
884 }
885
886
887 if ( !alive )
888 {
889 return;
890 }
891 alive = false;
892
893
894
895
896 for ( int i = 0; i < auxCaches.length; i++ )
897 {
898 try
899 {
900 ICache aux = auxCaches[i];
901
902
903
904
905
906
907 if ( aux == null || aux.getStatus() != CacheConstants.STATUS_ALIVE
908 || ( fromRemote && aux.getCacheType() == REMOTE_CACHE ) )
909 {
910 if ( log.isInfoEnabled() )
911 {
912 log.info( "In DISPOSE, [" + this.cacheName + "] SKIPPING auxiliary [" + aux + "] fromRemote ["
913 + fromRemote + "]" );
914 }
915 continue;
916 }
917
918 if ( log.isInfoEnabled() )
919 {
920 log.info( "In DISPOSE, [" + this.cacheName + "] auxiliary [" + aux + "]" );
921 }
922
923
924
925
926
927
928
929
930
931 if ( aux.getCacheType() == ICacheType.DISK_CACHE )
932 {
933 int numToFree = memCache.getSize();
934 memCache.freeElements( numToFree );
935
936 if ( log.isInfoEnabled() )
937 {
938 log.info( "In DISPOSE, [" + this.cacheName + "] put " + numToFree + " into auxiliary " + aux );
939 }
940 }
941
942
943 aux.dispose();
944 }
945 catch ( IOException ex )
946 {
947 log.error( "Failure disposing of aux.", ex );
948 }
949 }
950
951 if ( log.isInfoEnabled() )
952 {
953 log.info( "In DISPOSE, [" + this.cacheName + "] disposing of memory cache." );
954 }
955 try
956 {
957 memCache.dispose();
958 }
959 catch ( IOException ex )
960 {
961 log.error( "Failure disposing of memCache", ex );
962 }
963 }
964
965 /***
966 * Calling save cause the entire contents of the memory cache to be flushed to all auxiliaries.
967 * Though this put is extremely fast, this could bog the cache and should be avoided. The
968 * dispose method should call a version of this. Good for testing.
969 */
970 public void save()
971 {
972 if ( !alive )
973 {
974 return;
975 }
976 synchronized ( this )
977 {
978 if ( !alive )
979 {
980 return;
981 }
982 alive = false;
983
984 for ( int i = 0; i < auxCaches.length; i++ )
985 {
986 try
987 {
988 ICache aux = auxCaches[i];
989
990 if ( aux.getStatus() == CacheConstants.STATUS_ALIVE )
991 {
992
993 Iterator itr = memCache.getIterator();
994
995 while ( itr.hasNext() )
996 {
997 Map.Entry entry = (Map.Entry) itr.next();
998
999 ICacheElement ce = (ICacheElement) entry.getValue();
1000
1001 aux.update( ce );
1002 }
1003 }
1004 }
1005 catch ( IOException ex )
1006 {
1007 log.error( "Failure saving aux caches.", ex );
1008 }
1009 }
1010 }
1011 if ( log.isDebugEnabled() )
1012 {
1013 log.debug( "Called save for [" + cacheName + "]" );
1014 }
1015 }
1016
1017 /***
1018 * Gets the size attribute of the Cache object. This return the number of elements, not the byte
1019 * size.
1020 * <p>
1021 * @return The size value
1022 */
1023 public int getSize()
1024 {
1025 return memCache.getSize();
1026 }
1027
1028 /***
1029 * Gets the cacheType attribute of the Cache object.
1030 * <p>
1031 * @return The cacheType value
1032 */
1033 public int getCacheType()
1034 {
1035 return CACHE_HUB;
1036 }
1037
1038 /***
1039 * Gets the status attribute of the Cache object.
1040 * <p>
1041 * @return The status value
1042 */
1043 public int getStatus()
1044 {
1045 return alive ? CacheConstants.STATUS_ALIVE : CacheConstants.STATUS_DISPOSED;
1046 }
1047
1048 /***
1049 * Gets stats for debugging.
1050 * <p>
1051 * @return String
1052 */
1053 public String getStats()
1054 {
1055 return getStatistics().toString();
1056 }
1057
1058 /***
1059 * This returns data gathered for this region and all the auxiliaries it currently uses.
1060 * <p>
1061 * @return Statistics and Info on the Region.
1062 */
1063 public ICacheStats getStatistics()
1064 {
1065 ICacheStats stats = new CacheStats();
1066 stats.setRegionName( this.getCacheName() );
1067
1068
1069 IStatElement[] elems = new StatElement[2];
1070 elems[0] = new StatElement();
1071 elems[0].setName( "HitCountRam" );
1072 elems[0].setData( "" + getHitCountRam() );
1073
1074 elems[1] = new StatElement();
1075 elems[1].setName( "HitCountAux" );
1076 elems[1].setData( "" + getHitCountAux() );
1077
1078
1079 stats.setStatElements( elems );
1080
1081
1082 int total = auxCaches.length + 1;
1083 IStats[] auxStats = new Stats[total];
1084
1085 auxStats[0] = getMemoryCache().getStatistics();
1086
1087 for ( int i = 0; i < auxCaches.length; i++ )
1088 {
1089 AuxiliaryCache aux = auxCaches[i];
1090 auxStats[i + 1] = aux.getStatistics();
1091 }
1092
1093
1094 stats.setAuxiliaryCacheStats( auxStats );
1095
1096 return stats;
1097 }
1098
1099 /***
1100 * Gets the cacheName attribute of the Cache object. This is also known as the region name.
1101 * <p>
1102 * @return The cacheName value
1103 */
1104 public String getCacheName()
1105 {
1106 return cacheName;
1107 }
1108
1109 /***
1110 * Gets the default element attribute of the Cache object This returna a copy. It does not
1111 * return a reference to the attributes.
1112 * <p>
1113 * @return The attributes value
1114 */
1115 public IElementAttributes getElementAttributes()
1116 {
1117 if ( attr != null )
1118 {
1119 return attr.copy();
1120 }
1121 return null;
1122 }
1123
1124 /***
1125 * Sets the default element attribute of the Cache object.
1126 * <p>
1127 * @param attr
1128 */
1129 public void setElementAttributes( IElementAttributes attr )
1130 {
1131 this.attr = attr;
1132 }
1133
1134 /***
1135 * Gets the ICompositeCacheAttributes attribute of the Cache object.
1136 * <p>
1137 * @return The ICompositeCacheAttributes value
1138 */
1139 public ICompositeCacheAttributes getCacheAttributes()
1140 {
1141 return this.cacheAttr;
1142 }
1143
1144 /***
1145 * Sets the ICompositeCacheAttributes attribute of the Cache object.
1146 * <p>
1147 * @param cattr The new ICompositeCacheAttributes value
1148 */
1149 public void setCacheAttributes( ICompositeCacheAttributes cattr )
1150 {
1151 this.cacheAttr = cattr;
1152
1153 this.memCache.initialize( this );
1154 }
1155
1156 /***
1157 * Gets the elementAttributes attribute of the Cache object.
1158 * <p>
1159 * @param key
1160 * @return The elementAttributes value
1161 * @exception CacheException
1162 * @exception IOException
1163 */
1164 public IElementAttributes getElementAttributes( Serializable key )
1165 throws CacheException, IOException
1166 {
1167 CacheElement ce = (CacheElement) get( key );
1168 if ( ce == null )
1169 {
1170 throw new ObjectNotFoundException( "key " + key + " is not found" );
1171 }
1172 return ce.getElementAttributes();
1173 }
1174
1175 /***
1176 * Create the MemoryCache based on the config parameters. TODO: consider making this an
1177 * auxiliary, despite its close tie to the CacheHub. TODO: might want to create a memory cache
1178 * config file separate from that of the hub -- ICompositeCacheAttributes
1179 * <p>
1180 * @param cattr
1181 */
1182 private void createMemoryCache( ICompositeCacheAttributes cattr )
1183 {
1184 if ( memCache == null )
1185 {
1186 try
1187 {
1188 Class c = Class.forName( cattr.getMemoryCacheName() );
1189 memCache = (MemoryCache) c.newInstance();
1190 memCache.initialize( this );
1191 }
1192 catch ( Exception e )
1193 {
1194 log.warn( "Failed to init mem cache, using: LRUMemoryCache", e );
1195
1196 this.memCache = new LRUMemoryCache();
1197 this.memCache.initialize( this );
1198 }
1199 }
1200 else
1201 {
1202 log.warn( "Refusing to create memory cache -- already exists." );
1203 }
1204 }
1205
1206 /***
1207 * Access to the memory cache for instrumentation.
1208 * <p>
1209 * @return the MemoryCache implementation
1210 */
1211 public MemoryCache getMemoryCache()
1212 {
1213 return memCache;
1214 }
1215
1216 /***
1217 * Number of times a requested item was found in the memory cache.
1218 * <p>
1219 * @return number of hits in memory
1220 */
1221 public int getHitCountRam()
1222 {
1223 return hitCountRam;
1224 }
1225
1226 /***
1227 * Number of times a requested item was found in and auxiliary cache.
1228 * @return number of auxiliary hits.
1229 */
1230 public int getHitCountAux()
1231 {
1232 return hitCountAux;
1233 }
1234
1235 /***
1236 * Number of times a requested element was not found.
1237 * @return number of misses.
1238 */
1239 public int getMissCountNotFound()
1240 {
1241 return missCountNotFound;
1242 }
1243
1244 /***
1245 * Number of times a requested element was found but was expired.
1246 * @return number of found but expired gets.
1247 */
1248 public int getMissCountExpired()
1249 {
1250 return missCountExpired;
1251 }
1252
1253 /***
1254 * If there are event handlers for the item, then create an event and queue it up.
1255 * <p>
1256 * This does not call handle directly; instead the handler and the event are put into a queue.
1257 * This prevents the event handling from blocking normal cache operations.
1258 * @param ce
1259 * @param eventType
1260 */
1261 private void handleElementEvent( ICacheElement ce, int eventType )
1262 {
1263
1264 ArrayList eventHandlers = ce.getElementAttributes().getElementEventHandlers();
1265 if ( eventHandlers != null )
1266 {
1267 if ( log.isDebugEnabled() )
1268 {
1269 log.debug( "Element Handlers are registered. Create event type " + eventType );
1270 }
1271 IElementEvent event = new ElementEvent( ce, eventType );
1272 Iterator hIt = eventHandlers.iterator();
1273 while ( hIt.hasNext() )
1274 {
1275 IElementEventHandler hand = (IElementEventHandler) hIt.next();
1276 try
1277 {
1278 addElementEvent( hand, event );
1279 }
1280 catch ( Exception e )
1281 {
1282 log.error( "Trouble adding element event to queue", e );
1283 }
1284 }
1285 }
1286 }
1287
1288 /***
1289 * Adds an ElementEvent to be handled to the queue.
1290 * @param hand The IElementEventHandler
1291 * @param event The IElementEventHandler IElementEvent event
1292 * @exception IOException Description of the Exception
1293 */
1294 public void addElementEvent( IElementEventHandler hand, IElementEvent event )
1295 throws IOException
1296 {
1297 if ( log.isDebugEnabled() )
1298 {
1299 log.debug( "Adding event to Element Event Queue" );
1300 }
1301 elementEventQ.addElementEvent( hand, event );
1302 }
1303
1304 /***
1305 * @param updateCount The updateCount to set.
1306 */
1307 public void setUpdateCount( int updateCount )
1308 {
1309 this.updateCount = updateCount;
1310 }
1311
1312 /***
1313 * @return Returns the updateCount.
1314 */
1315 public int getUpdateCount()
1316 {
1317 return updateCount;
1318 }
1319
1320 /***
1321 * @param removeCount The removeCount to set.
1322 */
1323 public void setRemoveCount( int removeCount )
1324 {
1325 this.removeCount = removeCount;
1326 }
1327
1328 /***
1329 * @return Returns the removeCount.
1330 */
1331 public int getRemoveCount()
1332 {
1333 return removeCount;
1334 }
1335
1336 /***
1337 * This returns the stats.
1338 * <p>
1339 * (non-Javadoc)
1340 * @see java.lang.Object#toString()
1341 */
1342 public String toString()
1343 {
1344 return getStats();
1345 }
1346 }