View Javadoc
1   package org.apache.commons.jcs.engine.control;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Properties;
25  import java.util.StringTokenizer;
26  
27  import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
28  import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
29  import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
30  import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
31  import org.apache.commons.jcs.engine.behavior.ICache;
32  import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
33  import org.apache.commons.jcs.engine.behavior.IElementAttributes;
34  import org.apache.commons.jcs.engine.behavior.IElementSerializer;
35  import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
36  import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
37  import org.apache.commons.jcs.engine.match.KeyMatcherPatternImpl;
38  import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
39  import org.apache.commons.jcs.utils.config.OptionConverter;
40  import org.apache.commons.jcs.utils.config.PropertySetter;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * This class configures JCS based on a properties object.
46   * <p>
47   * This class is based on the log4j class org.apache.log4j.PropertyConfigurator which was made by:
48   * "Luke Blanshard" &lt;Luke@quiq.com&gt;"Mark DONSZELMANN" &lt;Mark.Donszelmann@cern.ch&gt;"Anders Kristensen"
49   * &lt;akristensen@dynamicsoft.com&gt;
50   */
51  public class CompositeCacheConfigurator
52  {
53      /** The logger */
54      private static final Log log = LogFactory.getLog( CompositeCacheConfigurator.class );
55  
56      /** The prefix of relevant system properties */
57      protected static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs";
58  
59      /** normal region prefix */
60      protected static final String REGION_PREFIX = "jcs.region.";
61  
62      /** system region prefix. might not be used */
63      protected static final String SYSTEM_REGION_PREFIX = "jcs.system.";
64  
65      /** auxiliary prefix */
66      protected static final String AUXILIARY_PREFIX = "jcs.auxiliary.";
67  
68      /** .attributes */
69      protected static final String ATTRIBUTE_PREFIX = ".attributes";
70  
71      /** .cacheattributes */
72      protected static final String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes";
73  
74      /** .elementattributes */
75      protected static final String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes";
76  
77      /**
78       * jcs.auxiliary.NAME.keymatcher=CLASSNAME
79       * <p>
80       * jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE
81       */
82      public static final String KEY_MATCHER_PREFIX = ".keymatcher";
83  
84      /**
85       * Constructor for the CompositeCacheConfigurator object
86       */
87      public CompositeCacheConfigurator()
88      {
89          // empty
90      }
91  
92      /**
93       * Create caches used internally. System status gives them creation priority.
94       *<p>
95       * @param props Configuration properties
96       * @param ccm Cache hub
97       */
98      protected void parseSystemRegions( Properties props, CompositeCacheManager ccm )
99      {
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 }