View Javadoc

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