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