All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src-lib.lib.cache.CacheNG Maven / Gradle / Ivy

/*
 * (c) Copyright 2008, 2009 Hewlett-Packard Development Company, LP
 * All rights reserved.
 * [See end of file]
 */

package lib.cache;


import java.util.Iterator;

import lib.ActionKeyValue;
import lib.Cache;
import lib.CacheLRU;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.hpl.jena.sparql.util.ALog;

/** A cache.  But we already use that name (and this is a replacement for that).
 *  This class provides a cache of objects that can be retrieved for shared (read)
 *  use or for exclusive (write) use.  It assumes the application will not make
 *  conflicting demands (e.g. exclusive of a currently multiply shared object).
 *  
 *   This class does not control the making of objects.
 * 
 * @author Andy Seaborne
 */

// Rename later.  This does not assume that read-only objects are returned (may change)?  
public class CacheNG implements Cache
{
    // XXX Stats and loging/reporting
    private static Logger log = LoggerFactory.getLogger(CacheNG.class) ;
    private int statsDumpTick = -1 ;
    private final boolean logging = false  ;
    
    // SoftReference? and then app has a hard reference when using.
    // Probably overkill.
    private int max ;
    private int min ;
    CacheLRU.CacheImpl objects ;
    
    // Overall statistics 
    private long cacheEntries ;
    private long cacheHits ;
    private long cacheMisses ; 
    private long cacheEjects ;
    private long cacheTimePoint = 0;  
    
    public CacheNG(int num)            { this(0, num) ; }
    private CacheNG(int min, int max)
    { 
        this.min = min ;
        this.max = max ;
        cacheEntries = 0 ;
        cacheHits = 0 ;
        cacheMisses = 0 ;
        cacheEjects = 0 ;
        
        objects = new CacheLRU.CacheImpl(max) ;
        objects.setDropHandler(new ActionKeyValue(){
            @Override
            public void apply(Key key, PoolEntry entry)
            {
                cacheEjects++ ;
                if ( logging && log.isDebugEnabled() )
                    log.debug("Eject: "+str(key)+ "Hits: "+entry.cacheHit) ;
            }}) ;
    }

    synchronized
    public boolean contains(Key key)
    {
        boolean b = objects.containsKey(key) ;
        if ( logging )
            log.info("Miss/chk : "+str(key)) ;
        return b ;
    }

    public T getObject(Key key) { return getObject(key, false) ; }

    
    //@Override
    synchronized
    public T getObject(Key key, boolean exclusive)
    { 
        PoolEntry entry = objects.get(key) ;
        if ( entry == null )
        {
            cacheMisses++ ;
            if ( logging )
                log.info("Miss/Get : "+str(key)) ;
            stats() ;
            return null ;
        }
        
        entry.cacheHit++ ;
        
        if ( exclusive )
        {
            if ( entry.refCount > 0 )
            {
                log.error("Attempt to get exclusive access when the object is already being shared: "+key) ;
                throw new CacheException("Failed to get exclusive access") ;
            }
            entry.refCount = -1 ;
            cacheHits++ ;
            if ( logging )
                log.info("Miss/Hit(ex): "+str(entry)) ;
            stats() ;
            return entry.thing ; 
        }
        else
        {
            if ( entry.refCount < 0 )
            {
                log.error("Attempt to get shared access when the object is already exclusively allocated: "+key) ;
                throw new CacheException("Failed to get shared access") ;
            }
            entry.refCount++ ;
            cacheHits++ ;
            if ( logging )
                log.info("Miss/Hit    : "+str(entry)) ;
            stats() ;
            return entry.thing ;
        }
    }
    synchronized
    public void putObject(Key key, T thing)
    {
        PoolEntry entry = objects.get(key) ;
        if ( entry != null )
        {
            if ( entry.thing.equals(thing) )
                log.error("Putting the same object into the cache: "+key) ;
            else
                if ( logging )
                    log.info("Replace    : "+str(entry)) ;
            return ;
        }
        cacheEntries++ ;
        if ( logging )
            log.info("Miss/Put    : "+str(key)) ;
        objects.put(key, new PoolEntry(key, thing)) ;
        stats() ;
    }

    // A simple cache just has get/put.
    // No promotion.
    
