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