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