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