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