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" &lt;Luke@quiq.com&gt;"Mark DONSZELMANN" &lt;Mark.Donszelmann@cern.ch&gt;"Anders Kristensen"
049 * &lt;akristensen@dynamicsoft.com&gt;
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}