    static class CacheException extends RuntimeException
    { CacheException(String msg) { super(msg) ; } }
    
    /** Turn a single-shared object into an exclusively locked object */ 
    synchronized
    public void promote(Key key)
    {
        PoolEntry entry = objects.get(key) ;
        if ( entry == null )
        {
            log.error("Attempt to promote object noit in the pool: "+key) ;
            throw new CacheException("Failed to promote") ;
        }
        if ( entry.refCount < 0 )
        {
            log.error("Attempt to promote object that is already exclusively allocated: "+key) ;
            throw new CacheException("Failed to promote") ;
        }
        else if ( entry.refCount == 0 )
        {
            log.error("Attempt to promote object that is not allocated: "+key) ;
            throw new CacheException("Failed to promote") ;
        }
        else if ( entry.refCount > 1 )
        {
            log.error("Attempt to promote object that is multiply shared: "+key) ;
            throw new CacheException("Failed to promote") ;
        }

        // OK!  Lock it.
        entry.refCount = -1 ;
        if ( logging )
            log.info("Promote    : "+str(entry)) ;
        stats() ;
    }
    
    //@Override
    synchronized
    public void returnObject(Key key)
    {
        PoolEntry entry = objects.get(key) ;
        if ( entry == null )
        {
            log.warn("Object returned that is not allocated: "+key) ;
            return ;
        }        
        
        if ( logging )
            log.info("Return    : "+key) ;
        
        if ( entry.refCount < 0 )
        {
            entry.refCount = 0 ;
            return ;
        }
        
        entry.refCount -- ;
//        if ( entry.refCount == 0 )
//        { }
        stats() ;
    }
    
    @Override
    synchronized
    public void removeObject(Key key)
    {
        PoolEntry entry = objects.get(key) ;
        if ( entry == null )
            return ;
        
//        if ( entry.refCount != 0 )
//            ; // Problems
        objects.remove(key) ;
        if ( logging )
            log.info("Remove   : "+key) ;
        cacheEntries-- ;
        stats() ;
    }
    
    
    @Override
    public Iterator keys()
    {
        return objects.keySet().iterator() ;
    }
    synchronized
    public void clear()
    {
        if ( logging )
            log.info("Clear") ;
        objects.clear() ;
    }
    @Override
    public boolean isEmpty()
    {
        return objects.isEmpty() ;
    }
    
    @Override
    public void setDropHandler(ActionKeyValue dropHandler)
    {}
    
    @Override
    public long size()
    {
        return objects.size() ;
    }
    private void stats()
    {
        long total = cacheMisses + cacheHits ;
        if ( total != 0 && total%statsDumpTick == 0 )
        {
            String x = String.format("Size=%d, Hits=%d, Misses=%d, Ejects=%d", size(), cacheHits, cacheMisses, cacheEjects) ;
            log.info(x) ;
        }
    }
    
    
    private void warn(String message, Object... args)
    {
        String x = String.format(message, args) ;
        ALog.warn(this, x) ;
    }
    
    private String str(Key key) { return key.toString() ; } 
    
    private String str(PoolEntry entry)
    { return entry.key.toString()+" ["+entry.refCount+"]"  ; }
    
//    static class Handler implements ActionKeyValue
//    {
//
//        @Override
//        public void apply(Key key, PoolEntry entry)
//        {
//            if ( entry.refCount != 0 )
//                ;
//            entry.refCount = 0 ;
//        }
//        
//    }

    // Hmm - bet there is an existing class to do all this. 
    class PoolEntry
    {
        // Ref count = -1 ==> exclusive lock.
        int refCount = 0 ;
        int cacheHit = 0 ;
        long cachePoint = 0 ;
//        enum Status { FREE, ALLOCATED, INVALID } ;
//        Status status = Status.INVALID ;
        T thing ;
        private Object key ;
        PoolEntry(Key key, T thing)
        { 
            this.key = key ; 
            this.thing = thing ;
            this.cachePoint = (++cacheTimePoint) ;
        }
        
        @Override public String toString() 
        {
            return String.format("[H:%d@%d] %s ", cacheHit, cachePoint, thing) ;
        }
    }
    
}

/*
 * (c) Copyright 2008, 2009 Hewlett-Packard Development Company, LP
 * 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 name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 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 AUTHOR 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.
 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy