Package org.apache.stratum.jcs.auxiliary.disk

Source Code of org.apache.stratum.jcs.auxiliary.disk.AbstractDiskCache$MyCacheListener

package org.apache.stratum.jcs.auxiliary.disk;

/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.stratum.jcs.engine.CacheElement;
import org.apache.stratum.jcs.engine.CacheEventQueue;
import org.apache.stratum.jcs.engine.CacheInfo;
import org.apache.stratum.jcs.engine.behavior.ICache;
import org.apache.stratum.jcs.engine.behavior.ICacheElement;
import org.apache.stratum.jcs.engine.behavior.ICacheEventQueue;
import org.apache.stratum.jcs.engine.behavior.ICacheListener;
import org.apache.stratum.jcs.engine.behavior.IElementAttributes;
import org.apache.stratum.jcs.auxiliary.behavior.IAuxiliaryCacheAttributes;
import org.apache.stratum.jcs.utils.locking.ReadWriteLock;

import java.io.IOException;
import java.io.Serializable;
import java.util.Hashtable;

/**
* Abstract class providing a base implementation of a disk cache, which can
* be easily extended to implement a disk cache for a specific perstistence
* mechanism.
*
* When implementing the abstract methods note that while this base class
* handles most things, it does not acquire or release any locks.
* Implementations should do so as neccesary. This is mainly done to minimize
* the time speant in critical sections.
*
* Error handling in this class needs to be addressed. Currently if an
* exception is thrown by the persistence mechanism, this class destroys the
* event queue. Should it also destory purgatory? Should it dispose itself?
*
* @author <a href="mailto:asmuts@yahoo.com">Aaron Smuts</a>
* @author <a href="mailto:james@jamestaylor.org">James Taylor</a>
* @version $Id: AbstractDiskCache.java,v 1.4 2002/03/01 18:31:55 dlr Exp $
*/
public abstract class AbstractDiskCache implements ICache, Serializable
{
    private final static Log log =
        LogFactory.getLog( AbstractDiskCache.class );

    /**
     * Map where elements are stored between being added to this cache and
     * actually spooled to disk. This allows puts to the disk cache to return
     * quickly, and the more expensive operation of serializing the elements
     * to persistent storage queued for later. If the elements are pulled into
     * the memory cache while the are still in purgatory, writing to disk can
     * be cancelled.
     */
    protected Hashtable purgatory = new Hashtable();

    /**
     * The CacheEventQueue where changes will be queued for asynchronous
     * updating of the persistent storage.
     */
    protected ICacheEventQueue cacheEventQueue;

    /**
     * Each instance of a Disk cache should use this lock to synchronize reads
     * and writes to the underlying storage mechansism.
     */
    protected ReadWriteLock lock = new ReadWriteLock();

    /**
     * Indicates whether the cache is 'alive', defined as having been
     * initialized, but not yet disposed.
     */
    protected boolean alive = false;

    /**
     * Every cache will have a name, subclasses must set this when they are
     * initialized.
     */
    protected String cacheName;

    /**
     * DEBUG: Keeps a count of the number of purgatory hits for debug messages
     */
    protected int purgHits = 0;

    // ----------------------------------------------------------- constructors

    public AbstractDiskCache( String cacheName )
    {
        this.cacheName = cacheName;

        this.cacheEventQueue = new CacheEventQueue( new MyCacheListener(),
                                                    CacheInfo.listenerId,
                                                    cacheName );
    }

    // ------------------------------------------------------- interface ICache

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#put
     */
    public final void put( Serializable key, Serializable value )
        throws IOException
    {
        put( key, value, null );
    }

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#put
     */
    public final void put( Serializable key, Serializable value,
                     IElementAttributes attributes )
        throws IOException
    {
        CacheElement ce = new CacheElement( cacheName, key, value );

        ce.setElementAttributes( attributes );

        update( ce );
    }

    /**
     * Adds the provided element to the cache. Element will be added to
     * purgatory, and then queued for later writing to the serialized storage
     * mechanism.
     *
     * @see org.apache.stratum.jcs.engine.behavior.ICache#update
     */
    public final void update( ICacheElement cacheElement )
        throws IOException
    {
        if ( log.isDebugEnabled() )
        {
            log.debug( "Putting element in purgatory, cacheName: " + cacheName +
                       ", key: " + cacheElement.getKey() );
        }

        try
        {
            // Wrap the CacheElement in a PurgatoryElement

            PurgatoryElement pe = new PurgatoryElement( cacheElement );

            // Indicates the the element is eligable to be spooled to disk,
            // this will remain true unless the item is pulled back into
            // memory.

            pe.setSpoolable( true );

            // Add the element to purgatory

            purgatory.put( pe.getKey(), pe );

            // Queue element for serialization

            cacheEventQueue.addPutEvent( pe );
        }
        catch ( IOException ex )
        {
            log.error( ex );

            cacheEventQueue.destroy();
        }
    }

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#get
     */
    public final Serializable get( Serializable key )
    {
        return get( key, true );
    }

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#get
     */
    public final Serializable get( Serializable key, boolean container )
    {
        // If not alive, always return null.

        if ( !alive )
        {
            return null;
        }

        PurgatoryElement pe = ( PurgatoryElement ) purgatory.get( key );

        // If the element was found in purgatory

        if ( pe != null )
        {
            purgHits++;

            if ( log.isDebugEnabled() )
            {
                if ( purgHits % 100 == 0 )
                {
                    log.debug( "Purgatory hits = " + purgHits );
                }
            }

            // The element will go back to the memory cache, so set spoolable
            // to false, which will prevent the queue listener from serializing
            // the element.

            pe.setSpoolable( false );

            log.debug( "Found element in purgatory, cacheName: " + cacheName +
                       ", key: " + key );

            if ( container )
            {
                purgatory.remove( key );

                return pe.cacheElement;
            }
            else
            {
                purgatory.remove( key );

                return ( Serializable ) pe.cacheElement.getVal();
            }
        }

        // If we reach this point, element was not found in purgatory, so get
        // it from the cache.

        try
        {
            return doGet( key );
        }
        catch ( Exception e )
        {
            log.error( e );

            cacheEventQueue.destroy();
        }

        return null;
    }

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#remove
     */
    public final boolean remove( Serializable key )
    {
        // Remove element from purgatory if it is there

        purgatory.remove( key );

        // Queue removal for persistent store

        try
        {
            cacheEventQueue.addRemoveEvent( key );
        }
        catch ( IOException e )
        {
            log.error( e );

            cacheEventQueue.destroy();
        }

        return false;
    }

