blob: 3c6982a60c28b9084cbb4de4aac0b96b96296bc7 [file] [log] [blame]
package org.apache.jcs.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.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.auxiliary.AuxiliaryCache;
import org.apache.jcs.auxiliary.AuxiliaryCacheAttributes;
import org.apache.jcs.auxiliary.AuxiliaryCacheConfigurator;
import org.apache.jcs.auxiliary.AuxiliaryCacheFactory;
import org.apache.jcs.engine.behavior.ICache;
import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
import org.apache.jcs.engine.behavior.IElementAttributes;
import org.apache.jcs.engine.behavior.IElementSerializer;
import org.apache.jcs.engine.logging.behavior.ICacheEventLogger;
import org.apache.jcs.engine.match.KeyMatcherPatternImpl;
import org.apache.jcs.engine.match.behavior.IKeyMatcher;
import org.apache.jcs.utils.config.OptionConverter;
import org.apache.jcs.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 final static Log log = LogFactory.getLog( CompositeCacheConfigurator.class );
/** default region prefix */
final static String DEFAULT_REGION = "jcs.default";
/** normal region prefix */
final static String REGION_PREFIX = "jcs.region.";
/** system region prefix. might not be used */
final static String SYSTEM_REGION_PREFIX = "jcs.system.";
/** auxiliary prefix */
final static String AUXILIARY_PREFIX = "jcs.auxiliary.";
/** .attributes */
final static String ATTRIBUTE_PREFIX = ".attributes";
/** .cacheattributes */
final static String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes";
/** .elementattributes */
final static String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes";
/**
* jcs.auxiliary.NAME.keymatcher=CLASSNAME
* <p>
* jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE
*/
public final static String KEY_MATCHER_PREFIX = ".keymatcher";
/** Can't operate on the interface. */
private CompositeCacheManager compositeCacheManager;
/**
* Constructor for the CompositeCacheConfigurator object
*<p>
* @param ccMgr
*/
public CompositeCacheConfigurator( CompositeCacheManager ccMgr )
{
this.compositeCacheManager = ccMgr;
}
/**
* Configure cached for file name.
* <p>
* This is only used for testing. The manager handles the translation of a file into a
* properties object.
* <p>
* @param configFileName
*/
protected void doConfigure( String configFileName )
{
Properties props = new Properties();
try
{
FileInputStream istream = new FileInputStream( configFileName );
props.load( istream );
istream.close();
}
catch ( IOException e )
{
log.error( "Could not read configuration file, ignored: " + configFileName, e );
return;
}
// If we reach here, then the config file is alright.
doConfigure( props );
}
/**
* Configure cache for properties object.
* <p>
* This method proceeds in several steps:
* <ul>
* <li>Store props for use by non configured caches.
* <li>Set default value list
* <li>Set default cache attr
* <li>Set default element attr
* <li>Setup system caches to be used
* <li>Setup preconfigured caches
* </ul>
* @param properties
*/
public void doConfigure( Properties properties )
{
long start = System.currentTimeMillis();
// store props for use by non configured caches
compositeCacheManager.setConfigurationProperties( properties );
// set default value list
setDefaultAuxValues( properties );
// set default cache attr
setDefaultCompositeCacheAttributes( properties );
// set default element attr
setDefaultElementAttributes( properties );
// set up ssytem caches to be used by non system caches
// need to make sure there is no circuarity of reference
parseSystemRegions( properties );
// setup preconfigured caches
parseRegions( properties );
long end = System.currentTimeMillis();
if ( log.isInfoEnabled() )
{
log.info( "Finished configuration in " + ( end - start ) + " ms." );
}
}
/**
* Set the default aux list for new caches.
* <p>
* @param props
*/
protected void setDefaultAuxValues( Properties props )
{
String value = OptionConverter.findAndSubst( DEFAULT_REGION, props );
compositeCacheManager.defaultAuxValues = value;
if ( log.isInfoEnabled() )
{
log.info( "Setting default auxiliaries to " + value );
}
}
/**
* Set the default CompositeCacheAttributes for new caches.
*<p>
* @param props
*/
protected void setDefaultCompositeCacheAttributes( Properties props )
{
ICompositeCacheAttributes icca = parseCompositeCacheAttributes( props, "",
CompositeCacheConfigurator.DEFAULT_REGION );
compositeCacheManager.setDefaultCacheAttributes( icca );
log.info( "setting defaultCompositeCacheAttributes to " + icca );
}
/**
* Set the default ElementAttributes for new caches.
*<p>
* @param props
*/
protected void setDefaultElementAttributes( Properties props )
{
IElementAttributes iea = parseElementAttributes( props, "", CompositeCacheConfigurator.DEFAULT_REGION );
compositeCacheManager.setDefaultElementAttributes( iea );
log.info( "setting defaultElementAttributes to " + iea );
}
/**
* Create caches used internally. System status gives them creation priority.
*<p>
* @param props
*/
protected void parseSystemRegions( Properties props )
{
Enumeration en = props.propertyNames();
while ( en.hasMoreElements() )
{
String key = (String) en.nextElement();
if ( key.startsWith( SYSTEM_REGION_PREFIX ) && ( key.indexOf( "attributes" ) == -1 ) )
{
String regionName = key.substring( SYSTEM_REGION_PREFIX.length() );
String value = OptionConverter.findAndSubst( key, props );
ICache cache;
synchronized ( regionName )
{
cache = parseRegion( props, regionName, value, null, SYSTEM_REGION_PREFIX );
}
compositeCacheManager.systemCaches.put( regionName, cache );
// to be availiable for remote reference they need to be here as
// well
compositeCacheManager.caches.put( regionName, cache );
}
}
}
/**
* Parse region elements.
*<p>
* @param props
*/
protected void parseRegions( Properties props )
{
List regionNames = new ArrayList();
Enumeration en = props.propertyNames();
while ( en.hasMoreElements() )
{
String key = (String) en.nextElement();
if ( key.startsWith( REGION_PREFIX ) && ( key.indexOf( "attributes" ) == -1 ) )
{
String regionName = key.substring( REGION_PREFIX.length() );
regionNames.add( regionName );
String auxiliaryList = OptionConverter.findAndSubst( key, props );
ICache cache;
synchronized ( regionName )
{
cache = parseRegion( props, regionName, auxiliaryList );
}
compositeCacheManager.caches.put( regionName, cache );
}
}
if ( log.isInfoEnabled() )
{
log.info( "Parsed regions " + regionNames );
}
}
/**
* Create cache region.
*<p>
* @param props
* @param regName
* @param value
* @return CompositeCache
*/
protected CompositeCache parseRegion( Properties props, String regName, String value )
{
return parseRegion( props, regName, value, 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
* @param regName
* @param value
* @param cca
* @return CompositeCache
*/
protected CompositeCache parseRegion( Properties props, String regName, String value, ICompositeCacheAttributes cca )
{
return parseRegion( props, regName, value, cca, REGION_PREFIX );
}
/**
* Get all the properties for a region and configure its cache.
*<p>
* @param props
* @param regName
* @param value
* @param cca
* @param regionPrefix
* @return CompositeCache
*/
protected CompositeCache parseRegion( Properties props, String regName, String value,
ICompositeCacheAttributes cca, String regionPrefix )
{
// First, create or get the cache and element attributes, and create
// the cache.
if ( cca == null )
{
cca = parseCompositeCacheAttributes( props, regName, regionPrefix );
}
IElementAttributes ea = parseElementAttributes( props, regName, regionPrefix );
CompositeCache cache = new CompositeCache( regName, cca, ea );
// Next, create the auxiliaries for the new cache
List auxList = new ArrayList();
if ( log.isDebugEnabled() )
{
log.debug( "Parsing region name '" + regName + "', value '" + value + "'" );
}
// We must skip over ',' but not white space
StringTokenizer st = new StringTokenizer( value, "," );
// If value is not in the form ", appender.." or "", then we should set
// the priority of the category.
if ( !( value.startsWith( "," ) || value.equals( "" ) ) )
{
// just to be on the safe side...
if ( !st.hasMoreTokens() )
{
return null;
}
}
AuxiliaryCache auxCache;
String auxName;
while ( st.hasMoreTokens() )
{
auxName = st.nextToken().trim();
if ( auxName == null || auxName.equals( "," ) )
{
continue;
}
log.debug( "Parsing auxiliary named \"" + auxName + "\"." );
auxCache = parseAuxiliary( cache, props, auxName, regName );
if ( auxCache != null )
{
auxList.add( auxCache );
}
}
// Associate the auxiliaries with the cache
cache.setAuxCaches( (AuxiliaryCache[]) auxList.toArray( new AuxiliaryCache[0] ) );
// Return the new cache
return cache;
}
/**
* Get an ICompositeCacheAttributes for the listed region.
*<p>
* @param props
* @param regName
* @return ICompositeCacheAttributes
*/
protected ICompositeCacheAttributes parseCompositeCacheAttributes( Properties props, String regName )
{
return parseCompositeCacheAttributes( props, regName, REGION_PREFIX );
}
/**
* Get the main attributes for a region.
*<p>
* @param props
* @param regName
* @param regionPrefix
* @return ICompositeCacheAttributes
*/
protected ICompositeCacheAttributes parseCompositeCacheAttributes( Properties props, String regName,
String regionPrefix )
{
ICompositeCacheAttributes ccAttr;
String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX;
// auxFactory was not previously initialized.
// String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX;
ccAttr = (ICompositeCacheAttributes) OptionConverter
.instantiateByKey( props, attrName, org.apache.jcs.engine.behavior.ICompositeCacheAttributes.class, null );
if ( ccAttr == null )
{
if ( log.isInfoEnabled() )
{
log.info( "No special CompositeCacheAttributes class defined for key [" + attrName
+ "], using default class." );
}
ICompositeCacheAttributes ccAttr2 = compositeCacheManager.getDefaultCacheAttributes();
ccAttr = ccAttr2.copy();
}
if ( log.isDebugEnabled() )
{
log.debug( "Parsing options for '" + attrName + "'" );
}
PropertySetter.setProperties( ccAttr, props, attrName + "." );
ccAttr.setCacheName( regName );
if ( log.isDebugEnabled() )
{
log.debug( "End of parsing for \"" + 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
* @param regName
* @param regionPrefix
* @return IElementAttributes
*/
protected IElementAttributes parseElementAttributes( Properties props, String regName, String regionPrefix )
{
IElementAttributes eAttr;
String attrName = regionPrefix + regName + CompositeCacheConfigurator.ELEMENT_ATTRIBUTE_PREFIX;
// auxFactory was not previously initialized.
// String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX;
eAttr = (IElementAttributes) OptionConverter
.instantiateByKey( props, attrName, org.apache.jcs.engine.behavior.IElementAttributes.class, null );
if ( eAttr == null )
{
if ( log.isInfoEnabled() )
{
log.info( "No special ElementAttribute class defined for key [" + attrName + "], using default class." );
}
IElementAttributes eAttr2 = compositeCacheManager.getDefaultElementAttributes();
eAttr = eAttr2.copy();
}
if ( log.isDebugEnabled() )
{
log.debug( "Parsing options for '" + attrName + "'" );
}
PropertySetter.setProperties( eAttr, props, attrName + "." );
// eAttr.setCacheName( regName );
if ( log.isDebugEnabled() )
{
log.debug( "End of parsing for \"" + 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 cache the cache manager
* @param props the configuration properties
* @param auxName the name of the auxiliary cache
* @param regName the name of the region.
* @return AuxiliaryCache
*/
protected AuxiliaryCache parseAuxiliary( CompositeCache cache, Properties props, String auxName, String regName )
{
AuxiliaryCache auxCache;
if ( log.isDebugEnabled() )
{
// cache isn't used.
// TODO change method signature if is isn't needed.
log.debug( "parseAuxiliary, Cache = " + cache );
}
// GET FACTORY
AuxiliaryCacheFactory auxFac = compositeCacheManager.registryFacGet( auxName );
if ( auxFac == null )
{
// auxFactory was not previously initialized.
String prefix = AUXILIARY_PREFIX + auxName;
auxFac = (AuxiliaryCacheFactory) OptionConverter
.instantiateByKey( props, prefix, org.apache.jcs.auxiliary.AuxiliaryCacheFactory.class, null );
if ( auxFac == null )
{
log.error( "Could not instantiate auxFactory named \"" + auxName + "\"." );
return null;
}
auxFac.setName( auxName );
compositeCacheManager.registryFacPut( auxFac );
}
// GET ATTRIBUTES
AuxiliaryCacheAttributes auxAttr = compositeCacheManager.registryAttrGet( auxName );
String attrName = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX;
if ( auxAttr == null )
{
// auxFactory was not previously initialized.
String prefix = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX;
auxAttr = (AuxiliaryCacheAttributes) OptionConverter
.instantiateByKey( props, prefix, org.apache.jcs.auxiliary.AuxiliaryCacheAttributes.class, null );
if ( auxAttr == null )
{
log.error( "Could not instantiate auxAttr named '" + attrName + "'" );
return null;
}
auxAttr.setName( auxName );
compositeCacheManager.registryAttrPut( auxAttr );
}
auxAttr = auxAttr.copy();
if ( log.isDebugEnabled() )
{
log.debug( "Parsing options for '" + attrName + "'" );
}
PropertySetter.setProperties( auxAttr, props, attrName + "." );
auxAttr.setCacheName( regName );
if ( log.isDebugEnabled() )
{
log.debug( "End of parsing for '" + attrName + "'" );
}
// GET CACHE FROM FACTORY WITH ATTRIBUTES
auxAttr.setCacheName( regName );
String auxPrefix = AUXILIARY_PREFIX + auxName;
// CONFIGURE THE EVENT LOGGER
ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props, auxPrefix );
// CONFIGURE THE ELEMENT SERIALIZER
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.
auxCache = auxFac.createCache( auxAttr, compositeCacheManager, cacheEventLogger, elementSerializer );
return auxCache;
}
/**
* 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
*/
public static IKeyMatcher parseKeyMatcher( Properties props, String auxPrefix )
{
IKeyMatcher keyMatcher = null;
// auxFactory was not previously initialized.
String keyMatcherClassName = auxPrefix + KEY_MATCHER_PREFIX;
keyMatcher = (IKeyMatcher) OptionConverter
.instantiateByKey( props, keyMatcherClassName,
IKeyMatcher.class, null );
if ( keyMatcher != null )
{
String attributePrefix = auxPrefix + KEY_MATCHER_PREFIX + ATTRIBUTE_PREFIX;
PropertySetter.setProperties( keyMatcher, props, attributePrefix + "." );
if ( log.isInfoEnabled() )
{
log.info( "Using custom key matcher [" + keyMatcher + "] for auxiliary [" + auxPrefix
+ "]" );
}
}
else
{
// use the default standard serializer
keyMatcher = new KeyMatcherPatternImpl();
if ( log.isInfoEnabled() )
{
log.info( "Using standard key matcher [" + keyMatcher + "] for auxiliary [" + auxPrefix + "]" );
}
}
return keyMatcher;
}
}