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.util.ArrayList; 023import java.util.List; 024import java.util.Properties; 025 026import org.apache.commons.jcs3.auxiliary.AuxiliaryCache; 027import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes; 028import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheConfigurator; 029import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheFactory; 030import org.apache.commons.jcs3.engine.behavior.ICache; 031import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes; 032import org.apache.commons.jcs3.engine.behavior.IElementAttributes; 033import org.apache.commons.jcs3.engine.behavior.IElementSerializer; 034import org.apache.commons.jcs3.engine.behavior.IRequireScheduler; 035import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger; 036import org.apache.commons.jcs3.engine.match.KeyMatcherPatternImpl; 037import org.apache.commons.jcs3.engine.match.behavior.IKeyMatcher; 038import org.apache.commons.jcs3.log.Log; 039import org.apache.commons.jcs3.log.LogManager; 040import org.apache.commons.jcs3.utils.config.OptionConverter; 041import org.apache.commons.jcs3.utils.config.PropertySetter; 042 043/** 044 * This class configures JCS based on a properties object. 045 * <p> 046 * This class is based on the log4j class org.apache.log4j.PropertyConfigurator which was made by: 047 * "Luke Blanshard" <Luke@quiq.com>"Mark DONSZELMANN" <Mark.Donszelmann@cern.ch>"Anders Kristensen" 048 * <akristensen@dynamicsoft.com> 049 */ 050public class CompositeCacheConfigurator 051{ 052 /** The logger */ 053 private static final Log log = LogManager.getLog( CompositeCacheConfigurator.class ); 054 055 /** The prefix of relevant system properties */ 056 protected static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs"; 057 058 /** normal region prefix */ 059 protected static final String REGION_PREFIX = "jcs.region."; 060 061 /** system region prefix. might not be used */ 062 protected static final String SYSTEM_REGION_PREFIX = "jcs.system."; 063 064 /** auxiliary prefix */ 065 protected static final String AUXILIARY_PREFIX = "jcs.auxiliary."; 066 067 /** .attributes */ 068 protected static final String ATTRIBUTE_PREFIX = ".attributes"; 069 070 /** .cacheattributes */ 071 protected static final String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes"; 072 073 /** .elementattributes */ 074 protected static final String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes"; 075 076 /** 077 * jcs.auxiliary.NAME.keymatcher=CLASSNAME 078 * <p> 079 * jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE 080 */ 081 public static final String KEY_MATCHER_PREFIX = ".keymatcher"; 082 083 /** 084 * Constructor for the CompositeCacheConfigurator object 085 */ 086 public CompositeCacheConfigurator() 087 { 088 // empty 089 } 090 091 /** 092 * Create caches used internally. System status gives them creation priority. 093 *<p> 094 * @param props Configuration properties 095 * @param ccm Cache hub 096 */ 097 protected void parseSystemRegions( final Properties props, final CompositeCacheManager ccm ) 098 { 099 for (final String key : props.stringPropertyNames() ) 100 { 101 if ( key.startsWith( SYSTEM_REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 ) 102 { 103 final String regionName = key.substring( SYSTEM_REGION_PREFIX.length() ); 104 final String auxiliaries = OptionConverter.findAndSubst( key, props ); 105 final ICache<?, ?> cache; 106 synchronized ( regionName ) 107 { 108 cache = parseRegion( props, ccm, regionName, auxiliaries, null, SYSTEM_REGION_PREFIX ); 109 } 110 ccm.addCache( regionName, cache ); 111 } 112 } 113 } 114 115 /** 116 * Parse region elements. 117 *<p> 118 * @param props Configuration properties 119 * @param ccm Cache hub 120 */ 121 protected void parseRegions( final Properties props, final CompositeCacheManager ccm ) 122 { 123 final List<String> regionNames = new ArrayList<>(); 124 125 for (final String key : props.stringPropertyNames() ) 126 { 127 if ( key.startsWith( REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 ) 128 { 129 final String regionName = key.substring( REGION_PREFIX.length() ); 130 regionNames.add( regionName ); 131 final String auxiliaries = OptionConverter.findAndSubst( key, props ); 132 final ICache<?, ?> cache; 133 synchronized ( regionName ) 134 { 135 cache = parseRegion( props, ccm, regionName, auxiliaries ); 136 } 137 ccm.addCache( regionName, cache ); 138 } 139 } 140 141 log.info( "Parsed regions {0}", regionNames ); 142 } 143 144 /** 145 * Create cache region. 146 *<p> 147 * @param props Configuration properties 148 * @param ccm Cache hub 149 * @param regName Name of the cache region 150 * @param auxiliaries Comma separated list of auxiliaries 151 * 152 * @return CompositeCache 153 */ 154 protected <K, V> CompositeCache<K, V> parseRegion( 155 final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries ) 156 { 157 return parseRegion( props, ccm, regName, auxiliaries, null, REGION_PREFIX ); 158 } 159 160 /** 161 * Get all the properties for a region and configure its cache. 162 * <p> 163 * This method tells the other parse method the name of the region prefix. 164 *<p> 165 * @param props Configuration properties 166 * @param ccm Cache hub 167 * @param regName Name of the cache region 168 * @param auxiliaries Comma separated list of auxiliaries 169 * @param cca Cache configuration 170 * 171 * @return CompositeCache 172 */ 173 protected <K, V> CompositeCache<K, V> parseRegion( 174 final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries, 175 final ICompositeCacheAttributes cca ) 176 { 177 return parseRegion( props, ccm, regName, auxiliaries, cca, REGION_PREFIX ); 178 } 179 180 /** 181 * Get all the properties for a region and configure its cache. 182 *<p> 183 * @param props Configuration properties 184 * @param ccm Cache hub 185 * @param regName Name of the cache region 186 * @param auxiliaries Comma separated list of auxiliaries 187 * @param cca Cache configuration 188 * @param regionPrefix Prefix for the region 189 * 190 * @return CompositeCache 191 */ 192 protected <K, V> CompositeCache<K, V> parseRegion( 193 final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries, 194 final ICompositeCacheAttributes cca, final String regionPrefix ) 195 { 196 // First, create or get the cache and element attributes, and create 197 // the cache. 198 final IElementAttributes ea = parseElementAttributes( props, regName, 199 ccm.getDefaultElementAttributes(), regionPrefix ); 200 201 final ICompositeCacheAttributes instantiationCca = cca == null 202 ? parseCompositeCacheAttributes(props, regName, ccm.getDefaultCacheAttributes(), regionPrefix) 203 : cca; 204 final CompositeCache<K, V> cache = newCache(instantiationCca, ea); 205 206 // Inject cache manager 207 cache.setCompositeCacheManager(ccm); 208 209 // Inject scheduler service 210 cache.setScheduledExecutorService(ccm.getScheduledExecutorService()); 211 212 // Inject element event queue 213 cache.setElementEventQueue(ccm.getElementEventQueue()); 214 215 if (auxiliaries != null) 216 { 217 // Next, create the auxiliaries for the new cache 218 final List<AuxiliaryCache<K, V>> auxList = new ArrayList<>(); 219 220 log.debug( "Parsing region name \"{0}\", value \"{1}\"", regName, auxiliaries ); 221 222 String auxNames[] = auxiliaries.split("\\s*,\\s*"); 223 224 // just to be on the safe side... 225 if (auxNames.length == 0) 226 { 227 return null; 228 } 229 230 for (String auxName : auxNames) 231 { 232 if (auxName.isEmpty()) 233 { 234 log.warn( "Skipping empty auxiliary name" ); 235 continue; 236 } 237 238 log.debug( "Parsing auxiliary named \"{0}\".", auxName ); 239 240 AuxiliaryCache<K, V> auxCache = parseAuxiliary( props, ccm, auxName, regName ); 241 242 if ( auxCache != null ) 243 { 244 if (auxCache instanceof IRequireScheduler) 245 { 246 ((IRequireScheduler) auxCache).setScheduledExecutorService( 247 ccm.getScheduledExecutorService()); 248 } 249 250 auxList.add( auxCache ); 251 } 252 } 253 254 // Associate the auxiliaries with the cache 255 cache.setAuxCaches(auxList); 256 } 257 258 // Return the new cache 259 return cache; 260 } 261 262 protected <K, V> CompositeCache<K, V> newCache( 263 final ICompositeCacheAttributes cca, final IElementAttributes ea) 264 { 265 return new CompositeCache<>( cca, ea ); 266 } 267 268 /** 269 * Get an ICompositeCacheAttributes for the listed region. 270 *<p> 271 * @param props Configuration properties 272 * @param regName the region name 273 * @param defaultCCAttr the default cache attributes 274 * 275 * @return ICompositeCacheAttributes 276 */ 277 protected ICompositeCacheAttributes parseCompositeCacheAttributes( final Properties props, 278 final String regName, final ICompositeCacheAttributes defaultCCAttr ) 279 { 280 return parseCompositeCacheAttributes( props, regName, defaultCCAttr, REGION_PREFIX ); 281 } 282 283 /** 284 * Get the main attributes for a region. 285 *<p> 286 * @param props Configuration properties 287 * @param regName the region name 288 * @param defaultCCAttr the default cache attributes 289 * @param regionPrefix the region prefix 290 * 291 * @return ICompositeCacheAttributes 292 */ 293 protected ICompositeCacheAttributes parseCompositeCacheAttributes( final Properties props, 294 final String regName, final ICompositeCacheAttributes defaultCCAttr, final String regionPrefix ) 295 { 296 ICompositeCacheAttributes ccAttr; 297 298 final String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX; 299 300 // auxFactory was not previously initialized. 301 // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX; 302 ccAttr = OptionConverter.instantiateByKey( props, attrName, null ); 303 304 if ( ccAttr == null ) 305 { 306 log.info( "No special CompositeCacheAttributes class defined for " 307 + "key [{0}], using default class.", attrName ); 308 309 ccAttr = defaultCCAttr; 310 } 311 312 log.debug( "Parsing options for \"{0}\"", attrName ); 313 314 PropertySetter.setProperties( ccAttr, props, attrName + "." ); 315 ccAttr.setCacheName( regName ); 316 317 log.debug( "End of parsing for \"{0}\"", attrName ); 318 319 // GET CACHE FROM FACTORY WITH ATTRIBUTES 320 ccAttr.setCacheName( regName ); 321 return ccAttr; 322 } 323 324 /** 325 * Create the element attributes from the properties object for a cache region. 326 *<p> 327 * @param props Configuration properties 328 * @param regName the region name 329 * @param defaultEAttr the default element attributes 330 * @param regionPrefix the region prefix 331 * 332 * @return IElementAttributes 333 */ 334 protected IElementAttributes parseElementAttributes( final Properties props, final String regName, 335 final IElementAttributes defaultEAttr, final String regionPrefix ) 336 { 337 IElementAttributes eAttr; 338 339 final String attrName = regionPrefix + regName + CompositeCacheConfigurator.ELEMENT_ATTRIBUTE_PREFIX; 340 341 // auxFactory was not previously initialized. 342 // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX; 343 eAttr = OptionConverter.instantiateByKey( props, attrName, null ); 344 if ( eAttr == null ) 345 { 346 log.info( "No special ElementAttribute class defined for key [{0}], " 347 + "using default class.", attrName ); 348 349 eAttr = defaultEAttr; 350 } 351 352 log.debug( "Parsing options for \"{0}\"", attrName ); 353 354 PropertySetter.setProperties( eAttr, props, attrName + "." ); 355 // eAttr.setCacheName( regName ); 356 357 log.debug( "End of parsing for \"{0}\"", attrName ); 358 359 // GET CACHE FROM FACTORY WITH ATTRIBUTES 360 // eAttr.setCacheName( regName ); 361 return eAttr; 362 } 363 364 /** 365 * Get an aux cache for the listed aux for a region. 366 *<p> 367 * @param props the configuration properties 368 * @param ccm Cache hub 369 * @param auxName the name of the auxiliary cache 370 * @param regName the name of the region. 371 * @return AuxiliaryCache 372 */ 373 protected <K, V> AuxiliaryCache<K, V> parseAuxiliary( final Properties props, final CompositeCacheManager ccm, 374 final String auxName, final String regName ) 375 { 376 log.debug( "parseAuxiliary {0}", auxName ); 377 378 // GET CACHE 379 AuxiliaryCache<K, V> auxCache = ccm.getAuxiliaryCache(auxName, regName); 380 381 if (auxCache == null) 382 { 383 // GET FACTORY 384 AuxiliaryCacheFactory auxFac = ccm.registryFacGet( auxName ); 385 if ( auxFac == null ) 386 { 387 // auxFactory was not previously initialized. 388 final String prefix = AUXILIARY_PREFIX + auxName; 389 auxFac = OptionConverter.instantiateByKey( props, prefix, null ); 390 if ( auxFac == null ) 391 { 392 log.error( "Could not instantiate auxFactory named \"{0}\"", auxName ); 393 return null; 394 } 395 396 auxFac.setName( auxName ); 397 398 if ( auxFac instanceof IRequireScheduler) 399 { 400 ((IRequireScheduler)auxFac).setScheduledExecutorService(ccm.getScheduledExecutorService()); 401 } 402 403 auxFac.initialize(); 404 ccm.registryFacPut( auxFac ); 405 } 406 407 // GET ATTRIBUTES 408 AuxiliaryCacheAttributes auxAttr = ccm.registryAttrGet( auxName ); 409 final String attrName = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX; 410 if ( auxAttr == null ) 411 { 412 // auxFactory was not previously initialized. 413 final String prefix = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX; 414 auxAttr = OptionConverter.instantiateByKey( props, prefix, null ); 415 if ( auxAttr == null ) 416 { 417 log.error( "Could not instantiate auxAttr named \"{0}\"", attrName ); 418 return null; 419 } 420 auxAttr.setName( auxName ); 421 ccm.registryAttrPut( auxAttr ); 422 } 423 424 auxAttr = auxAttr.clone(); 425 426 log.debug( "Parsing options for \"{0}\"", attrName ); 427 428 PropertySetter.setProperties( auxAttr, props, attrName + "." ); 429 auxAttr.setCacheName( regName ); 430 431 log.debug( "End of parsing for \"{0}\"", attrName ); 432 433 // GET CACHE FROM FACTORY WITH ATTRIBUTES 434 auxAttr.setCacheName( regName ); 435 436 final String auxPrefix = AUXILIARY_PREFIX + auxName; 437 438 // CONFIGURE THE EVENT LOGGER 439 final ICacheEventLogger cacheEventLogger = 440 AuxiliaryCacheConfigurator.parseCacheEventLogger( props, auxPrefix ); 441 442 // CONFIGURE THE ELEMENT SERIALIZER 443 final IElementSerializer elementSerializer = 444 AuxiliaryCacheConfigurator.parseElementSerializer( props, auxPrefix ); 445 446 // CONFIGURE THE KEYMATCHER 447 //IKeyMatcher keyMatcher = parseKeyMatcher( props, auxPrefix ); 448 // TODO add to factory interface 449 450 // Consider putting the compositeCache back in the factory interface 451 // since the manager may not know about it at this point. 452 // need to make sure the manager already has the cache 453 // before the auxiliary is created. 454 try 455 { 456 auxCache = auxFac.createCache( auxAttr, ccm, cacheEventLogger, elementSerializer ); 457 } 458 catch (final Exception e) 459 { 460 log.error( "Could not instantiate auxiliary cache named \"{0}\"", regName, e ); 461 return null; 462 } 463 464 ccm.addAuxiliaryCache(auxName, regName, auxCache); 465 } 466 467 return auxCache; 468 } 469 470 /** 471 * Any property values will be replaced with system property values that match the key. 472 * <p> 473 * @param props 474 */ 475 protected static void overrideWithSystemProperties( final Properties props ) 476 { 477 // override any setting with values from the system properties. 478 final Properties sysProps = System.getProperties(); 479 for (final String key : sysProps.stringPropertyNames()) 480 { 481 if ( key.startsWith( SYSTEM_PROPERTY_KEY_PREFIX ) ) 482 { 483 log.info( "Using system property [[{0}] [{1}]]", () -> key, 484 () -> sysProps.getProperty( key ) ); 485 props.setProperty( key, sysProps.getProperty( key ) ); 486 } 487 } 488 } 489 490 /** 491 * Creates a custom key matcher if one is defined. Else, it uses the default. 492 * <p> 493 * @param props 494 * @param auxPrefix - ex. AUXILIARY_PREFIX + auxName 495 * @return IKeyMatcher 496 */ 497 protected <K> IKeyMatcher<K> parseKeyMatcher( final Properties props, final String auxPrefix ) 498 { 499 500 // auxFactory was not previously initialized. 501 final String keyMatcherClassName = auxPrefix + KEY_MATCHER_PREFIX; 502 IKeyMatcher<K> keyMatcher = OptionConverter.instantiateByKey( props, keyMatcherClassName, null ); 503 if ( keyMatcher != null ) 504 { 505 final String attributePrefix = auxPrefix + KEY_MATCHER_PREFIX + ATTRIBUTE_PREFIX; 506 PropertySetter.setProperties( keyMatcher, props, attributePrefix + "." ); 507 log.info( "Using custom key matcher [{0}] for auxiliary [{1}]", keyMatcher, auxPrefix ); 508 } 509 else 510 { 511 // use the default standard serializer 512 keyMatcher = new KeyMatcherPatternImpl<>(); 513 log.info( "Using standard key matcher [{0}] for auxiliary [{1}]", keyMatcher, auxPrefix ); 514 } 515 return keyMatcher; 516 } 517}