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