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