| package org.apache.commons.jcs3.engine.control; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| |
| import org.apache.commons.jcs3.auxiliary.AuxiliaryCache; |
| import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes; |
| import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheConfigurator; |
| import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheFactory; |
| import org.apache.commons.jcs3.engine.behavior.ICache; |
| import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes; |
| import org.apache.commons.jcs3.engine.behavior.IElementAttributes; |
| import org.apache.commons.jcs3.engine.behavior.IElementSerializer; |
| import org.apache.commons.jcs3.engine.behavior.IRequireScheduler; |
| import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger; |
| import org.apache.commons.jcs3.engine.match.KeyMatcherPatternImpl; |
| import org.apache.commons.jcs3.engine.match.behavior.IKeyMatcher; |
| import org.apache.commons.jcs3.log.Log; |
| import org.apache.commons.jcs3.log.LogManager; |
| import org.apache.commons.jcs3.utils.config.OptionConverter; |
| import org.apache.commons.jcs3.utils.config.PropertySetter; |
| |
| /** |
| * This class configures JCS based on a properties object. |
| * <p> |
| * This class is based on the log4j class org.apache.log4j.PropertyConfigurator which was made by: |
| * "Luke Blanshard" <Luke@quiq.com>"Mark DONSZELMANN" <Mark.Donszelmann@cern.ch>"Anders Kristensen" |
| * <akristensen@dynamicsoft.com> |
| */ |
| public class CompositeCacheConfigurator |
| { |
| /** The logger */ |
| private static final Log log = LogManager.getLog( CompositeCacheConfigurator.class ); |
| |
| /** The prefix of relevant system properties */ |
| protected static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs"; |
| |
| /** normal region prefix */ |
| protected static final String REGION_PREFIX = "jcs.region."; |
| |
| /** system region prefix. might not be used */ |
| protected static final String SYSTEM_REGION_PREFIX = "jcs.system."; |
| |
| /** auxiliary prefix */ |
| protected static final String AUXILIARY_PREFIX = "jcs.auxiliary."; |
| |
| /** .attributes */ |
| protected static final String ATTRIBUTE_PREFIX = ".attributes"; |
| |
| /** .cacheattributes */ |
| protected static final String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes"; |
| |
| /** .elementattributes */ |
| protected static final String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes"; |
| |
| /** |
| * jcs.auxiliary.NAME.keymatcher=CLASSNAME |
| * <p> |
| * jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE |
| */ |
| public static final String KEY_MATCHER_PREFIX = ".keymatcher"; |
| |
| /** |
| * Constructor for the CompositeCacheConfigurator object |
| */ |
| public CompositeCacheConfigurator() |
| { |
| // empty |
| } |
| |
| /** |
| * Create caches used internally. System status gives them creation priority. |
| *<p> |
| * @param props Configuration properties |
| * @param ccm Cache hub |
| */ |
| protected void parseSystemRegions( final Properties props, final CompositeCacheManager ccm ) |
| { |
| for (final String key : props.stringPropertyNames() ) |
| { |
| if ( key.startsWith( SYSTEM_REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 ) |
| { |
| final String regionName = key.substring( SYSTEM_REGION_PREFIX.length() ); |
| final String auxiliaries = OptionConverter.findAndSubst( key, props ); |
| ICache<?, ?> cache; |
| synchronized ( regionName ) |
| { |
| cache = parseRegion( props, ccm, regionName, auxiliaries, null, SYSTEM_REGION_PREFIX ); |
| } |
| ccm.addCache( regionName, cache ); |
| } |
| } |
| } |
| |
| /** |
| * Parse region elements. |
| *<p> |
| * @param props Configuration properties |
| * @param ccm Cache hub |
| */ |
| protected void parseRegions( final Properties props, final CompositeCacheManager ccm ) |
| { |
| final List<String> regionNames = new ArrayList<>(); |
| |
| for (final String key : props.stringPropertyNames() ) |
| { |
| if ( key.startsWith( REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 ) |
| { |
| final String regionName = key.substring( REGION_PREFIX.length() ); |
| regionNames.add( regionName ); |
| final String auxiliaries = OptionConverter.findAndSubst( key, props ); |
| ICache<?, ?> cache; |
| synchronized ( regionName ) |
| { |
| cache = parseRegion( props, ccm, regionName, auxiliaries ); |
| } |
| ccm.addCache( regionName, cache ); |
| } |
| } |
| |
| log.info( "Parsed regions {0}", regionNames ); |
| } |
| |
| /** |
| * Create cache region. |
| *<p> |
| * @param props Configuration properties |
| * @param ccm Cache hub |
| * @param regName Name of the cache region |
| * @param auxiliaries Comma separated list of auxiliaries |
| * |
| * @return CompositeCache |
| */ |
| protected <K, V> CompositeCache<K, V> parseRegion( |
| final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries ) |
| { |
| return parseRegion( props, ccm, regName, auxiliaries, null, REGION_PREFIX ); |
| } |
| |
| /** |
| * Get all the properties for a region and configure its cache. |
| * <p> |
| * This method tells the other parse method the name of the region prefix. |
| *<p> |
| * @param props Configuration properties |
| * @param ccm Cache hub |
| * @param regName Name of the cache region |
| * @param auxiliaries Comma separated list of auxiliaries |
| * @param cca Cache configuration |
| * |
| * @return CompositeCache |
| */ |
| protected <K, V> CompositeCache<K, V> parseRegion( |
| final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries, |
| final ICompositeCacheAttributes cca ) |
| { |
| return parseRegion( props, ccm, regName, auxiliaries, cca, REGION_PREFIX ); |
| } |
| |
| /** |
| * Get all the properties for a region and configure its cache. |
| *<p> |
| * @param props Configuration properties |
| * @param ccm Cache hub |
| * @param regName Name of the cache region |
| * @param auxiliaries Comma separated list of auxiliaries |
| * @param cca Cache configuration |
| * @param regionPrefix Prefix for the region |
| * |
| * @return CompositeCache |
| */ |
| protected <K, V> CompositeCache<K, V> parseRegion( |
| final Properties props, final CompositeCacheManager ccm, final String regName, final String auxiliaries, |
| final ICompositeCacheAttributes cca, final String regionPrefix ) |
| { |
| // First, create or get the cache and element attributes, and create |
| // the cache. |
| final IElementAttributes ea = parseElementAttributes( props, regName, |
| ccm.getDefaultElementAttributes(), regionPrefix ); |
| |
| final ICompositeCacheAttributes instantiationCca = cca == null |
| ? parseCompositeCacheAttributes(props, regName, ccm.getDefaultCacheAttributes(), regionPrefix) |
| : cca; |
| final CompositeCache<K, V> cache = newCache(instantiationCca, ea); |
| |
| // Inject cache manager |
| cache.setCompositeCacheManager(ccm); |
| |
| // Inject scheduler service |
| cache.setScheduledExecutorService(ccm.getScheduledExecutorService()); |
| |
| // Inject element event queue |
| cache.setElementEventQueue(ccm.getElementEventQueue()); |
| |
| if (cache.getMemoryCache() instanceof IRequireScheduler) |
| { |
| ((IRequireScheduler)cache.getMemoryCache()).setScheduledExecutorService( |
| ccm.getScheduledExecutorService()); |
| } |
| |
| if (auxiliaries != null) |
| { |
| // Next, create the auxiliaries for the new cache |
| final List<AuxiliaryCache<K, V>> auxList = new ArrayList<>(); |
| |
| log.debug( "Parsing region name \"{0}\", value \"{1}\"", regName, auxiliaries ); |
| |
| // We must skip over ',' but not white space |
| final StringTokenizer st = new StringTokenizer( auxiliaries, "," ); |
| |
| // If value is not in the form ", appender.." or "", then we should set |
| // the priority of the category. |
| |
| if ( !( auxiliaries.startsWith( "," ) || auxiliaries.equals( "" ) ) ) |
| { |
| // just to be on the safe side... |
| if ( !st.hasMoreTokens() ) |
| { |
| return null; |
| } |
| } |
| |
| AuxiliaryCache<K, V> auxCache; |
| String auxName; |
| while ( st.hasMoreTokens() ) |
| { |
| auxName = st.nextToken().trim(); |
| if ( auxName == null || auxName.equals( "," ) ) |
| { |
| continue; |
| } |
| log.debug( "Parsing auxiliary named \"{0}\".", auxName ); |
| |
| auxCache = parseAuxiliary( props, ccm, auxName, regName ); |
| |
| if ( auxCache != null ) |
| { |
| if (auxCache instanceof IRequireScheduler) |
| { |
| ((IRequireScheduler) auxCache).setScheduledExecutorService( |
| ccm.getScheduledExecutorService()); |
| } |
| |
| auxList.add( auxCache ); |
| } |
| } |
| |
| // Associate the auxiliaries with the cache |
| @SuppressWarnings("unchecked") // No generic arrays in java |
| final |
| AuxiliaryCache<K, V>[] auxArray = auxList.toArray( new AuxiliaryCache[0] ); |
| cache.setAuxCaches( auxArray ); |
| } |
| |
| // Return the new cache |
| return cache; |
| } |
| |
| protected <K, V> CompositeCache<K, V> newCache( |
| final ICompositeCacheAttributes cca, final IElementAttributes ea) |
| { |
| return new CompositeCache<>( cca, ea ); |
| } |
| |
| /** |
| * Get an ICompositeCacheAttributes for the listed region. |
| *<p> |
| * @param props Configuration properties |
| * @param regName the region name |
| * @param defaultCCAttr the default cache attributes |
| * |
| * @return ICompositeCacheAttributes |
| */ |
| protected ICompositeCacheAttributes parseCompositeCacheAttributes( final Properties props, |
| final String regName, final ICompositeCacheAttributes defaultCCAttr ) |
| { |
| return parseCompositeCacheAttributes( props, regName, defaultCCAttr, REGION_PREFIX ); |
| } |
| |
| /** |
| * Get the main attributes for a region. |
| *<p> |
| * @param props Configuration properties |
| * @param regName the region name |
| * @param defaultCCAttr the default cache attributes |
| * @param regionPrefix the region prefix |
| * |
| * @return ICompositeCacheAttributes |
| */ |
| protected ICompositeCacheAttributes parseCompositeCacheAttributes( final Properties props, |
| final String regName, final ICompositeCacheAttributes defaultCCAttr, final String regionPrefix ) |
| { |
| ICompositeCacheAttributes ccAttr; |
| |
| final String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX; |
| |
| // auxFactory was not previously initialized. |
| // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX; |
| ccAttr = OptionConverter.instantiateByKey( props, attrName, null ); |
| |
| if ( ccAttr == null ) |
| { |
| log.info( "No special CompositeCacheAttributes class defined for " |
| + "key [{0}], using default class.", attrName ); |
| |
| ccAttr = defaultCCAttr; |
| } |
| |
| log.debug( "Parsing options for \"{0}\"", attrName ); |
| |
| PropertySetter.setProperties( ccAttr, props, attrName + "." ); |
| ccAttr.setCacheName( regName ); |
| |
| log.debug( "End of parsing for \"{0}\"", attrName ); |
| |
| // GET CACHE FROM FACTORY WITH ATTRIBUTES |
| ccAttr.setCacheName( regName ); |
| return ccAttr; |
| } |
| |
| /** |
| * Create the element attributes from the properties object for a cache region. |
| *<p> |
| * @param props Configuration properties |
| * @param regName the region name |
| * @param defaultEAttr the default element attributes |
| * @param regionPrefix the region prefix |
| * |
| * @return IElementAttributes |
| */ |
| protected IElementAttributes parseElementAttributes( final Properties props, final String regName, |
| final IElementAttributes defaultEAttr, final String regionPrefix ) |
| { |
| IElementAttributes eAttr; |
| |
| final String attrName = regionPrefix + regName + CompositeCacheConfigurator.ELEMENT_ATTRIBUTE_PREFIX; |
| |
| // auxFactory was not previously initialized. |
| // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX; |
| eAttr = OptionConverter.instantiateByKey( props, attrName, null ); |
| if ( eAttr == null ) |
| { |
| log.info( "No special ElementAttribute class defined for key [{0}], " |
| + "using default class.", attrName ); |
| |
| eAttr = defaultEAttr; |
| } |
| |
| log.debug( "Parsing options for \"{0}\"", attrName ); |
| |
| PropertySetter.setProperties( eAttr, props, attrName + "." ); |
| // eAttr.setCacheName( regName ); |
| |
| log.debug( "End of parsing for \"{0}\"", attrName ); |
| |
| // GET CACHE FROM FACTORY WITH ATTRIBUTES |
| // eAttr.setCacheName( regName ); |
| return eAttr; |
| } |
| |
| /** |
| * Get an aux cache for the listed aux for a region. |
| *<p> |
| * @param props the configuration properties |
| * @param ccm Cache hub |
| * @param auxName the name of the auxiliary cache |
| * @param regName the name of the region. |
| * @return AuxiliaryCache |
| */ |
| protected <K, V> AuxiliaryCache<K, V> parseAuxiliary( final Properties props, final CompositeCacheManager ccm, |
| final String auxName, final String regName ) |
| { |
| log.debug( "parseAuxiliary {0}", auxName ); |
| |
| // GET CACHE |
| @SuppressWarnings("unchecked") // Common map for all caches |
| AuxiliaryCache<K, V> auxCache = (AuxiliaryCache<K, V>) ccm.getAuxiliaryCache(auxName, regName); |
| |
| if (auxCache == null) |
| { |
| // GET FACTORY |
| AuxiliaryCacheFactory auxFac = ccm.registryFacGet( auxName ); |
| if ( auxFac == null ) |
| { |
| // auxFactory was not previously initialized. |
| final String prefix = AUXILIARY_PREFIX + auxName; |
| auxFac = OptionConverter.instantiateByKey( props, prefix, null ); |
| if ( auxFac == null ) |
| { |
| log.error( "Could not instantiate auxFactory named \"{0}\"", auxName ); |
| return null; |
| } |
| |
| auxFac.setName( auxName ); |
| |
| if ( auxFac instanceof IRequireScheduler) |
| { |
| ((IRequireScheduler)auxFac).setScheduledExecutorService(ccm.getScheduledExecutorService()); |
| } |
| |
| auxFac.initialize(); |
| ccm.registryFacPut( auxFac ); |
| } |
| |
| // GET ATTRIBUTES |
| AuxiliaryCacheAttributes auxAttr = ccm.registryAttrGet( auxName ); |
| final String attrName = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX; |
| if ( auxAttr == null ) |
| { |
| // auxFactory was not previously initialized. |
| final String prefix = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX; |
| auxAttr = OptionConverter.instantiateByKey( props, prefix, null ); |
| if ( auxAttr == null ) |
| { |
| log.error( "Could not instantiate auxAttr named \"{0}\"", attrName ); |
| return null; |
| } |
| auxAttr.setName( auxName ); |
| ccm.registryAttrPut( auxAttr ); |
| } |
| |
| auxAttr = auxAttr.clone(); |
| |
| log.debug( "Parsing options for \"{0}\"", attrName ); |
| |
| PropertySetter.setProperties( auxAttr, props, attrName + "." ); |
| auxAttr.setCacheName( regName ); |
| |
| log.debug( "End of parsing for \"{0}\"", attrName ); |
| |
| // GET CACHE FROM FACTORY WITH ATTRIBUTES |
| auxAttr.setCacheName( regName ); |
| |
| final String auxPrefix = AUXILIARY_PREFIX + auxName; |
| |
| // CONFIGURE THE EVENT LOGGER |
| final ICacheEventLogger cacheEventLogger = |
| AuxiliaryCacheConfigurator.parseCacheEventLogger( props, auxPrefix ); |
| |
| // CONFIGURE THE ELEMENT SERIALIZER |
| final IElementSerializer elementSerializer = |
| AuxiliaryCacheConfigurator.parseElementSerializer( props, auxPrefix ); |
| |
| // CONFIGURE THE KEYMATCHER |
| //IKeyMatcher keyMatcher = parseKeyMatcher( props, auxPrefix ); |
| // TODO add to factory interface |
| |
| // Consider putting the compositeCache back in the factory interface |
| // since the manager may not know about it at this point. |
| // need to make sure the manager already has the cache |
| // before the auxiliary is created. |
| try |
| { |
| auxCache = auxFac.createCache( auxAttr, ccm, cacheEventLogger, elementSerializer ); |
| } |
| catch (final Exception e) |
| { |
| log.error( "Could not instantiate auxiliary cache named \"{0}\"", regName, e ); |
| return null; |
| } |
| |
| ccm.addAuxiliaryCache(auxName, regName, auxCache); |
| } |
| |
| return auxCache; |
| } |
| |
| /** |
| * Any property values will be replaced with system property values that match the key. |
| * <p> |
| * @param props |
| */ |
| protected static void overrideWithSystemProperties( final Properties props ) |
| { |
| // override any setting with values from the system properties. |
| final Properties sysProps = System.getProperties(); |
| for (final String key : sysProps.stringPropertyNames()) |
| { |
| if ( key.startsWith( SYSTEM_PROPERTY_KEY_PREFIX ) ) |
| { |
| log.info( "Using system property [[{0}] [{1}]]", () -> key, |
| () -> sysProps.getProperty( key ) ); |
| props.setProperty( key, sysProps.getProperty( key ) ); |
| } |
| } |
| } |
| |
| /** |
| * Creates a custom key matcher if one is defined. Else, it uses the default. |
| * <p> |
| * @param props |
| * @param auxPrefix - ex. AUXILIARY_PREFIX + auxName |
| * @return IKeyMatcher |
| */ |
| protected <K> IKeyMatcher<K> parseKeyMatcher( final Properties props, final String auxPrefix ) |
| { |
| |
| // auxFactory was not previously initialized. |
| final String keyMatcherClassName = auxPrefix + KEY_MATCHER_PREFIX; |
| IKeyMatcher<K> keyMatcher = OptionConverter.instantiateByKey( props, keyMatcherClassName, null ); |
| if ( keyMatcher != null ) |
| { |
| final String attributePrefix = auxPrefix + KEY_MATCHER_PREFIX + ATTRIBUTE_PREFIX; |
| PropertySetter.setProperties( keyMatcher, props, attributePrefix + "." ); |
| log.info( "Using custom key matcher [{0}] for auxiliary [{1}]", keyMatcher, auxPrefix ); |
| } |
| else |
| { |
| // use the default standard serializer |
| keyMatcher = new KeyMatcherPatternImpl<>(); |
| log.info( "Using standard key matcher [{0}] for auxiliary [{1}]", keyMatcher, auxPrefix ); |
| } |
| return keyMatcher; |
| } |
| } |