    /**
     * @see org.apache.stratum.jcs.engine.behavior.ICache#removeAll
     */
    public final void removeAll()
    {
        // Replace purgatory with a new empty hashtable

        purgatory = new Hashtable();

        // Queue a remove all for the persistent store

        try
        {
            cacheEventQueue.addRemoveAllEvent();
        }
        catch ( IOException e )
        {
            log.error( e );

            cacheEventQueue.destroy();
        }
    }

    /**
     * Adds a dispose request to the disk cache.
     */
    public final void dispose()
    {
        alive = false;

        // Invoke any implementation specific disposal code

        doDispose();

        // FIXME: May lose the end of the queue, need to be more graceful

        cacheEventQueue.destroy();
    }

    /**
     * @see ICache#getCacheName
     */
    public String getCacheName()
    {
        return cacheName;
    }

    /**
     * Subclasses will want to override this to provide more information most
     * likely.
     *
     * @see ICache#getStats
     */
    public String getStats()
    {
        return "cacheName = " + cacheName;
    }

    /**
     * @see ICache#getStatus
     */
    public int getStatus()
    {
        return ( alive ? STATUS_ALIVE : STATUS_DISPOSED );
    }

    /**
     * Size cannot be determined without knowledge of the cache implementation,
     * so subclasses will need to implement this method.
     *
     * @see ICache#getSize
     */
    public abstract int getSize();

    /**
     * @see ICache#getType
     *
     * @return Always returns DISK_CACHE since subclasses should all be of
     *         that type.
     */
    public int getCacheType()
    {
        return DISK_CACHE;
    }

    /**
     * Cache that implements the CacheListener interface, and calls appropriate
     * methods in its parent class.
     */
    private class MyCacheListener implements ICacheListener
    {
        private byte listenerId = 0;

        /**
         * @see ICacheListener#getListenerID
         */
        public byte getListenerId()
            throws IOException
        {
            return this.listenerId;
        }

        /**
         * @see ICacheListener#setListenerID
         */
        public void setListenerId( byte id )
            throws IOException
        {
            this.listenerId = id;
        }

        /**
         * @see ICacheListener#handlePut
         *
         * NOTE: This checks if the element is a puratory element and behaves
         * differently depending. However since we have control over how
         * elements are added to the cache event queue.
         */
        public void handlePut( ICacheElement element )
            throws IOException
        {
            if ( alive )
            {
                // If the element is a PurgatoryElement we must check to see
                // if it is still spoolable, and remove it from purgatory.

                if ( element instanceof PurgatoryElement )
                {
                    PurgatoryElement pe = ( PurgatoryElement ) element;

                    element = pe.getCacheElement();

                    // If the element is still eligable, spool it.

                    if ( pe.isSpoolable() )
                    {
                        doUpdate( element );
                    }

                    // After the update has completed, it is safe to remove
                    // the element from purgatory.

                    purgatory.remove( element.getKey() );
                }
                else
                {
                    doUpdate( element );
                }
            }
        }

        /**
         * @see ICacheListener#handleRemove
         */
        public void handleRemove( String cacheName, Serializable key )
            throws IOException
        {
            if ( alive )
            {
                if ( doRemove( key ) )
                {
                    log.debug( "Element removed, key: " + key );
                }
            }
        }

        /**
         * @see ICacheListener#handleRemoveAll
         */
        public void handleRemoveAll( String cacheName )
            throws IOException
        {
            if ( alive )
            {
                doRemoveAll();
            }
        }

        /**
         * @see ICacheListener#handleDispose
         */
        public void handleDispose( String cacheName )
            throws IOException
        {
            if ( alive )
            {
                doDispose();
            }
        }
    }

    // ---------------------- subclasses should implement the following methods

    /**
     * Get a value from the persistent store.
     *
     * @param key Key to locate value for.
     * @return An object matching key, or null.
     */
    protected abstract Serializable doGet( Serializable key );

    /**
     * Add a cache element to the persistent store.
     */
    protected abstract void doUpdate( ICacheElement element );

    /**
     * Remove an object from the persistent store if found.
     *
     * @param key Key of object to remove.
     */
    protected abstract boolean doRemove( Serializable key );

    /**
     * Remove all objects from the persistent store.
     */
    protected abstract void doRemoveAll();

    /**
     * Dispose of the persistent store. Note that disposal of purgatory and
     * setting alive to false does NOT need to be done by this method.
     */
    protected abstract void doDispose();
}
TOP

Related Classes of org.apache.stratum.jcs.auxiliary.disk.AbstractDiskCache$MyCacheListener

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.