View Javadoc
1   package org.apache.commons.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.InputStream;
24  import java.lang.management.ManagementFactory;
25  import java.security.AccessControlException;
26  import java.util.ArrayList;
27  import java.util.Properties;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  import java.util.concurrent.Executors;
31  import java.util.concurrent.LinkedBlockingDeque;
32  import java.util.concurrent.ScheduledExecutorService;
33  import java.util.concurrent.atomic.AtomicInteger;
34  import java.util.concurrent.locks.ReentrantLock;
35  
36  import javax.management.MBeanServer;
37  import javax.management.ObjectName;
38  
39  import org.apache.commons.jcs.access.exception.CacheException;
40  import org.apache.commons.jcs.admin.JCSAdminBean;
41  import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
42  import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
43  import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
44  import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
45  import org.apache.commons.jcs.engine.CacheConstants;
46  import org.apache.commons.jcs.engine.CompositeCacheAttributes;
47  import org.apache.commons.jcs.engine.ElementAttributes;
48  import org.apache.commons.jcs.engine.behavior.ICache;
49  import org.apache.commons.jcs.engine.behavior.ICacheType.CacheType;
50  import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
51  import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
52  import org.apache.commons.jcs.engine.behavior.IElementAttributes;
53  import org.apache.commons.jcs.engine.behavior.IProvideScheduler;
54  import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
55  import org.apache.commons.jcs.engine.control.event.ElementEventQueue;
56  import org.apache.commons.jcs.engine.control.event.behavior.IElementEventQueue;
57  import org.apache.commons.jcs.engine.stats.CacheStats;
58  import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
59  import org.apache.commons.jcs.utils.config.OptionConverter;
60  import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
61  import org.apache.commons.jcs.utils.threadpool.ThreadPoolManager;
62  import org.apache.commons.logging.Log;
63  import org.apache.commons.logging.LogFactory;
64  
65  /**
66   * Manages a composite cache. This provides access to caches and is the primary way to shutdown the
67   * caching system as a whole.
68   * <p>
69   * The composite cache manager is responsible for creating / configuring cache regions. It serves as
70   * a factory for the ComositeCache class. The CompositeCache is the core of JCS, the hub for various
71   * auxiliaries.
72   */
73  public class CompositeCacheManager
74      implements IRemoteCacheConstants, ICompositeCacheManager, IProvideScheduler
75  {
76      /** The logger */
77      private static final Log log = LogFactory.getLog( CompositeCacheManager.class );
78  
79      /** JMX object name */
80      public static final String JMX_OBJECT_NAME = "org.apache.commons.jcs:type=JCSAdminBean";
81  
82      /** default region prefix */
83      private static final String DEFAULT_REGION = "jcs.default";
84  
85      /** Caches managed by this cache manager */
86      private final ConcurrentMap<String, ICache<?, ?>> caches =
87          new ConcurrentHashMap<String, ICache<?, ?>>();
88  
89      /** Lock for initialization of caches */
90      private final ReentrantLock cacheLock = new ReentrantLock();
91  
92      /** Number of clients accessing this cache manager */
93      private final AtomicInteger clients = new AtomicInteger(0);
94  
95      /** Default cache attributes for this cache manager */
96      private ICompositeCacheAttributes defaultCacheAttr = new CompositeCacheAttributes();
97  
98      /** Default element attributes for this cache manager */
99      private IElementAttributes defaultElementAttr = new ElementAttributes();
100 
101     /** Used to keep track of configured auxiliaries */
102     private final ConcurrentMap<String, AuxiliaryCacheFactory> auxiliaryFactoryRegistry =
103         new ConcurrentHashMap<String, AuxiliaryCacheFactory>( );
104 
105     /** Used to keep track of attributes for auxiliaries. */
106     private final ConcurrentMap<String, AuxiliaryCacheAttributes> auxiliaryAttributeRegistry =
107         new ConcurrentHashMap<String, AuxiliaryCacheAttributes>( );
108 
109     /** Used to keep track of configured auxiliaries */
110     private final ConcurrentMap<String, AuxiliaryCache<?, ?>> auxiliaryCaches =
111         new ConcurrentHashMap<String, AuxiliaryCache<?, ?>>( );
112 
113     /** Properties with which this manager was configured. This is exposed for other managers. */
114     private Properties configurationProperties;
115 
116     /** The default auxiliary caches to be used if not preconfigured */
117     private String defaultAuxValues;
118 
119     /** The Singleton Instance */
120     private static CompositeCacheManager instance;
121 
122     /** Should we use system property substitutions. */
123     private static final boolean DEFAULT_USE_SYSTEM_PROPERTIES = true;
124 
125     /** Once configured, you can force a reconfiguration of sorts. */
126     private static final boolean DEFAULT_FORCE_RECONFIGURATION = false;
127 
128     /** Stack for those waiting for notification of a shutdown. */
129     private final LinkedBlockingDeque<IShutdownObserver> shutdownObservers = new LinkedBlockingDeque<IShutdownObserver>();
130 
131     /** The central background scheduler. */
132     private ScheduledExecutorService scheduledExecutor;
133 
134     /** The central event queue. */
135     private IElementEventQueue elementEventQueue;
136 
137     /** Shutdown hook thread instance */
138     private ShutdownHook shutdownHook;
139 
140     /** Indicates whether the instance has been initialized. */
141     private boolean isInitialized = false;
142 
143     /** Indicates whether configure has been called. */
144     private boolean isConfigured = false;
145 
146     /** Indicates whether JMX bean has been registered. */
147     private boolean isJMXRegistered = false;
148 
149     private String jmxName = JMX_OBJECT_NAME;
150 
151     /**
152      * Gets the CacheHub instance. For backward compatibility, if this creates the instance it will
153      * attempt to configure it with the default configuration. If you want to configure from your
154      * own source, use {@link #getUnconfiguredInstance}and then call {@link #configure}
155      * <p>
156      * @return CompositeCacheManager
157      * @throws CacheException if the configuration cannot be loaded
158      */
159     public static synchronized CompositeCacheManager getInstance() throws CacheException
160     {
161         return getInstance( CacheConstants.DEFAULT_CONFIG );
162     }
163 
164     /**
165      * Initializes the cache manager using the props file for the given name.
166      * <p>
167      * @param propsFilename
168      * @return CompositeCacheManager configured from the give propsFileName
169      * @throws CacheException if the configuration cannot be loaded
170      */
171     public static synchronized CompositeCacheManager getInstance( String propsFilename ) throws CacheException
172     {
173         if ( instance == null )
174         {
175             if ( log.isInfoEnabled() )
176             {
177                 log.info( "Instance is null, creating with config [" + propsFilename + "]" );
178             }
179 
180             instance = createInstance();
181         }
182 
183         if (!instance.isInitialized())
184         {
185             instance.initialize();
186         }
187 
188         if (!instance.isConfigured())
189         {
190             instance.configure( propsFilename );
191         }
192 
193         instance.clients.incrementAndGet();
194 
195         return instance;
196     }
197 
198     /**
199      * Get a CacheHub instance which is not configured. If an instance already exists, it will be
200      * returned.
201      *<p>
202      * @return CompositeCacheManager
203      */
204     public static synchronized CompositeCacheManager getUnconfiguredInstance()
205     {
206         if ( instance == null )
207         {
208             if ( log.isInfoEnabled() )
209             {
210                 log.info( "Instance is null, returning unconfigured instance" );
211             }
212 
213             instance = createInstance();
214         }
215 
216         if (!instance.isInitialized())
217         {
218             instance.initialize();
219         }
220 
221         instance.clients.incrementAndGet();
222 
223         return instance;
224     }
225 
226     /**
227      * Simple factory method, must override in subclasses so getInstance creates / returns the
228      * correct object.
229      * <p>
230      * @return CompositeCacheManager
231      */
232     protected static CompositeCacheManager createInstance()
233     {
234         return new CompositeCacheManager();
235     }
236 
237     /**
238      * Default constructor
239      */
240     protected CompositeCacheManager()
241     {
242         // empty
243     }
244 
245     /** Creates a shutdown hook and starts the scheduler service */
246     protected void initialize()
247     {
248         if (!isInitialized)
249         {
250             this.shutdownHook = new ShutdownHook();
251             try
252             {
253                 Runtime.getRuntime().addShutdownHook( shutdownHook );
254             }
255             catch ( AccessControlException e )
256             {
257                 log.error( "Could not register shutdown hook.", e );
258             }
259 
260             this.scheduledExecutor = Executors.newScheduledThreadPool(4,
261                     new DaemonThreadFactory("JCS-Scheduler-", Thread.MIN_PRIORITY));
262 
263             // Register JMX bean
264             if (!isJMXRegistered && jmxName != null)
265             {
266                 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
267                 JCSAdminBean adminBean = new JCSAdminBean(this);
268                 try
269                 {
270                     ObjectName jmxObjectName = new ObjectName(jmxName);
271                     mbs.registerMBean(adminBean, jmxObjectName);
272                     isJMXRegistered = true;
273                 }
274                 catch (Exception e)
275                 {
276                     log.warn( "Could not register JMX bean.", e );
277                 }
278             }
279 
280             this.elementEventQueue = new ElementEventQueue();
281 
282             isInitialized = true;
283         }
284     }
285 
286     /**
287      * Get the element event queue
288      *
289      * @return the elementEventQueue
290      */
291     public IElementEventQueue getElementEventQueue()
292     {
293         return elementEventQueue;
294     }
295 
296     /**
297      * Get the scheduler service
298      *
299      * @return the scheduledExecutor
300      */
301     @Override
302     public ScheduledExecutorService getScheduledExecutorService()
303     {
304         return scheduledExecutor;
305     }
306 
307     /**
308      * Configure with default properties file
309      * @throws CacheException if the configuration cannot be loaded
310      */
311     public void configure() throws CacheException
312     {
313         configure( CacheConstants.DEFAULT_CONFIG );
314     }
315 
316     /**
317      * Configure from specific properties file.
318      * <p>
319      * @param propFile Path <u>within classpath </u> to load configuration from
320      * @throws CacheException if the configuration cannot be loaded
321      */
322     public void configure( String propFile ) throws CacheException
323     {
324         log.info( "Creating cache manager from config file: " + propFile );
325 
326         Properties props = new Properties();
327 
328         InputStream is = getClass().getResourceAsStream( propFile );
329 
330         if ( is != null )
331         {
332             try
333             {
334                 props.load( is );
335 
336                 if ( log.isDebugEnabled() )
337                 {
338                     log.debug( "File [" + propFile + "] contained " + props.size() + " properties" );
339                 }
340             }
341             catch ( IOException ex )
342             {
343                 throw new CacheException("Failed to load properties for name [" + propFile + "]", ex);
344             }
345             finally
346             {
347                 try
348                 {
349                     is.close();
350                 }
351                 catch ( IOException ignore )
352                 {
353                     // Ignored
354                 }
355             }
356         }
357         else
358         {
359             throw new CacheException( "Failed to read configuration file [" + propFile + "]" );
360         }
361 
362         configure( props );
363     }
364 
365     /**
366      * Configure from properties object.
367      * <p>
368      * This method will call configure, instructing it to use system properties as a default.
369      * @param props
370      */
371     public void configure( Properties props )
372     {
373         configure( props, DEFAULT_USE_SYSTEM_PROPERTIES );
374     }
375 
376     /**
377      * Configure from properties object, overriding with values from the system properties if
378      * instructed.
379      * <p>
380      * You can override a specific value by passing in a system property:
381      * <p>
382      * For example, you could override this value in the cache.ccf file by starting up your program
383      * with the argument: -Djcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
384      * <p>
385      * @param props
386      * @param useSystemProperties -- if true, values starting with jcs will be put into the props
387      *            file prior to configuring the cache.
388      */
389     public void configure( Properties props, boolean useSystemProperties )
390     {
391         configure( props, useSystemProperties, DEFAULT_FORCE_RECONFIGURATION );
392     }
393 
394     /**
395      * Configure from properties object, overriding with values from the system properties if
396      * instructed.
397      * <p>
398      * You can override a specific value by passing in a system property:
399      * <p>
400      * For example, you could override this value in the cache.ccf file by starting up your program
401      * with the argument: -Djcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
402      * <p>
403      * @param props
404      * @param useSystemProperties -- if true, values starting with jcs will be put into the props
405      *            file prior to configuring the cache.
406      * @param forceReconfiguration - if the manager is already configured, we will try again. This
407      *            may not work properly.
408      */
409     public synchronized void configure( Properties props, boolean useSystemProperties, boolean forceReconfiguration )
410     {
411         if ( props == null )
412         {
413             log.error( "No properties found.  Please configure the cache correctly." );
414             return;
415         }
416 
417         if ( isConfigured )
418         {
419             if ( !forceReconfiguration )
420             {
421                 if ( log.isDebugEnabled() )
422                 {
423                     log.debug( "Configure called after the manager has been configured.  "
424                         + "Force reconfiguration is false.  Doing nothing" );
425                 }
426                 return;
427             }
428             else
429             {
430                 if ( log.isInfoEnabled() )
431                 {
432                     log.info( "Configure called after the manager has been configured.  "
433                         + "Force reconfiguration is true.  Reconfiguring as best we can." );
434                 }
435             }
436         }
437         if ( useSystemProperties )
438         {
439             CompositeCacheConfigurator.overrideWithSystemProperties( props );
440         }
441         doConfigure( props );
442     }
443 
444     /**
445      * Configure the cache using the supplied properties.
446      * <p>
447      * @param properties assumed not null
448      */
449     private void doConfigure( Properties properties )
450     {
451         // We will expose this for managers that need raw properties.
452         this.configurationProperties = properties;
453 
454         // set the props value and then configure the ThreadPoolManager
455         ThreadPoolManager.setProps( properties );
456         ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
457         if ( log.isDebugEnabled() )
458         {
459             log.debug( "ThreadPoolManager = " + poolMgr );
460         }
461 
462         // configure the cache
463         CompositeCacheConfigurator configurator = newConfigurator();
464 
465         long start = System.currentTimeMillis();
466 
467         // set default value list
468         this.defaultAuxValues = OptionConverter.findAndSubst( CompositeCacheManager.DEFAULT_REGION,
469                 properties );
470 
471         log.info( "Setting default auxiliaries to " + this.defaultAuxValues );
472 
473         // set default cache attr
474         this.defaultCacheAttr = configurator.parseCompositeCacheAttributes( properties, "",
475                 new CompositeCacheAttributes(), DEFAULT_REGION );
476 
477         log.info( "setting defaultCompositeCacheAttributes to " + this.defaultCacheAttr );
478 
479         // set default element attr
480         this.defaultElementAttr = configurator.parseElementAttributes( properties, "",
481                 new ElementAttributes(), DEFAULT_REGION );
482 
483         log.info( "setting defaultElementAttributes to " + this.defaultElementAttr );
484 
485         // set up system caches to be used by non system caches
486         // need to make sure there is no circularity of reference
487         configurator.parseSystemRegions( properties, this );
488 
489         // setup preconfigured caches
490         configurator.parseRegions( properties, this );
491 
492         long end = System.currentTimeMillis();
493         if ( log.isInfoEnabled() )
494         {
495             log.info( "Finished configuration in " + ( end - start ) + " ms." );
496         }
497 
498         isConfigured = true;
499     }
500 
501     /**
502      * Gets the defaultCacheAttributes attribute of the CacheHub object
503      * <p>
504      * @return The defaultCacheAttributes value
505      */
506     public ICompositeCacheAttributes getDefaultCacheAttributes()
507     {
508         return this.defaultCacheAttr.clone();
509     }
510 
511     /**
512      * Gets the defaultElementAttributes attribute of the CacheHub object
513      * <p>
514      * @return The defaultElementAttributes value
515      */
516     public IElementAttributes getDefaultElementAttributes()
517     {
518         return this.defaultElementAttr.clone();
519     }
520 
521     /**
522      * Gets the cache attribute of the CacheHub object
523      * <p>
524      * @param cacheName
525      * @return CompositeCache -- the cache region controller
526      */
527     @Override
528     public <K, V> CompositeCache<K, V> getCache( String cacheName )
529     {
530         return getCache( cacheName, getDefaultCacheAttributes() );
531     }
532 
533     /**
534      * Gets the cache attribute of the CacheHub object
535      * <p>
536      * @param cacheName
537      * @param cattr
538      * @return CompositeCache
539      */
540     public <K, V> CompositeCache<K, V> getCache( String cacheName, ICompositeCacheAttributes cattr )
541     {
542         cattr.setCacheName( cacheName );
543         return getCache( cattr, getDefaultElementAttributes() );
544     }
545 
546     /**
547      * Gets the cache attribute of the CacheHub object
548      * <p>
549      * @param cacheName
550      * @param cattr
551      * @param attr
552      * @return CompositeCache
553      */
554     public <K, V> CompositeCache<K, V>  getCache( String cacheName, ICompositeCacheAttributes cattr, IElementAttributes attr )
555     {
556         cattr.setCacheName( cacheName );
557         return getCache( cattr, attr );
558     }
559 
560     /**
561      * Gets the cache attribute of the CacheHub object
562      * <p>
563      * @param cattr
564      * @return CompositeCache
565      */
566     public <K, V> CompositeCache<K, V>  getCache( ICompositeCacheAttributes cattr )
567     {
568         return getCache( cattr, getDefaultElementAttributes() );
569     }
570 
571     /**
572      * If the cache has already been created, then the CacheAttributes and the element Attributes
573      * will be ignored. Currently there is no overriding the CacheAttributes once it is set up. You
574      * can change the default ElementAttributes for a region later.
575      * <p>
576      * Overriding the default elemental attributes will require changing the way the attributes are
577      * assigned to elements. Get cache creates a cache with defaults if none are specified. We might
578      * want to create separate method for creating/getting. . .
579      * <p>
580      * @param cattr
581      * @param attr
582      * @return CompositeCache
583      */
584     @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
585     public <K, V> CompositeCache<K, V>  getCache( ICompositeCacheAttributes cattr, IElementAttributes attr )
586     {
587         CompositeCache<K, V> cache;
588 
589         if ( log.isDebugEnabled() )
590         {
591             log.debug( "attr = " + attr );
592         }
593 
594         cache = (CompositeCache<K, V>) caches.get( cattr.getCacheName() );
595 
596         if (cache == null)
597         {
598             cacheLock.lock();
599 
600             try
601             {
602                 // double check
603                 cache = (CompositeCache<K, V>) caches.get( cattr.getCacheName() );
604 
605                 if ( cache == null )
606                 {
607                     CompositeCacheConfigurator configurator = newConfigurator();
608 
609                     cache = configurator.parseRegion( this.getConfigurationProperties(), this, cattr.getCacheName(),
610                                                       this.defaultAuxValues, cattr );
611 
612                     caches.put( cattr.getCacheName(), cache );
613                 }
614             }
615             finally
616             {
617                 cacheLock.unlock();
618             }
619         }
620 
621         return cache;
622     }
623 
624     protected CompositeCacheConfigurator newConfigurator() {
625         return new CompositeCacheConfigurator();
626     }
627 
628     /**
629      * @param name
630      */
631     public void freeCache( String name )
632     {
633         freeCache( name, false );
634     }
635 
636     /**
637      * @param name
638      * @param fromRemote
639      */
640     public void freeCache( String name, boolean fromRemote )
641     {
642         CompositeCache<?, ?> cache = (CompositeCache<?, ?>) caches.remove( name );
643 
644         if ( cache != null )
645         {
646             cache.dispose( fromRemote );
647         }
648     }
649 
650     /**
651      * Calls freeCache on all regions
652      */
653     public void shutDown()
654     {
655         synchronized (CompositeCacheManager.class)
656         {
657             // shutdown element event queue
658             this.elementEventQueue.dispose();
659 
660             // shutdown all scheduled jobs
661             this.scheduledExecutor.shutdownNow();
662 
663             // shutdown all thread pools
664             ThreadPoolManager.dispose();
665 
666             // notify any observers
667             IShutdownObserver observer = null;
668             while ((observer = shutdownObservers.poll()) != null)
669             {
670                 observer.shutdown();
671             }
672 
673             // Unregister JMX bean
674             if (isJMXRegistered)
675             {
676                 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
677                 try
678                 {
679                     ObjectName jmxObjectName = new ObjectName(jmxName);
680                     mbs.unregisterMBean(jmxObjectName);
681                 }
682                 catch (Exception e)
683                 {
684                     log.warn( "Could not unregister JMX bean.", e );
685                 }
686 
687                 isJMXRegistered = false;
688             }
689 
690             // do the traditional shutdown of the regions.
691             for (String name : getCacheNames())
692             {
693                 freeCache( name );
694             }
695 
696             // shut down factories
697             for (AuxiliaryCacheFactory factory : auxiliaryFactoryRegistry.values())
698             {
699                 factory.dispose();
700             }
701 
702             auxiliaryAttributeRegistry.clear();
703             auxiliaryFactoryRegistry.clear();
704 
705             if (shutdownHook != null)
706             {
707                 try
708                 {
709                     Runtime.getRuntime().removeShutdownHook(shutdownHook);
710                 }
711                 catch (IllegalStateException e)
712                 {
713                     // May fail if the JVM is already shutting down
714                 }
715 
716                 this.shutdownHook = null;
717             }
718 
719             isConfigured = false;
720             isInitialized = false;
721         }
722     }
723 
724     /** */
725     public void release()
726     {
727         release( false );
728     }
729 
730     /**
731      * @param fromRemote
732      */
733     private void release( boolean fromRemote )
734     {
735         synchronized ( CompositeCacheManager.class )
736         {
737             // Wait until called by the last client
738             if ( clients.decrementAndGet() > 0 )
739             {
740                 if ( log.isDebugEnabled() )
741                 {
742                     log.debug( "Release called, but " + clients + " remain" );
743                 }
744                 return;
745             }
746 
747             if ( log.isDebugEnabled() )
748             {
749                 log.debug( "Last client called release. There are " + caches.size() + " caches which will be disposed" );
750             }
751 
752             for (ICache<?, ?> c : caches.values() )
753             {
754                 CompositeCache<?, ?> cache = (CompositeCache<?, ?>) c;
755 
756                 if ( cache != null )
757                 {
758                     cache.dispose( fromRemote );
759                 }
760             }
761         }
762     }
763 
764     /**
765      * Returns a list of the current cache names.
766      * @return String[]
767      */
768     public String[] getCacheNames()
769     {
770         return caches.keySet().toArray(new String[caches.size()]);
771     }
772 
773     /**
774      * @return ICacheType.CACHE_HUB
775      */
776     public CacheType getCacheType()
777     {
778         return CacheType.CACHE_HUB;
779     }
780 
781     /**
782      * @param auxFac
783      */
784     public void registryFacPut( AuxiliaryCacheFactory auxFac )
785     {
786         auxiliaryFactoryRegistry.put( auxFac.getName(), auxFac );
787     }
788 
789     /**
790      * @param name
791      * @return AuxiliaryCacheFactory
792      */
793     public AuxiliaryCacheFactory registryFacGet( String name )
794     {
795         return auxiliaryFactoryRegistry.get( name );
796     }
797 
798     /**
799      * @param auxAttr
800      */
801     public void registryAttrPut( AuxiliaryCacheAttributes auxAttr )
802     {
803         auxiliaryAttributeRegistry.put( auxAttr.getName(), auxAttr );
804     }
805 
806     /**
807      * @param name
808      * @return AuxiliaryCacheAttributes
809      */
810     public AuxiliaryCacheAttributes registryAttrGet( String name )
811     {
812         return auxiliaryAttributeRegistry.get( name );
813     }
814 
815     /**
816      * Add a cache to the map of registered caches
817      *
818      * @param cacheName the region name
819      * @param cache the cache instance
820      */
821     public void addCache(String cacheName, ICache<?, ?> cache)
822     {
823         caches.put(cacheName, cache);
824     }
825 
826     /**
827      * Add a cache to the map of registered auxiliary caches
828      *
829      * @param auxName the auxiliary name
830      * @param cacheName the region name
831      * @param cache the cache instance
832      */
833     public void addAuxiliaryCache(String auxName, String cacheName, AuxiliaryCache<?, ?> cache)
834     {
835         String key = String.format("aux.%s.region.%s", auxName, cacheName);
836         auxiliaryCaches.put(key, cache);
837     }
838 
839     /**
840      * Get a cache from the map of registered auxiliary caches
841      *
842      * @param auxName the auxiliary name
843      * @param cacheName the region name
844      *
845      * @return the cache instance
846      */
847     @Override
848     @SuppressWarnings("unchecked") // because of common map for all auxiliary caches
849     public <K, V> AuxiliaryCache<K, V> getAuxiliaryCache(String auxName, String cacheName)
850     {
851         String key = String.format("aux.%s.region.%s", auxName, cacheName);
852         return (AuxiliaryCache<K, V>) auxiliaryCaches.get(key);
853     }
854 
855     /**
856      * Gets stats for debugging. This calls gets statistics and then puts all the results in a
857      * string. This returns data for all regions.
858      * <p>
859      * @return String
860      */
861     @Override
862     public String getStats()
863     {
864         ICacheStats[] stats = getStatistics();
865         if ( stats == null )
866         {
867             return "NONE";
868         }
869 
870         // force the array elements into a string.
871         StringBuilder buf = new StringBuilder();
872         int statsLen = stats.length;
873         for ( int i = 0; i < statsLen; i++ )
874         {
875             buf.append( "\n---------------------------\n" );
876             buf.append( stats[i] );
877         }
878         return buf.toString();
879     }
880 
881     /**
882      * This returns data gathered for all regions and all the auxiliaries they currently uses.
883      * <p>
884      * @return ICacheStats[]
885      */
886     public ICacheStats[] getStatistics()
887     {
888         ArrayList<ICacheStats> cacheStats = new ArrayList<ICacheStats>();
889         for (ICache<?, ?> c :  caches.values())
890         {
891             CompositeCache<?, ?> cache = (CompositeCache<?, ?>) c;
892             if ( cache != null )
893             {
894                 cacheStats.add( cache.getStatistics() );
895             }
896         }
897         ICacheStats[] stats = cacheStats.toArray( new CacheStats[0] );
898         return stats;
899     }
900 
901     /**
902      * Perhaps the composite cache itself should be the observable object. It doesn't make much of a
903      * difference. There are some problems with region by region shutdown. Some auxiliaries are
904      * global. They will need to track when every region has shutdown before doing things like
905      * closing the socket with a lateral.
906      * <p>
907      * @param observer
908      */
909     @Override
910     public void registerShutdownObserver( IShutdownObserver observer )
911     {
912     	if (!shutdownObservers.contains(observer))
913     	{
914     		shutdownObservers.push( observer );
915     	}
916     	else
917     	{
918     		log.warn("Shutdown observer added twice " + observer);
919     	}
920     }
921 
922     /**
923      * @param observer
924      */
925     @Override
926     public void deregisterShutdownObserver( IShutdownObserver observer )
927     {
928         shutdownObservers.remove( observer );
929     }
930 
931     /**
932      * This is exposed so other manager can get access to the props.
933      * <p>
934      * @return the configurationProperties
935      */
936     @Override
937     public Properties getConfigurationProperties()
938     {
939         return configurationProperties;
940     }
941 
942     /**
943      * @return the isInitialized
944      */
945     public boolean isInitialized()
946     {
947         return isInitialized;
948     }
949 
950     /**
951      * @return the isConfigured
952      */
953     public boolean isConfigured()
954     {
955         return isConfigured;
956     }
957 
958     public void setJmxName(final String name)
959     {
960         if (isJMXRegistered)
961         {
962             throw new IllegalStateException("Too late, MBean registration is done");
963         }
964         jmxName = name;
965     }
966 
967     /**
968      * Called on shutdown. This gives use a chance to store the keys and to optimize even if the
969      * cache manager's shutdown method was not called manually.
970      */
971     class ShutdownHook
972         extends Thread
973     {
974         /**
975          * This will persist the keys on shutdown.
976          * <p>
977          * @see java.lang.Thread#run()
978          */
979         @SuppressWarnings("synthetic-access")
980         @Override
981         public void run()
982         {
983             if ( isInitialized() )
984             {
985                 log.info( "Shutdown hook activated.  Shutdown was not called.  Shutting down JCS." );
986                 shutDown();
987             }
988         }
989     }
990 }