Package org.apache.excalibur.store.impl

Source Code of org.apache.excalibur.store.impl.MRUMemoryStore

/*
* 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.
*/
package org.apache.excalibur.store.impl;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.excalibur.instrument.CounterInstrument;
import org.apache.excalibur.instrument.Instrument;
import org.apache.excalibur.instrument.Instrumentable;
import org.apache.excalibur.instrument.ValueInstrument;
import org.apache.excalibur.store.Store;
import org.apache.excalibur.store.StoreJanitor;

/**
* This class provides a cache algorithm for the requested documents.
* It combines a HashMap and a LinkedList to create a so called MRU
* (Most Recently Used) cache.
*
* @avalon.component
* @avalon.service type=Store
* @x-avalon.info name=mru-store
* @x-avalon.lifestyle type=singleton
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Id: MRUMemoryStore.java,v 1.5 2004/02/28 11:47:31 cziegeler Exp $
*/
public class MRUMemoryStore
    extends AbstractLogEnabled
    implements Store, Parameterizable, Serviceable, Disposable, ThreadSafe, Instrumentable, Component
{
    private String m_instrumentableName;
    private int m_maxobjects;
    private boolean m_persistent;
    private Hashtable m_cache;
    private LinkedList m_mrulist;
    private Store m_persistentStore;
    private StoreJanitor m_storeJanitor;
    private ServiceManager m_manager;
   
    private ValueInstrument m_sizeInstrument = new ValueInstrument("size");
    private CounterInstrument m_hitsInstrument = new CounterInstrument("hits");
    private CounterInstrument m_missesInstrument = new CounterInstrument("misses");

    /**
     * Get components of the ComponentLocator
     *
     * @param manager The ComponentLocator
     * @avalon.dependency type=org.apache.excalibur.store.Store
     * @avalon.dependency type=org.apache.excalibur.store.StoreJanitor
     */
    public void service( ServiceManager manager )
        throws ServiceException
    {
        m_manager = manager;
        if( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "Looking up " + StoreJanitor.ROLE );
        }
        m_storeJanitor = (StoreJanitor)manager.lookup( StoreJanitor.ROLE );
    }

    /**
     * Initialize the MRUMemoryStore.
     * A few options can be used:
     * <UL>
     <LI>maxobjects: Maximum number of objects stored in memory (Default: 100 objects)</LI>
     <LI>use-persistent-cache: Use persistent cache to keep objects persisted after
     *      container shutdown or not (Default: false)</LI>
     * </UL>
     *
     * @param params Store parameters
     * @exception ParameterException
     */
    public void parameterize( Parameters params ) throws ParameterException
    {
        m_maxobjects = params.getParameterAsInteger( "maxobjects", 100 );
        m_persistent = params.getParameterAsBoolean( "use-persistent-cache", false );
        if( ( m_maxobjects < 1 ) )
        {
            throw new ParameterException( "MRUMemoryStore maxobjects must be at least 1!" );
        }

        if ( m_persistent )
        {
            if( getLogger().isDebugEnabled() )
            {
                getLogger().debug( "Looking up " + Store.PERSISTENT_STORE );
            }
            try
            {
                m_persistentStore = (Store)m_manager.lookup( Store.PERSISTENT_STORE );
            }
            catch (ServiceException se)
            {
                throw new ParameterException("Unable to look up persistent store.", se);
            }
        }

        m_cache = new Hashtable( (int)( m_maxobjects * 1.2 ) );
        m_mrulist = new LinkedList();
        m_storeJanitor.register( this );
    }

    /**
     * Dispose the component
     */
    public void dispose()
    {
        if( m_manager != null )
        {
            getLogger().debug( "Disposing component!" );

            if( m_storeJanitor != null )
            {
                m_storeJanitor.unregister( this );
            }
            m_manager.release( m_storeJanitor );
            m_storeJanitor = null;

            // save all cache entries to filesystem
            if( m_persistent )
            {
                getLogger().debug( "Final cache size: " + m_cache.size() );
                Enumeration enumer = m_cache.keys();
                while( enumer.hasMoreElements() )
                {
                    Object key = enumer.nextElement();
                    if( key == null )
                    {
                        continue;
                    }
                    try
                    {
                        Object value = m_cache.remove( key );
                        if( checkSerializable( value ) )
                        {
                            m_persistentStore.store( key, value );
                        }
                    }
                    catch( IOException ioe )
                    {
                        getLogger().error( "Error in dispose()", ioe );
                    }
                }
            }
            m_manager.release( m_persistentStore );
            m_persistentStore = null;
        }

        m_manager = null;
    }

    /**
     * Store the given object in a persistent state. It is up to the
     * caller to ensure that the key has a persistent state across
     * different JVM executions.
     *
     * @param key The key for the object to store
     * @param value The object to store
     */
    public synchronized void store( Object key, Object value )
    {
        hold( key, value );
    }

    /**
     * This method holds the requested object in a HashMap combined
     * with a LinkedList to create the MRU.
     * It also stores objects onto the filesystem if configured.
     *
     * @param key The key of the object to be stored
     * @param value The object to be stored
     */
    public synchronized void hold( Object key, Object value )
    {
        if( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "Holding object in memory:" );
            getLogger().debug( "  key: " + key );
            getLogger().debug( "  value: " + value );
        }
        /** ...first test if the max. objects in cache is reached... */
        while( m_mrulist.size() >= m_maxobjects )
        {
            /** ...ok, heapsize is reached, remove the last element... */
            free();
        }
        /** ..put the new object in the cache, on the top of course ... */
        m_cache.put( key, value );
        m_mrulist.remove( key );
        m_mrulist.addFirst( key );
        m_sizeInstrument.setValue( m_mrulist.size() );
    }

    /**
     * Get the object associated to the given unique key.
     *
     * @param key The key of the requested object
     * @return the requested object
     */
    public synchronized Object get( Object key )
    {
        Object value = m_cache.get( key );
        if( value != null )
        {
            /** put the accessed key on top of the linked list */
            m_mrulist.remove( key );
            m_mrulist.addFirst( key );
            if( getLogger().isDebugEnabled() )
            {
                getLogger().debug( "Found key: " + key.toString() );
            }
            m_hitsInstrument.increment();
            return value;
        }

        if( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "NOT Found key: " + key.toString() );
        }

        /** try to fetch from filesystem */
        if( m_persistent )
        {
            value = m_persistentStore.get( key );
            if( value != null )
            {
                try
                {
                    if( !m_cache.containsKey( key ) )
                    {
                        hold( key, value );
                    }
                    m_hitsInstrument.increment();
                    return value;
                }
                catch( Exception e )
                {
                    getLogger().error( "Error in get()!", e );
                }
            }
        }
        m_missesInstrument.increment();
        return null;
    }

    /**
     * Remove the object associated to the given key.
     *
     * @param key The key of to be removed object
     */
    public synchronized void remove( Object key )
    {
        if( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "Removing object from store" );
            getLogger().debug( "  key: " + key );
        }
        m_cache.remove( key );
        m_mrulist.remove( key );
        m_sizeInstrument.setValue( m_mrulist.size() );
       
        if( m_persistent && key != null )
        {
            m_persistentStore.remove( key );
        }
    }

    /**
     * Clear the Store of all elements
     */
    public synchronized void clear()
    {
        Enumeration enumer = m_cache.keys();
        while( enumer.hasMoreElements() )
        {
            Object key = enumer.nextElement();
            if( key == null )
            {
                continue;
            }
            remove( key );
        }
        m_sizeInstrument.setValue( 0 );
    }

    /**
     * Indicates if the given key is associated to a contained object.
     *
     * @param key The key of the object
     * @return true if the key exists
     */
    public synchronized boolean containsKey( Object key )
    {
        if( m_persistent )
        {
            return ( m_cache.containsKey( key ) || m_persistentStore.containsKey( key ) );
        }
        else
        {
            return m_cache.containsKey( key );
        }
    }

    /**
     * Returns the list of used keys as an Enumeration.
     *
     * @return the enumeration of the cache
     */
    public synchronized Enumeration keys()
    {
        return m_cache.keys();
    }

    /**
     * Returns count of the objects in the store, or -1 if could not be
     * obtained.
     */
    public synchronized int size()
    {
        return m_cache.size();
    }

    /**
     * Frees some of the fast memory used by this store.
     * It removes the last element in the store.
     */
    public synchronized void free()
    {
        try
        {
            if( m_cache.size() > 0 )
            {
                // This can throw NoSuchElementException
                Object key = m_mrulist.removeLast();
                Object value = m_cache.remove( key );
                if( value == null )
                {
                    getLogger().warn( "Concurrency condition in free()" );
                }

                if( getLogger().isDebugEnabled() )
                {
                    getLogger().debug( "Freeing cache." );
                    getLogger().debug( "  key: " + key );
                    getLogger().debug( "  value: " + value );
                }

                if( m_persistent )
                {
                    // Swap object on fs.
                    if( checkSerializable( value ) )
                    {
                        try
                        {
                            m_persistentStore.store( key, value );
                        }
                        catch( Exception e )
                        {
                            getLogger().error( "Error storing object on fs", e );
                        }
                    }
                }
               
                m_sizeInstrument.setValue( m_mrulist.size() );
            }
        }
        catch( NoSuchElementException e )
        {
            getLogger().warn( "Concurrency error in free()", e );
        }
        catch( Exception e )
        {
            getLogger().error( "Error in free()", e );
        }
    }

    /**
     * This method checks if an object is seriazable.
     *
     * @param object The object to be checked
     * @return true if the object is storeable
     */
    private boolean checkSerializable( Object object )
    {

        if( object == null ) return false;

        return ( object instanceof java.io.Serializable );
    }

    public void setInstrumentableName(String name)
    {
        m_instrumentableName = name;   
    }

    public String getInstrumentableName()
    {
        return m_instrumentableName;
    }

    public Instrument[] getInstruments()
    {
        return new Instrument[] { m_sizeInstrument, m_hitsInstrument, m_missesInstrument };
    }

    public Instrumentable[] getChildInstrumentables() {
        return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY;
    }
}
TOP

Related Classes of org.apache.excalibur.store.impl.MRUMemoryStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.