View Javadoc
1   package org.apache.commons.jcs3.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  
26  import org.apache.commons.jcs3.auxiliary.AuxiliaryCache;
27  import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes;
28  import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheConfigurator;
29  import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheFactory;
30  import org.apache.commons.jcs3.engine.behavior.ICache;
31  import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
32  import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
33  import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
34  import org.apache.commons.jcs3.engine.behavior.IRequireScheduler;
35  import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
36  import org.apache.commons.jcs3.engine.match.KeyMatcherPatternImpl;
37  import org.apache.commons.jcs3.engine.match.behavior.IKeyMatcher;
38  import org.apache.commons.jcs3.log.Log;
39  import org.apache.commons.jcs3.log.LogManager;
40  import org.apache.commons.jcs3.utils.config.OptionConverter;
41  import org.apache.commons.jcs3.utils.config.PropertySetter;
42  
43  /**
44   * This class configures JCS based on a properties object.
45   * <p>
46   * This class is based on the log4j class org.apache.log4j.PropertyConfigurator which was made by:
47   * "Luke Blanshard" &lt;Luke@quiq.com&gt;"Mark DONSZELMANN" &lt;Mark.Donszelmann@cern.ch&gt;"Anders Kristensen"
48   * &lt;akristensen@dynamicsoft.com&gt;
49   */
50  public class CompositeCacheConfigurator
51  {
52      /** The logger */
53      private static final Log log = LogManager.getLog( CompositeCacheConfigurator.class );
54  
55      /** The prefix of relevant system properties */
56      protected static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs";
57  
58      /** normal region prefix */
59      protected static final String REGION_PREFIX = "jcs.region.";
60  
61      /** system region prefix. might not be used */
62      protected static final String SYSTEM_REGION_PREFIX = "jcs.system.";
63  
64      /** auxiliary prefix */
65      protected static final String AUXILIARY_PREFIX = "jcs.auxiliary.";
66  
67      /** .attributes */
68      protected static final String ATTRIBUTE_PREFIX = ".attributes";
69  
70      /** .cacheattributes */
71      protected static final String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes";
72  
73      /** .elementattributes */
74      protected static final String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes";
75  
76      /**
77       * jcs.auxiliary.NAME.keymatcher=CLASSNAME
78       * <p>
79       * jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE
80       */
81      public static final String KEY_MATCHER_PREFIX = ".keymatcher";
82  
83      /**
84       * Constructor for the CompositeCacheConfigurator object
85       */
86      public CompositeCacheConfigurator()
87      {
88          // empty
89      }
90  
91      /**
92       * Create caches used internally. System status gives them creation priority.
93       *<p>
94       * @param props Configuration properties
95       * @param ccm Cache hub
96       */
97      protected void parseSystemRegions( final Properties props, final CompositeCacheManager ccm )
98      {
99          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 }