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