1 package org.apache.jcs.engine.control;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
303 ThreadPoolManager.setProps( props );
304 ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
305
306 if ( log.isDebugEnabled() )
307 {
308 log.debug( "ThreadPoolManager = " + poolMgr );
309 }
310
311
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
485 synchronized ( shutdownObservers )
486 {
487
488
489
490 Iterator it = shutdownObservers.iterator();
491 while ( it.hasNext() )
492 {
493 IShutdownObserver observer = (IShutdownObserver) it.next();
494 observer.shutdown();
495 }
496 }
497
498
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
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
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
681
682 synchronized ( shutdownObservers )
683 {
684
685 shutdownObservers.add( observer );
686 }
687 }
688
689
690
691
692
693
694 public void deregisterShutdownObserver( IShutdownObserver observer )
695 {
696 synchronized ( shutdownObservers )
697 {
698 shutdownObservers.remove( observer );
699 }
700 }
701 }