blob: f8b795185ef40871a75d118e4a85e9b8c2f8b3e8 [file] [log] [blame]
package org.apache.jcs.engine.control;
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed 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.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.access.exception.CacheException;
import org.apache.jcs.access.exception.ObjectNotFoundException;
import org.apache.jcs.auxiliary.AuxiliaryCache;
import org.apache.jcs.engine.CacheConstants;
import org.apache.jcs.engine.CacheElement;
import org.apache.jcs.engine.behavior.ICache;
import org.apache.jcs.engine.behavior.ICacheElement;
import org.apache.jcs.engine.behavior.ICacheType;
import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
import org.apache.jcs.engine.behavior.IElementAttributes;
import org.apache.jcs.engine.memory.MemoryCache;
import org.apache.jcs.engine.memory.lru.LRUMemoryCache;
import org.apache.jcs.engine.stats.CacheStats;
import org.apache.jcs.engine.stats.StatElement;
import org.apache.jcs.engine.stats.Stats;
import org.apache.jcs.engine.stats.behavior.ICacheStats;
import org.apache.jcs.engine.stats.behavior.IStatElement;
import org.apache.jcs.engine.stats.behavior.IStats;
import org.apache.jcs.engine.control.event.ElementEvent;
import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;
import org.apache.jcs.engine.control.event.behavior.IElementEvent;
import org.apache.jcs.engine.control.event.behavior.IElementEventConstants;
import org.apache.jcs.engine.control.event.behavior.IElementEventQueue;
import org.apache.jcs.engine.control.event.ElementEventQueue;
import org.apache.jcs.engine.control.group.GroupId;
/**
* This is the primary hub for a single cache/region. It control the flow of
* items through the cache. The auxiliary and memory caches are plugged in here.
*
* @version $Id$
*/
public class CompositeCache
implements ICache, Serializable
{
private final static Log log = LogFactory.getLog( CompositeCache.class );
/**
* EventQueue for handling element events.
* 1 should be enough for all the regions. Else should create as needed
* per region.
*/
public static IElementEventQueue elementEventQ = new ElementEventQueue( "AllRegionQueue" );
// Auxiliary caches.
private AuxiliaryCache[] auxCaches;
private boolean alive = true;
// this is int he cacheAttr, shouldn't be used, remove
final String cacheName;
/**
* Region Elemental Attributes, default
*/
public IElementAttributes attr;
/**
* Cache Attributes, for hub and memory auxiliary
*/
public ICompositeCacheAttributes cacheAttr;
// Statistics
/** Memory cache hit count */
private int hitCountRam;
/** Auxiliary cache hit count (number of times found in ANY auxiliary) */
private int hitCountAux;
/** Auxiliary hit counts broken down by auxiliary */
private int[] auxHitCountByIndex;
/** Count of misses where element was not found */
private int missCountNotFound = 0;
/** Count of misses where element was expired */
private int missCountExpired = 0;
/**
* The cache hub can only have one memory cache. This could be made more
* flexible in the future, but they are tied closely together. More than
* one doesn't make much sense.
*/
MemoryCache memCache;
/**
* Constructor for the Cache object
*
*@param cacheName The name of the region
*@param cattr The cache attribute
*@param attr The default element attributes
*/
public CompositeCache( String cacheName,
ICompositeCacheAttributes cattr,
IElementAttributes attr )
{
this.cacheName = cacheName;
this.attr = attr;
this.cacheAttr = cattr;
createMemoryCache( cattr );
if ( log.isDebugEnabled() )
{
log.debug( "Constructed cache with name " + cacheName +
" and cache attributes: " + cattr );
}
else if ( log.isInfoEnabled() )
{
log.info( "Constructed cache with name: " + cacheName );
}
}
/**
* This sets the list of auxiliary caches for this region.
*
* @param auxCaches
*/
public void setAuxCaches( AuxiliaryCache[] auxCaches )
{
this.auxCaches = auxCaches;
if ( auxCaches != null )
{
this.auxHitCountByIndex = new int[auxCaches.length];
}
}
/**
* Standard update method
*
*@param ce
*@exception IOException
*/
public synchronized void update( ICacheElement ce )
throws IOException
{
update( ce, false );
}
/**
* Standard update method
*
*@param ce
*@exception IOException
*/
public synchronized void localUpdate( ICacheElement ce )
throws IOException
{
update( ce, true );
}
/**
* Put an item into the cache. If it is localOnly, then do no notify remote
* or lateral auxiliaries.
*
* @param ce, the ICacheElement
* @param localOnly, Whether the operation should be restricted to local auxiliaries.
* @exception IOException
*/
protected synchronized void update( ICacheElement ce, boolean localOnly )
throws IOException
{
if ( ce.getKey() instanceof String
&& ce.getKey().toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
{
throw new IllegalArgumentException( "key must not end with "
+ CacheConstants.NAME_COMPONENT_DELIMITER
+ " for a put operation" );
}
else if ( ce.getKey() instanceof GroupId )
{
throw new IllegalArgumentException( "key cannot be a GroupId "
+ " for a put operation" );
}
log.debug( "Updating memory cache" );
memCache.update( ce );
// Updates to all auxiliary caches -- remote and laterals, can add as many of each
// as necessary.
// could put the update criteria in each but it would a bit cumbersome
// the disk cache would have to check the cache size, the lateral
// would have to check the region cattr configuration
// UPDATE AUXILLIARY CACHES
// There are 3 types of auxiliary caches: remote, lateral, and disk
// more can be added if future auxiliary caches don't fit the model
// You could run a database cache as either a remote or a local disk.
// The types would describe the purpose.
if ( log.isDebugEnabled() )
{
if ( auxCaches.length > 0 )
{
log.debug( "Updating auxilliary caches" );
}
else
{
log.debug( "No auxilliary cache to update" );
}
}
for ( int i = 0; i < auxCaches.length; i++ )
{
ICache aux = auxCaches[i];
if ( log.isDebugEnabled() )
{
log.debug( "Auxilliary cache type: " + aux.getCacheType() );
}
// SEND TO REMOTE STORE
if ( aux != null && aux.getCacheType() == ICache.REMOTE_CACHE )
{
if ( log.isDebugEnabled() )
{
log.debug( "ce.getElementAttributes().getIsRemote() = "
+ ce.getElementAttributes().getIsRemote() );
}
if ( ce.getElementAttributes().getIsRemote()
&& ! localOnly )
{
try
{
// need to make sure the group cache understands that the
// key is a group attribute on update
aux.update( ce );
if ( log.isDebugEnabled() )
{
log.debug( "Updated remote store for "
+ ce.getKey() + ce );
}
}
catch ( IOException ex )
{
log.error( "Failure in updateExclude", ex );
}
}
// SEND LATERALLY
}
else if ( aux != null
&& aux.getCacheType() == ICache.LATERAL_CACHE )
{
// lateral can't do the checking since it is dependent on the cache region
// restrictions
if ( log.isDebugEnabled() )
{
log.debug( "lateralcache in aux list: cattr " +
cacheAttr.getUseLateral() );
}
if ( cacheAttr.getUseLateral()
&& ce.getElementAttributes().getIsLateral()
&& ! localOnly )
{
// later if we want a multicast, possibly delete abnormal broadcaster
// DISTRIBUTE LATERALLY
// Currently always multicast even if the value is unchanged,
// just to cause the cache item to move to the front.
aux.update( ce );
if ( log.isDebugEnabled() )
{
log.debug( "updated lateral cache for " + ce.getKey() );
}
}
}
else if ( aux != null && aux.getCacheType() == ICache.DISK_CACHE )
{
// do nothing, the memory manager will call spool where necesary
// TODO: add option to put all element on disk
}
}
return;
}
/**
* Writes the specified element to any disk auxilliaries Might want to
* rename this "overflow" incase the hub wants to do something else.
*
*@param ce The CacheElement
*/
public void spoolToDisk( ICacheElement ce )
{
// if the item is not spoolable, return
if ( !ce.getElementAttributes().getIsSpool() )
{
//TODO define an event for this.
handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_NOT_ALLOWED );
return;
}
boolean diskAvailable = false;
// SPOOL TO DISK.
for ( int i = 0; i < auxCaches.length; i++ )
{
ICache aux = auxCaches[i];
if ( aux != null && aux.getCacheType() == ICache.DISK_CACHE )
{
diskAvailable = true;
// write the last items to disk.2
try
{
handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE );
aux.update( ce );
}
catch ( IOException ex )
{
// impossible case.
ex.printStackTrace();
throw new IllegalStateException( ex.getMessage() );
}
catch ( Exception oee )
{
}
if ( log.isDebugEnabled() )
{
log.debug( "spoolToDisk done for: " + ce.getKey() + " on disk cache[" + i + "]" );
}
}
}
if ( !diskAvailable )
{
try
{
handleElementEvent( ce, IElementEventConstants.ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE );
}
catch ( Exception e )
{
log.error( "Trouble handling the ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE element event", e );
}
}
}
/*
* (non-Javadoc)
* @see org.apache.jcs.engine.behavior.ICache#get(java.io.Serializable)
*/
public ICacheElement get( Serializable key )
{
return get( key, false );
}
/**
* Do not try to go remote or laterally for this get.
*
* @param key
* @return ICacheElement
*/
public ICacheElement localGet( Serializable key )
{
return get( key, true );
}
/**
* Look in memory, then disk, remote, or laterally for this item. The order
* is dependent on the order in the cache.ccf file.
* <p>
* Do not try to go remote or laterally for this get if it is localOnly.
* Otherwise try to go remote or lateral if such an auxiliary is configured
* for this region.
*
* @param key
* @param localOnly
* @return ICacheElement
*/
protected ICacheElement get( Serializable key, boolean localOnly )
{
ICacheElement element = null;
boolean found = false;
if ( log.isDebugEnabled() )
{
log.debug( "get: key = " + key + ", localOnly = " + localOnly );
}
try
{
// First look in memory cache
element = memCache.get( key );
if ( element != null )
{
// Found in memory cache
if ( isExpired( element ) )
{
if ( log.isDebugEnabled() )
{
log.debug( cacheName +
" - Memory cache hit, but element expired" );
}
missCountExpired++;
remove( key );
element = null;
}
else
{
if ( log.isDebugEnabled() )
{
log.debug( cacheName + " - Memory cache hit" );
}
// Update counters
hitCountRam++;
}
found = true;
}
else
{
// Item not found in memory. If local invocation look in aux
// caches, even if not local look in disk auxiliaries
for ( int i = 0; i < auxCaches.length; i++ )
{
AuxiliaryCache aux = auxCaches[i];
if ( aux != null )
{
long cacheType = aux.getCacheType();
if ( ! localOnly || cacheType == AuxiliaryCache.DISK_CACHE )
{
if ( log.isDebugEnabled() )
{
log.debug( "Attempting to get from aux: "
+ aux.getCacheName()
+ " which is of type: "
+ cacheType );
}
try
{
element = aux.get( key );
}
catch ( IOException ex )
{
log.error( "Error getting from aux", ex );
}
}
if ( log.isDebugEnabled() )
{
log.debug( "Got CacheElement: " + element );
}
if ( element != null )
{
// Item found in one of the auxiliary caches.
if ( isExpired( element ) )
{
if ( log.isDebugEnabled() )
{
log.debug( cacheName + " - Aux cache[" + i
+ "] hit, but element expired" );
}
missCountExpired++;
remove( key );
element = null;
}
else
{
if ( log.isDebugEnabled() )
{
log.debug( cacheName +
" - Aux cache[" + i + "] hit" );
}
// Update counters
hitCountAux++;
auxHitCountByIndex[ i ]++;
// Spool the item back into memory
// only spool if the mem cache size is greater than
// 0, else the item will immediately get put into purgatory
if ( memCache.getCacheAttributes().getMaxObjects() > 0 )
{
memCache.update(element);
} else {
if ( log.isDebugEnabled() ) {
log.debug( "Skipping memory update since" +
"no items are allowed in memory" );
}
}
}
found = true;
break;
}
}
}
}
}
catch ( Exception e )
{
log.error( e );
}
if ( ! found )
{
missCountNotFound++;
if ( log.isDebugEnabled() )
{
log.debug( cacheName + " - Miss" );
}
}
return element;
}
/**
* Determine if the element has exceeded its max life.
*
* @param element
* @return true if the element is expired, else false.
*/
private boolean isExpired( ICacheElement element )
{
try
{
IElementAttributes attributes = element.getElementAttributes();
if ( !attributes.getIsEternal() )
{
long now = System.currentTimeMillis();
// Remove if maxLifeSeconds exceeded
long maxLifeSeconds = attributes.getMaxLifeSeconds();
long createTime = attributes.getCreateTime();
if ( maxLifeSeconds != -1
&& ( now - createTime ) > ( maxLifeSeconds * 1000 ) )
{
if ( log.isDebugEnabled() )
{
log.debug( "Exceeded maxLife: " + element.getKey() );
}
return true;
}
long idleTime = attributes.getIdleTime();
long lastAccessTime = attributes.getLastAccessTime();
// Remove if maxIdleTime exceeded
// FIXME: Does this really belong here?
if ( ( idleTime != -1 )
&& ( now - lastAccessTime ) > ( idleTime * 1000 ) )
{
if ( log.isDebugEnabled() )
{
log.info( "Exceeded maxIdle: " + element.getKey() );
}
return true;
}
}
}
catch ( Exception e )
{
log.error( "Error determining expiration period, expiring", e );
return true;
}
return false;
}
/**
* Gets the set of keys of objects currently in the group
*
* @param group
* @return A Set of keys, or null.
*/
public Set getGroupKeys(String group)
{
HashSet allKeys = new HashSet();
allKeys.addAll(memCache.getGroupKeys(group));
for ( int i = 0; i < auxCaches.length; i++ )
{
AuxiliaryCache aux = auxCaches[i];
if ( aux != null )
{
try {
allKeys.addAll(aux.getGroupKeys(group));
}
catch ( IOException e )
{
//ignore
}
}
}
return allKeys;
}
/*
* (non-Javadoc)
* @see org.apache.jcs.engine.behavior.ICache#remove(java.io.Serializable)
*/
public boolean remove( Serializable key )
{
return remove( key, false );
}
/**
* Do not propogate removeall laterally or remotely.
*
* @param key
* @return true if the item was already in the cache.
*/
public boolean localRemove( Serializable key )
{
return remove( key, true );
}
/**
* fromRemote: If a remove call was made on a cache with both, then the
* remote should have been called. If it wasn't then the remote is down.
* we'll assume it is down for all. If it did come from the remote then the
* caceh is remotely configured and lateral removal is unncessary. If it
* came laterally then lateral removal is unnecessary. Does this assumes
* that there is only one lateral and remote for the cache? Not really, the
* intial removal should take care of the problem if the source cache was
* similiarly configured. Otherwise the remote cache, if it had no
* laterals, would remove all the elements from remotely configured caches,
* but if those caches had some other wierd laterals that were not remotely
* configured, only laterally propagated then they would go out of synch.
* The same could happen for multiple remotes. If this looks necessary we
* will need to build in an identifier to specify the source of a removal.
*
*@param key
*@param localOnly
*@return true if the item was in the cache, else false
*/
protected synchronized boolean remove( Serializable key,
boolean localOnly )
{
boolean removed = false;
try
{
removed = memCache.remove( key );
}
catch ( IOException e )
{
log.error( e );
}
// Removes from all auxiliary caches.
for ( int i = 0; i < auxCaches.length; i++ )
{
ICache aux = auxCaches[i];
if ( aux == null )
{
continue;
}
int cacheType = aux.getCacheType();
// for now let laterals call remote remove but not vice versa
if ( localOnly && ( cacheType == REMOTE_CACHE || cacheType == LATERAL_CACHE ) )
{
continue;
}
try
{
if ( log.isDebugEnabled() )
{
log.debug( "Removing " + key + " from cacheType" + cacheType );
}
boolean b = aux.remove( key );
// Don't take the remote removal into account.
if ( !removed && cacheType != REMOTE_CACHE )
{
removed = b;
}
}
catch ( IOException ex )
{
log.error( "Failure removing from aux", ex );
}
}
return removed;
}
/*
* (non-Javadoc)
* @see org.apache.jcs.engine.behavior.ICache#removeAll()
*/
public void removeAll()
throws IOException
{
removeAll( false );
}
/**
*
* @throws IOException
*/
public void localRemoveAll()
throws IOException
{
removeAll( true );
}
/**
* Removes all cached items.
*
* @param localOnly
* @throws IOException
*/
protected synchronized void removeAll( boolean localOnly )
throws IOException
{
try
{
memCache.removeAll();
if ( log.isDebugEnabled() )
{
log.debug( "Removed All keys from mem cache." );
}
}
catch ( IOException ex )
{
log.error( "Trouble updating memory cache.", ex );
}
// Removes from all auxiliary disk caches.
for ( int i = 0; i < auxCaches.length; i++ )
{
ICache aux = auxCaches[i];
int cacheType = aux.getCacheType();
if ( aux != null
&& ( cacheType == ICache.DISK_CACHE || !localOnly ) )
{
try
{
if ( log.isDebugEnabled() )
{
log.debug( "Removing All keys from cacheType" + cacheType );
}
aux.removeAll();
}
catch ( IOException ex )
{
log.error( "Failure removing all from aux", ex );
}
}
}
return;
}
/**
* Flushes all cache items from memory to auxilliary caches and close the
* auxilliary caches.
*/
public void dispose()
{
dispose( false );
}
/**
* invoked only by CacheManager.
*
*@param fromRemote
*/
protected synchronized void dispose( boolean fromRemote )
{
// If already disposed, return immediately
if ( ! alive )
{
return;
}
alive = false;
// Dispose of each auxilliary cache, Remote auxilliaries will be
// skipped if 'fromRemote' is true.
for ( int i = 0; i < auxCaches.length; i++ )
{
try
{
ICache aux = auxCaches[i];
// Skip this auxilliary if:
// - The auxilliary is null
// - The auxilliary is not alive
// - The auxilliary is remote and the invocation was remote
if ( aux == null
|| aux.getStatus() != CacheConstants.STATUS_ALIVE
|| fromRemote && aux.getCacheType() == REMOTE_CACHE )
{
continue;
}
// If the auxilliary is not a lateral, or the cache attributes
// have 'getUseLateral' set, all the elements currently in
// memory are written to the lateral before disposing
//TODO make sure disk gets a change to finish
if ( aux.getCacheType() != ICacheType.LATERAL_CACHE
|| this.cacheAttr.getUseLateral() )
{
Iterator itr = memCache.getIterator();
while ( itr.hasNext() )
{
Map.Entry entry = ( Map.Entry ) itr.next();
ICacheElement ce = (ICacheElement) entry.getValue();
try
{
if ( aux.getCacheType() == ICacheType.LATERAL_CACHE
&& !ce.getElementAttributes().getIsLateral() )
{
continue;
}
aux.update( ce );
}
catch ( Exception e )
{
log.error( e );
}
}
}
// Dispose of the auxiliary
aux.dispose();
}
catch ( IOException ex )
{
log.error( "Failure disposing of aux", ex );
}
try {
memCache.dispose();
}
catch ( IOException ex )
{
log.error( "Failure disposing of memCache", ex );
}
}
log.warn( "Called close for " + cacheName );
}
/**
* Though this put is extremely fast, this could bog the cache and should
* be avoided. The dispose method should call a version of this. Good for
* testing.
*/
public void save()
{
if ( !alive )
{
return;
}
synchronized ( this )
{
if ( !alive )
{
return;
}
alive = false;
for ( int i = 0; i < auxCaches.length; i++ )
{
try
{
ICache aux = auxCaches[i];
if ( aux.getStatus() == CacheConstants.STATUS_ALIVE )
{
Iterator itr = memCache.getIterator();
while ( itr.hasNext() )
{
Map.Entry entry = ( Map.Entry ) itr.next();
ICacheElement ce = (ICacheElement) entry.getValue();
aux.update(ce);
}
}
}
catch ( IOException ex )
{
log.error( "Failure saving aux caches", ex );
}
}
}
if ( log.isDebugEnabled() )
{
log.debug( "Called save for " + cacheName );
}
}
/**
* Gets the size attribute of the Cache object
*
*@return The size value
*/
public int getSize()
{
return memCache.getSize();
}
/**
* Gets the cacheType attribute of the Cache object
*
*@return The cacheType value
*/
public int getCacheType()
{
return CACHE_HUB;
}
/**
* Gets the status attribute of the Cache object
*
*@return The status value
*/
public int getStatus()
{
return alive ? CacheConstants.STATUS_ALIVE : CacheConstants.STATUS_DISPOSED;
}
/**
* Gets stats for debugging.
* @return String
*/
public String getStats()
{
return getStatistics().toString();
}
/**
* This returns data gathered for this region and all the
* auxiliaries it currently uses.
*
* @return Statistics and Info on the Region.
*/
public ICacheStats getStatistics()
{
ICacheStats stats = new CacheStats();
stats.setRegionName( this.getCacheName() );
// store the composite cache stats first
IStatElement[] elems = new StatElement[2];
elems[0] = new StatElement();
elems[0].setName( "HitCountRam" );
elems[0].setData( "" + getHitCountRam() );
elems[1] = new StatElement();
elems[1].setName( "HitCountAux" );
elems[1].setData( "" + getHitCountAux() );
// store these local stats
stats.setStatElements( elems );
// memory + aux, memory is not considered an auxiliary internally
int total = auxCaches.length + 1;
IStats[] auxStats = new Stats[total];
auxStats[0] = getMemoryCache().getStatistics();
for ( int i = 0; i < auxCaches.length; i++ )
{
AuxiliaryCache aux = auxCaches[i];
auxStats[i + 1] = aux.getStatistics();
}
// sore the auxiliary stats
stats.setAuxiliaryCacheStats( auxStats );
return stats;
}
/**
* Gets the cacheName attribute of the Cache object
*
*@return The cacheName value
*/
public String getCacheName()
{
return cacheName;
}
/**
* Gets the default element attribute of the Cache object
*
* Should this return a copy?
*
*@return The attributes value
*/
public IElementAttributes getElementAttributes()
{
return attr;
}
/**
* Sets the default element attribute of the Cache object
*
* @param attr
*/
public void setElementAttributes( IElementAttributes attr)
{
this.attr = attr;
}
/**
* Gets the ICompositeCacheAttributes attribute of the Cache object
*
*@return The ICompositeCacheAttributes value
*/
public ICompositeCacheAttributes getCacheAttributes()
{
return this.cacheAttr;
}
/**
* Sets the ICompositeCacheAttributes attribute of the Cache object
*
*@param cattr The new ICompositeCacheAttributes value
*/
public void setCacheAttributes( ICompositeCacheAttributes cattr )
{
this.cacheAttr = cattr;
// need a better way to do this, what if it is in error
this.memCache.initialize( this );
}
/**
* Gets the elementAttributes attribute of the Cache object
*
*@param key
*@return The elementAttributes value
*@exception CacheException
*@exception IOException
*/
public IElementAttributes getElementAttributes( Serializable key )
throws CacheException, IOException
{
CacheElement ce = ( CacheElement ) get( key );
if ( ce == null )
{
throw new ObjectNotFoundException( "key " + key + " is not found" );
}
return ce.getElementAttributes();
}
/**
* Create the MemoryCache based on the config parameters. TODO: consider
* making this an auxiliary, despite its close tie to the CacheHub. TODO:
* might want to create a memory cache config file separate from that of
* the hub -- ICompositeCacheAttributes
*
*@param cattr
*/
private void createMemoryCache( ICompositeCacheAttributes cattr )
{
if ( memCache == null )
{
try
{
Class c = Class.forName( cattr.getMemoryCacheName() );
memCache = ( MemoryCache ) c.newInstance();
memCache.initialize( this );
}
catch ( Exception e )
{
log.warn( "Failed to init mem cache, using: LRUMemoryCache", e );
this.memCache = new LRUMemoryCache();
this.memCache.initialize( this );
}
}
else
{
log.warn( "Refusing to create memory cache -- already exists." );
}
}
// ---------------------------------------------------- For Instrumentation
/**
* Access to the memory cache for instrumentation.
*
* @return the MemoryCache implementation
*/
public MemoryCache getMemoryCache()
{
return memCache;
}
/**
* Number of times a requested item was found in the memory cache.
*
* @return number of hits in memory
*/
public int getHitCountRam()
{
return hitCountRam;
}
/**
* Number of times a requested item was found in and auxiliary cache.
*
* @return number of auxiliary hits.
*/
public int getHitCountAux()
{
return hitCountAux;
}
/**
* Number of times a requested element was not found.
*
* @return number of misses.
*/
public int getMissCountNotFound()
{
return missCountNotFound;
}
/**
* Number of times a requested element was found but was expired.
*
* @return number of found but expired gets.
*/
public int getMissCountExpired()
{
return missCountExpired;
}
/**
* If there are event handlers for the item, then create an event and
* queue it up.
*
* @param ce
* @param eventType
*/
private void handleElementEvent( ICacheElement ce, int eventType )
{
// handle event, might move to a new method
ArrayList eventHandlers = ce.getElementAttributes().getElementEventHandlers();
if ( eventHandlers != null )
{
if ( log.isDebugEnabled() )
{
log.debug( "Element Handlers are registered. Create event type " + eventType );
}
IElementEvent event = new ElementEvent( ce, eventType );
Iterator hIt = eventHandlers.iterator();
while ( hIt.hasNext() )
{
IElementEventHandler hand = ( IElementEventHandler ) hIt.next();
try
{
addElementEvent( hand, event );
}
catch ( Exception e )
{
log.error( "Trouble adding element event to queue", e );
}
}
}
}
/**
* Adds an ElementEvent to be handled
*
*@param hand The IElementEventHandler
*@param event The IElementEventHandler IElementEvent event
*@exception IOException Description of the Exception
*/
public void addElementEvent( IElementEventHandler hand, IElementEvent event )
throws IOException
{
if ( log.isDebugEnabled() )
{
log.debug( "Adding event to Element Event Queue" );
}
elementEventQ.addElementEvent( hand, event );
}
}