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